Skip to content

Commit de271ea

Browse files
committed
better way to find the actual expression
In gomega expression, the current code knows how to skip the helper method like `WithOffset()` and get to the actual method (e.g. `Expect`). The problem is that this logic is done by comparing to a static list of method names. If a new method will be added, we'll have to add it to the list each time. This commit fixes the issue by skiping any non-actual method. Also, move things around for better oerdering.
1 parent 1309578 commit de271ea

File tree

5 files changed

+248
-232
lines changed

5 files changed

+248
-232
lines changed

internal/expression/actual/asyncfuncarg.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package actual
22

33
import (
4-
"github.com/nunnatsa/ginkgolinter/internal/gomegahandler"
5-
"github.com/nunnatsa/ginkgolinter/internal/interfaces"
64
gotypes "go/types"
5+
6+
"github.com/nunnatsa/ginkgolinter/internal/gomegainfo"
7+
"github.com/nunnatsa/ginkgolinter/internal/interfaces"
78
)
89

910
func getAsyncFuncArg(sig *gotypes.Signature) ArgPayload {
@@ -16,7 +17,7 @@ func getAsyncFuncArg(sig *gotypes.Signature) ArgPayload {
1617

1718
if sig.Params().Len() > 0 {
1819
arg := sig.Params().At(0).Type()
19-
if gomegahandler.IsGomegaType(arg) && sig.Results().Len() == 0 {
20+
if gomegainfo.IsGomegaType(arg) && sig.Results().Len() == 0 {
2021
argType |= FuncSigArgType | GomegaParamArgType
2122
}
2223
}

internal/gomegahandler/dothandler.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package gomegahandler
2+
3+
import (
4+
"go/ast"
5+
6+
"golang.org/x/tools/go/analysis"
7+
8+
"github.com/nunnatsa/ginkgolinter/internal/gomegainfo"
9+
)
10+
11+
// dotHandler is used when importing gomega with dot; i.e.
12+
// import . "github.com/onsi/gomega"
13+
type dotHandler struct {
14+
pass *analysis.Pass
15+
}
16+
17+
// GetActualFuncName returns the name of the gomega function, e.g. `Expect`
18+
func (h dotHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) {
19+
switch actualFunc := expr.Fun.(type) {
20+
case *ast.Ident:
21+
return actualFunc.Name, true
22+
case *ast.SelectorExpr:
23+
if h.isGomegaVar(actualFunc.X) {
24+
return actualFunc.Sel.Name, true
25+
}
26+
27+
if x, ok := actualFunc.X.(*ast.CallExpr); ok {
28+
return h.GetActualFuncName(x)
29+
}
30+
31+
case *ast.CallExpr:
32+
return h.GetActualFuncName(actualFunc)
33+
}
34+
return "", false
35+
}
36+
37+
// ReplaceFunction replaces the function with another one, for fix suggestions
38+
func (dotHandler) ReplaceFunction(caller *ast.CallExpr, newExpr *ast.Ident) {
39+
switch f := caller.Fun.(type) {
40+
case *ast.Ident:
41+
caller.Fun = newExpr
42+
case *ast.SelectorExpr:
43+
f.Sel = newExpr
44+
}
45+
}
46+
47+
func (dotHandler) GetNewWrapperMatcher(name string, existing *ast.CallExpr) *ast.CallExpr {
48+
return &ast.CallExpr{
49+
Fun: ast.NewIdent(name),
50+
Args: []ast.Expr{existing},
51+
}
52+
}
53+
54+
func (h dotHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr {
55+
actualExpr, ok := assertionFunc.X.(*ast.CallExpr)
56+
if !ok {
57+
return nil
58+
}
59+
60+
switch fun := actualExpr.Fun.(type) {
61+
case *ast.Ident:
62+
return actualExpr
63+
case *ast.SelectorExpr:
64+
if gomegainfo.IsActualMethod(fun.Sel.Name) {
65+
if h.isGomegaVar(fun.X) {
66+
return actualExpr
67+
}
68+
} else {
69+
return h.GetActualExpr(fun)
70+
}
71+
}
72+
return nil
73+
}
74+
75+
func (h dotHandler) GetActualExprClone(origFunc, funcClone *ast.SelectorExpr) *ast.CallExpr {
76+
actualExpr, ok := funcClone.X.(*ast.CallExpr)
77+
if !ok {
78+
return nil
79+
}
80+
81+
switch funClone := actualExpr.Fun.(type) {
82+
case *ast.Ident:
83+
return actualExpr
84+
case *ast.SelectorExpr:
85+
origFun := origFunc.X.(*ast.CallExpr).Fun.(*ast.SelectorExpr)
86+
if gomegainfo.IsActualMethod(funClone.Sel.Name) {
87+
if h.isGomegaVar(origFun.X) {
88+
return actualExpr
89+
}
90+
} else {
91+
return h.GetActualExprClone(origFun, funClone)
92+
}
93+
}
94+
return nil
95+
}
96+
97+
func (h dotHandler) isGomegaVar(x ast.Expr) bool {
98+
return gomegainfo.IsGomegaVar(x, h.pass)
99+
}

internal/gomegahandler/handler.go

Lines changed: 0 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package gomegahandler
22

33
import (
44
"go/ast"
5-
gotypes "go/types"
6-
"regexp"
7-
85
"golang.org/x/tools/go/analysis"
96
)
107

@@ -48,229 +45,3 @@ func GetGomegaHandler(file *ast.File, pass *analysis.Pass) Handler {
4845

4946
return nil // no gomega import; this file does not use gomega
5047
}
51-
52-
// dotHandler is used when importing gomega with dot; i.e.
53-
// import . "github.com/onsi/gomega"
54-
type dotHandler struct {
55-
pass *analysis.Pass
56-
}
57-
58-
// GetActualFuncName returns the name of the gomega function, e.g. `Expect`
59-
func (h dotHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) {
60-
switch actualFunc := expr.Fun.(type) {
61-
case *ast.Ident:
62-
return actualFunc.Name, true
63-
case *ast.SelectorExpr:
64-
if h.isGomegaVar(actualFunc.X) {
65-
return actualFunc.Sel.Name, true
66-
}
67-
68-
if x, ok := actualFunc.X.(*ast.CallExpr); ok {
69-
return h.GetActualFuncName(x)
70-
}
71-
72-
case *ast.CallExpr:
73-
return h.GetActualFuncName(actualFunc)
74-
}
75-
return "", false
76-
}
77-
78-
// ReplaceFunction replaces the function with another one, for fix suggestions
79-
func (dotHandler) ReplaceFunction(caller *ast.CallExpr, newExpr *ast.Ident) {
80-
switch f := caller.Fun.(type) {
81-
case *ast.Ident:
82-
caller.Fun = newExpr
83-
case *ast.SelectorExpr:
84-
f.Sel = newExpr
85-
}
86-
}
87-
88-
func (dotHandler) GetNewWrapperMatcher(name string, existing *ast.CallExpr) *ast.CallExpr {
89-
return &ast.CallExpr{
90-
Fun: ast.NewIdent(name),
91-
Args: []ast.Expr{existing},
92-
}
93-
}
94-
95-
// nameHandler is used when importing gomega without name; i.e.
96-
// import "github.com/onsi/gomega"
97-
//
98-
// or with a custom name; e.g.
99-
// import customname "github.com/onsi/gomega"
100-
type nameHandler struct {
101-
name string
102-
pass *analysis.Pass
103-
}
104-
105-
// GetActualFuncName returns the name of the gomega function, e.g. `Expect`
106-
func (g nameHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) {
107-
selector, ok := expr.Fun.(*ast.SelectorExpr)
108-
if !ok {
109-
return "", false
110-
}
111-
112-
switch x := selector.X.(type) {
113-
case *ast.Ident:
114-
if x.Name != g.name {
115-
if !g.isGomegaVar(x) {
116-
return "", false
117-
}
118-
}
119-
120-
return selector.Sel.Name, true
121-
122-
case *ast.CallExpr:
123-
return g.GetActualFuncName(x)
124-
}
125-
126-
return "", false
127-
}
128-
129-
// ReplaceFunction replaces the function with another one, for fix suggestions
130-
func (nameHandler) ReplaceFunction(caller *ast.CallExpr, newExpr *ast.Ident) {
131-
caller.Fun.(*ast.SelectorExpr).Sel = newExpr
132-
}
133-
134-
func (g nameHandler) isGomegaVar(x ast.Expr) bool {
135-
return isGomegaVar(x, g.pass)
136-
}
137-
138-
var gomegaTypeRegex = regexp.MustCompile(`github\.com/onsi/gomega/(?:internal|types)\.Gomega`)
139-
140-
func isGomegaVar(x ast.Expr, pass *analysis.Pass) bool {
141-
if tx, ok := pass.TypesInfo.Types[x]; ok {
142-
return IsGomegaType(tx.Type)
143-
}
144-
145-
return false
146-
}
147-
148-
func IsGomegaType(t gotypes.Type) bool {
149-
var typeStr string
150-
switch ttx := t.(type) {
151-
case *gotypes.Pointer:
152-
tp := ttx.Elem()
153-
typeStr = tp.String()
154-
155-
case *gotypes.Named:
156-
typeStr = ttx.String()
157-
158-
default:
159-
return false
160-
}
161-
162-
return gomegaTypeRegex.MatchString(typeStr)
163-
}
164-
165-
func (h dotHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr {
166-
actualExpr, ok := assertionFunc.X.(*ast.CallExpr)
167-
if !ok {
168-
return nil
169-
}
170-
171-
switch fun := actualExpr.Fun.(type) {
172-
case *ast.Ident:
173-
return actualExpr
174-
case *ast.SelectorExpr:
175-
if isHelperMethods(fun.Sel.Name) {
176-
return h.GetActualExpr(fun)
177-
}
178-
if h.isGomegaVar(fun.X) {
179-
return actualExpr
180-
}
181-
}
182-
return nil
183-
}
184-
185-
func (h dotHandler) GetActualExprClone(origFunc, funcClone *ast.SelectorExpr) *ast.CallExpr {
186-
actualExpr, ok := funcClone.X.(*ast.CallExpr)
187-
if !ok {
188-
return nil
189-
}
190-
191-
switch funClone := actualExpr.Fun.(type) {
192-
case *ast.Ident:
193-
return actualExpr
194-
case *ast.SelectorExpr:
195-
origFun := origFunc.X.(*ast.CallExpr).Fun.(*ast.SelectorExpr)
196-
if isHelperMethods(funClone.Sel.Name) {
197-
return h.GetActualExprClone(origFun, funClone)
198-
}
199-
if h.isGomegaVar(origFun.X) {
200-
return actualExpr
201-
}
202-
}
203-
return nil
204-
}
205-
206-
func (h dotHandler) isGomegaVar(x ast.Expr) bool {
207-
return isGomegaVar(x, h.pass)
208-
}
209-
210-
func (g nameHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr {
211-
actualExpr, ok := assertionFunc.X.(*ast.CallExpr)
212-
if !ok {
213-
return nil
214-
}
215-
216-
switch fun := actualExpr.Fun.(type) {
217-
case *ast.Ident:
218-
return actualExpr
219-
case *ast.SelectorExpr:
220-
if x, ok := fun.X.(*ast.Ident); ok && x.Name == g.name {
221-
return actualExpr
222-
}
223-
if isHelperMethods(fun.Sel.Name) {
224-
return g.GetActualExpr(fun)
225-
}
226-
227-
if g.isGomegaVar(fun.X) {
228-
return actualExpr
229-
}
230-
}
231-
return nil
232-
}
233-
234-
func (g nameHandler) GetActualExprClone(origFunc, funcClone *ast.SelectorExpr) *ast.CallExpr {
235-
actualExpr, ok := funcClone.X.(*ast.CallExpr)
236-
if !ok {
237-
return nil
238-
}
239-
240-
switch funClone := actualExpr.Fun.(type) {
241-
case *ast.Ident:
242-
return actualExpr
243-
case *ast.SelectorExpr:
244-
if x, ok := funClone.X.(*ast.Ident); ok && x.Name == g.name {
245-
return actualExpr
246-
}
247-
origFun := origFunc.X.(*ast.CallExpr).Fun.(*ast.SelectorExpr)
248-
if isHelperMethods(funClone.Sel.Name) {
249-
return g.GetActualExprClone(origFun, funClone)
250-
}
251-
252-
if g.isGomegaVar(origFun.X) {
253-
return actualExpr
254-
}
255-
}
256-
return nil
257-
}
258-
259-
func (g nameHandler) GetNewWrapperMatcher(name string, existing *ast.CallExpr) *ast.CallExpr {
260-
return &ast.CallExpr{
261-
Fun: &ast.SelectorExpr{
262-
X: ast.NewIdent(g.name),
263-
Sel: ast.NewIdent(name),
264-
},
265-
Args: []ast.Expr{existing},
266-
}
267-
}
268-
269-
func isHelperMethods(funcName string) bool {
270-
switch funcName {
271-
case "WithOffset", "WithTimeout", "WithPolling", "Within", "ProbeEvery", "WithContext", "WithArguments", "MustPassRepeatedly":
272-
return true
273-
}
274-
275-
return false
276-
}

0 commit comments

Comments
 (0)