@@ -53,17 +53,18 @@ type funCall struct {
5353func acceptsNameArgument (def * build.DefStmt ) bool {
5454 for _ , param := range def .Params {
5555 if name , op := build .GetParamName (param ); name == "name" || op == "**" {
56- return true
56+ return true
5757 }
5858 }
5959 return false
6060}
6161
6262// fileData represents information about rules and functions extracted from a file
6363type fileData struct {
64- rules map [string ]bool // all rules defined in the file
65- functions map [string ]map [string ]funCall // outer map: all functions defined in the file, inner map: all distinct function calls from the given function
66- aliases map [string ]function // all top-level aliases (e.g. `foo = bar`).
64+ loadedSymbols map [string ]function // Symbols loaded from other files.
65+ rulesOrMacros map [string ]bool // all rules or macros defined in the file.
66+ functions map [string ]map [string ]funCall // outer map: all functions defined in the file, inner map: all distinct function calls from the given function
67+ aliases map [string ]function // all top-level aliases (e.g. `foo = bar`).
6768}
6869
6970// resolvesExternal takes a local function definition and replaces it with an external one if it's been defined
@@ -123,8 +124,14 @@ func analyzeFile(f *build.File) fileData {
123124 return fileData {}
124125 }
125126
127+ report := fileData {
128+ loadedSymbols : make (map [string ]function ),
129+ rulesOrMacros : make (map [string ]bool ),
130+ functions : make (map [string ]map [string ]funCall ),
131+ aliases : make (map [string ]function ),
132+ }
133+
126134 // Collect loaded symbols
127- externalSymbols := make (map [string ]function )
128135 for _ , stmt := range f .Stmt {
129136 load , ok := stmt .(* build.LoadStmt )
130137 if ! ok {
@@ -135,15 +142,10 @@ func analyzeFile(f *build.File) fileData {
135142 continue
136143 }
137144 for i , from := range load .From {
138- externalSymbols [load .To [i ].Name ] = function {label .Package , label .Target , from .Name }
145+ report . loadedSymbols [load .To [i ].Name ] = function {label .Package , label .Target , from .Name }
139146 }
140147 }
141148
142- report := fileData {
143- rules : make (map [string ]bool ),
144- functions : make (map [string ]map [string ]funCall ),
145- aliases : make (map [string ]function ),
146- }
147149 for _ , stmt := range f .Stmt {
148150 switch stmt := stmt .(type ) {
149151 case * build.AssignExpr :
@@ -153,21 +155,22 @@ func analyzeFile(f *build.File) fileData {
153155 continue
154156 }
155157 if rhsIdent , ok := stmt .RHS .(* build.Ident ); ok {
156- report .aliases [lhsIdent .Name ] = resolveExternal (function {f .Pkg , f .Label , rhsIdent .Name }, externalSymbols )
158+ report .aliases [lhsIdent .Name ] = resolveExternal (function {f .Pkg , f .Label , rhsIdent .Name }, report . loadedSymbols )
157159 continue
158160 }
159161
160162 call , ok := stmt .RHS .(* build.CallExpr )
161163 if ! ok {
162164 continue
163165 }
164- ident , ok := call .X .(* build.Ident )
165- if ! ok || ident .Name != "rule" {
166- continue
166+ if ident , ok := call .X .(* build.Ident ); ok {
167+ if ident .Name == "rule" || ident .Name == "macro" {
168+ report .rulesOrMacros [lhsIdent .Name ] = true
169+ continue
170+ }
167171 }
168- report .rules [lhsIdent .Name ] = true
169172 case * build.DefStmt :
170- report .functions [stmt .Name ] = getFunCalls (stmt , f .Pkg , f .Label , externalSymbols )
173+ report .functions [stmt .Name ] = getFunCalls (stmt , f .Pkg , f .Label , report . loadedSymbols )
171174 default :
172175 continue
173176 }
@@ -177,8 +180,8 @@ func analyzeFile(f *build.File) fileData {
177180
178181// functionReport represents the analysis result of a function
179182type functionReport struct {
180- isMacro bool // whether the function is a macro (or a rule)
181- fc * funCall // a call to the rule or another macro
183+ isRuleOrMacro bool // whether the function is a macro (or a rule)
184+ fc * funCall // a call to the rule or another macro
182185}
183186
184187// macroAnalyzer is an object that analyzes the directed graph of functions calling each other,
@@ -208,7 +211,7 @@ func (ma macroAnalyzer) getFileData(pkg, label string) fileData {
208211}
209212
210213// IsMacro is a public function that checks whether the given function is a macro
211- func (ma macroAnalyzer ) IsMacro (fn function ) (report functionReport ) {
214+ func (ma macroAnalyzer ) IsRuleOrMacro (fn function ) (report functionReport ) {
212215 // Check the cache first
213216 if cached , ok := ma .cache [fn ]; ok {
214217 return cached
@@ -228,7 +231,7 @@ func (ma macroAnalyzer) IsMacro(fn function) (report functionReport) {
228231 "repository_name" , "exports_files" :
229232 // Not a rule
230233 default :
231- report .isMacro = true
234+ report .isRuleOrMacro = true
232235 }
233236 return
234237 }
@@ -237,18 +240,26 @@ func (ma macroAnalyzer) IsMacro(fn function) (report functionReport) {
237240
238241 // Check whether fn.name is an alias for another function
239242 if alias , ok := fileData .aliases [fn .name ]; ok {
240- if ma .IsMacro (alias ).isMacro {
241- report .isMacro = true
243+ if ma .IsRuleOrMacro (alias ).isRuleOrMacro {
244+ report .isRuleOrMacro = true
242245 }
243246 return
244247 }
245248
246- // Check whether fn.name is a rule
247- if fileData .rules [fn .name ] {
248- report .isMacro = true
249+ // Check whether fn.name is a rule or macro
250+ if fileData .rulesOrMacros [fn .name ] {
251+ report .isRuleOrMacro = true
249252 return
250253 }
251254
255+ // Check whether fn.name is a loaded symbol from another file
256+ if externalFn , ok := fileData .loadedSymbols [fn .name ]; ok {
257+ if ma .IsRuleOrMacro (externalFn ).isRuleOrMacro {
258+ report .isRuleOrMacro = true
259+ return
260+ }
261+ }
262+
252263 // Check whether fn.name is an ordinary function
253264 funCalls , ok := fileData .functions [fn .name ]
254265 if ! ok {
@@ -267,8 +278,8 @@ func (ma macroAnalyzer) IsMacro(fn function) (report functionReport) {
267278 }
268279
269280 for _ , fc := range append (knownFunCalls , newFunCalls ... ) {
270- if ma .IsMacro (fc .function ).isMacro {
271- report .isMacro = true
281+ if ma .IsRuleOrMacro (fc .function ).isRuleOrMacro {
282+ report .isRuleOrMacro = true
272283 report .fc = & fc
273284 return
274285 }
@@ -305,8 +316,8 @@ func unnamedMacroWarning(f *build.File, fileReader *FileReader) []*LinterFinding
305316 continue
306317 }
307318
308- report := macroAnalyzer .IsMacro (function {f .Pkg , f .Label , def .Name })
309- if ! report .isMacro {
319+ report := macroAnalyzer .IsRuleOrMacro (function {f .Pkg , f .Label , def .Name })
320+ if ! report .isRuleOrMacro {
310321 continue
311322 }
312323 msg := fmt .Sprintf (`The macro %q should have a keyword argument called "name".` , def .Name )
0 commit comments