Skip to content

Commit 28c8ccb

Browse files
authored
Merge pull request #56 from ilya-hontarau/fix/incorrect-handling-variadic-functions
Fix handling variadic functions
2 parents b209725 + 0303eac commit 28c8ccb

File tree

11 files changed

+106
-32
lines changed

11 files changed

+106
-32
lines changed

internal/services/checker/deepscan/models.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type (
1919
ArgumentDefinition Source // where method param type defined (func (a,b,c _int_))
2020
Interface Interface // used interface for injection
2121
Implementations []Implementation // all code links to this param
22+
IsVariadic bool // function param is variadic (func (a bool, nums ...int))
2223
}
2324

2425
Interface struct {

internal/services/checker/deepscan/searcher_find_implementations.go

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -131,43 +131,57 @@ func (s *Searcher) applyMethodImplementationsInPackages(method *InjectionMethod,
131131
for gateIndex := range method.Gates {
132132
gate := &method.Gates[gateIndex]
133133
callMethod := callExpr.Fun
134-
callParam := callExpr.Args[gate.Index]
135-
136-
paramType := astPackage.TypesInfo.TypeOf(callParam)
137-
targetName, targetPos, valid := s.extractTargetFromCallParam(paramType)
138-
if !valid {
139-
// unknown injection type, possible generics or other not known
140-
// go features on current moment
141-
// we can extend this function later for new cases
142-
continue
143-
}
144134

145-
targetDefinitions := s.sourceFromToken(targetPos)
146-
if targetDefinitions.Import == method.Definition.Import {
147-
// injector use our public interface for typing
148-
// we exclude this cases from more deep analyse, because of runtime
149-
// types injection. (we have not known type on compile time)
135+
// variadic function arguments may be absent in callExpr.Args
136+
if gate.IsVariadic && len(callExpr.Args) <= gate.Index {
150137
continue
151138
}
152-
153-
if !targetDefinitions.Place.Valid {
154-
// invalid target
155-
// possible is some not importable std const like `errors`
156-
// or not known ast at this moment
157-
continue
139+
maxIdx := gate.Index
140+
if gate.IsVariadic {
141+
// variadic function arguments are passed as individual elements in callExpr.Args
142+
// iterate over callExpr.Args to process all variadic arguments
143+
maxIdx = len(callExpr.Args) - 1
158144
}
159145

160-
gate.Implementations = append(gate.Implementations, Implementation{
161-
Injector: Injector{
162-
CodeName: s.extractCodeFromASTNode(callParam),
163-
ParamDefinition: s.sourceFromToken(callParam.Pos()),
164-
MethodDefinition: s.sourceFromToken(callMethod.Pos()),
165-
},
166-
Target: Target{
167-
StructName: targetName,
168-
Definition: targetDefinitions,
169-
},
170-
})
146+
for idx := gate.Index; idx <= maxIdx; idx++ {
147+
callParam := callExpr.Args[idx]
148+
149+
paramType := astPackage.TypesInfo.TypeOf(callParam)
150+
targetName, targetPos, valid := s.extractTargetFromCallParam(paramType)
151+
if !valid {
152+
// unknown injection type, possible generics or other not known
153+
// go features on current moment
154+
// we can extend this function later for new cases
155+
continue
156+
}
157+
158+
targetDefinitions := s.sourceFromToken(targetPos)
159+
if targetDefinitions.Import == method.Definition.Import {
160+
// injector use our public interface for typing
161+
// we exclude this cases from more deep analyse, because of runtime
162+
// types injection. (we have not known type on compile time)
163+
continue
164+
}
165+
166+
if !targetDefinitions.Place.Valid {
167+
// invalid target
168+
// possible is some not importable std const like `errors`
169+
// or not known ast at this moment
170+
continue
171+
}
172+
173+
gate.Implementations = append(gate.Implementations, Implementation{
174+
Injector: Injector{
175+
CodeName: s.extractCodeFromASTNode(callParam),
176+
ParamDefinition: s.sourceFromToken(callParam.Pos()),
177+
MethodDefinition: s.sourceFromToken(callMethod.Pos()),
178+
},
179+
Target: Target{
180+
StructName: targetName,
181+
Definition: targetDefinitions,
182+
},
183+
})
184+
}
171185
}
172186
})
173187
}

internal/services/checker/deepscan/searcher_parse_methods.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ func (s *Searcher) extractMethodGates(astPackage *packages.Package, method *ast.
8484
pos = field.Pos()
8585
}
8686

87+
var isVariadic bool
88+
if _, ok := field.Type.(*ast.Ellipsis); ok {
89+
isVariadic = true
90+
}
91+
8792
params = append(params, Gate{
8893
MethodName: method.Name.Name,
8994
ParamName: fieldIdent.Name,
@@ -95,6 +100,7 @@ func (s *Searcher) extractMethodGates(astPackage *packages.Package, method *ast.
95100
Definition: s.sourceFromToken(pos),
96101
GoType: paramType.String(),
97102
},
103+
IsVariadic: isVariadic,
98104
})
99105
}
100106
}

test/check/arch3_variadic.ct

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
$ go-arch-lint check --project-path ${PWD}/test/check/project --arch-file arch3_variadic.yml --output-color=false
2+
module: github.com/fe3dback/go-arch-lint/test/check/project
3+
linters:
4+
On | Base: component imports # always on
5+
On | Advanced: vendor imports # switch 'allow.depOnAnyVendor = false' (or delete) to on
6+
On | Advanced: method calls and dependency injections # switch 'allow.deepScan = true' (or delete) to on
7+
8+
OK - No warnings found

test/check/project/arch1_nested_glob.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ allow:
66
exclude:
77
- internal/excluded
88
- vendor
9+
- variadic
910

1011
excludeFiles:
1112
- "^.*_test\\.go$"

test/check/project/arch1_ok.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ allow:
66
exclude:
77
- internal/excluded
88
- vendor
9+
- variadic
910

1011
excludeFiles:
1112
- "^.*_test\\.go$"

test/check/project/arch1_warnings.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ allow:
66
exclude:
77
- internal/excluded
88
- vendor
9+
- variadic
910

1011
excludeFiles:
1112
- "^.*_test\\.go$"

test/check/project/arch2_ok_fallback.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ allow:
66
exclude:
77
- internal/excluded
88
- vendor
9+
- variadic
910

1011
excludeFiles:
1112
- "^.*_test\\.go$"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
version: 3
2+
3+
workdir:
4+
variadic
5+
6+
allow:
7+
depOnAnyVendor: false
8+
deepScan: true
9+
10+
components:
11+
foo: { in: foo }
12+
use_foo: { in: use_foo }
13+
14+
deps:
15+
foo:
16+
anyProjectDeps: true
17+
18+
use_foo:
19+
anyProjectDeps: true
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package foo
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type Foo []string
8+
9+
func FooF(message string, args ...any) Foo {
10+
return Foo{fmt.Sprintf(message, args)}
11+
}

0 commit comments

Comments
 (0)