Skip to content

Commit 91ab8b6

Browse files
authored
Merge pull request #38 from gostaticanalysis/add-field
Add Field
2 parents 2f839a0 + 2d8f64e commit 91ab8b6

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

types.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,30 @@ func HasField(s *types.Struct, f *types.Var) bool {
131131
return false
132132
}
133133

134+
// Field returns field of the struct type.
135+
// If the type is not struct or has not the field,
136+
// Field returns -1, nil.
137+
// If the type is a named type or a pointer type,
138+
// Field calls itself recursively with
139+
// an underlying type or an element type of pointer.
140+
func Field(t types.Type, name string) (int, *types.Var) {
141+
switch t := t.(type) {
142+
case *types.Pointer:
143+
return Field(t.Elem(), name)
144+
case *types.Named:
145+
return Field(t.Underlying(), name)
146+
case *types.Struct:
147+
for i := 0; i < t.NumFields(); i++ {
148+
f := t.Field(i)
149+
if f.Name() == name {
150+
return i, f
151+
}
152+
}
153+
}
154+
155+
return -1, nil
156+
}
157+
134158
func TypesInfo(info ...*types.Info) *types.Info {
135159
if len(info) == 0 {
136160
return nil

types_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,58 @@ func TestUnder(t *testing.T) {
115115
})
116116
}
117117
}
118+
119+
func TestField(t *testing.T) {
120+
t.Parallel()
121+
122+
lookup := func(pass *analysis.Pass, n string) (types.Type, error) {
123+
_, obj := pass.Pkg.Scope().LookupParent(n, token.NoPos)
124+
if obj == nil {
125+
return nil, fmt.Errorf("does not find: %s", n)
126+
}
127+
return obj.Type(), nil
128+
}
129+
130+
cases := map[string]struct {
131+
src string
132+
typ string
133+
field string
134+
want int
135+
}{
136+
"nomarl": {"type a struct{n int}", "a", "n", 0},
137+
"nofield": {"type a struct{n int}", "a", "m", -1},
138+
"empty": {"type a struct{}", "a", "n", -1},
139+
"two": {"type a struct{n, m int}", "a", "m", 1},
140+
"nonamed": {"var a struct{n, m int}", "a", "m", 1},
141+
"ptr": {"var a *struct{n, m int}", "a", "m", 1},
142+
"namednamed": {"type a struct{n int}; type b a", "b", "n", 0},
143+
"alias": {"type a struct{n int}; type b = a", "b", "n", 0},
144+
}
145+
146+
for name, tt := range cases {
147+
name, tt := name, tt
148+
t.Run(name, func(t *testing.T) {
149+
t.Parallel()
150+
a := &analysis.Analyzer{
151+
Name: name + "Analyzer",
152+
Run: func(pass *analysis.Pass) (interface{}, error) {
153+
typ, err := lookup(pass, tt.typ)
154+
if err != nil {
155+
return nil, err
156+
}
157+
158+
got, _ := analysisutil.Field(typ, tt.field)
159+
if tt.want != got {
160+
return nil, fmt.Errorf("want %v but got %v", tt.want, got)
161+
}
162+
return nil, nil
163+
},
164+
}
165+
path := filepath.Join(name, name+".go")
166+
dir := WriteFiles(t, map[string]string{
167+
path: fmt.Sprintf("package %s\n%s", name, tt.src),
168+
})
169+
analysistest.Run(t, dir, a, name)
170+
})
171+
}
172+
}

0 commit comments

Comments
 (0)