Skip to content

Commit 32122e4

Browse files
authored
Merge pull request #22 from inteon/values_linter_rewrite
Refactor linter and fix bugs/ missing functionality
2 parents 4779c32 + a103276 commit 32122e4

File tree

5 files changed

+307
-183
lines changed

5 files changed

+307
-183
lines changed

linter/linter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func Lint(
3939
valuePathsDict := map[string]struct{}{}
4040
for _, section := range document.Sections {
4141
for _, property := range section.Properties {
42-
valuePathsDict[property.Path.String()] = struct{}{}
42+
valuePathsDict[property.Path.PatternString()] = struct{}{}
4343
}
4444
}
4545
valuePathsDict = parsetemplates.MakeUniform(valuePathsDict)

linter/parsetemplates/templates.go

Lines changed: 160 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package parsetemplates
1818

1919
import (
20-
"fmt"
2120
"os"
2221
"path/filepath"
2322
"reflect"
@@ -33,7 +32,7 @@ func ListTemplatePaths(templatesPath string) ([]string, error) {
3332

3433
tmpl.Funcs(funcs_serdes.FuncMap())
3534

36-
templates := map[*template.Template]struct{}{}
35+
templates := Set[*template.Template]{}
3736

3837
// parse all templates
3938
err := filepath.Walk(
@@ -68,79 +67,80 @@ func ListTemplatePaths(templatesPath string) ([]string, error) {
6867
return nil, err
6968
}
7069

71-
// remove prefixes
72-
templatePaths := make([]string, 0, len(templatePathStrings))
73-
for _, pathString := range templatePathStrings {
74-
if !strings.HasPrefix(pathString, ".$") {
75-
continue
76-
}
77-
78-
templatePaths = append(templatePaths, strings.TrimPrefix(pathString, ".$"))
79-
}
70+
return templatePathStrings, nil
71+
}
8072

81-
return templatePaths, nil
73+
type templateUsage struct {
74+
node string
75+
context string
8276
}
8377

78+
const (
79+
RootNode = "<root-node>"
80+
RootPath = "<root-path>"
81+
)
82+
8483
func ListTemplatePathsFromTemplates(
8584
tmpl *template.Template,
86-
templates map[*template.Template]struct{},
85+
templates Set[*template.Template],
8786
) ([]string, error) {
88-
// walk all templates
89-
templateResults := map[string]map[string]struct{}{}
90-
templateUsage := map[string][][]string{}
87+
// templateResults lists all property paths that are used in a template
88+
templateResults := map[string]Set[string]{}
89+
// templateUsage lists all templates that are used in a template
90+
templateUsages := map[string]Set[templateUsage]{}
9191
for _, t := range tmpl.Templates() {
92-
root := "$"
93-
tmplPath := "$"
94-
if _, ok := templates[t]; !ok {
95-
// we found a directly parsed template
96-
root = ""
97-
tmplPath = t.Name()
92+
var selfNode, selfPath string
93+
if _, ok := templates[t]; ok {
94+
// we found a template that is the output of a
95+
// parsed file in the templates folder
96+
selfNode = RootNode
97+
selfPath = RootPath
98+
} else {
99+
// we found a template that is NOT the output
100+
// of a parsed file in the templates folder
101+
selfNode = t.Name()
102+
selfPath = ""
98103
}
99104

100-
result, ok := templateResults[tmplPath]
101-
if !ok {
102-
result = map[string]struct{}{}
103-
templateResults[tmplPath] = result
104-
}
105+
results := getSet(templateResults, selfNode)
106+
usages := getSet(templateUsages, selfNode)
105107

106-
walk(t.Root, root, func(path string, node parse.Node, templateName string) {
107-
if node != nil {
108-
result[path] = struct{}{}
109-
} else {
110-
templateUsage[tmplPath] = append(templateUsage[tmplPath], []string{templateName, path})
111-
}
112-
}, func(varname string, assignName, assign, access *string) {
113-
if assign != nil {
114-
varPath := fmt.Sprintf("%s%s", tmplPath, varname)
115-
assignPath := fmt.Sprintf("%s%s", tmplPath, *assignName)
116-
templateUsage[assignPath] = append(templateUsage[assignPath], []string{varPath, *assign})
117-
} else {
118-
varPath := fmt.Sprintf("%s%s", tmplPath, varname)
119-
result, ok := templateResults[varPath]
120-
if !ok {
121-
result = map[string]struct{}{}
122-
templateResults[varPath] = result
123-
}
124-
125-
result[*access] = struct{}{}
126-
}
127-
})
108+
walk(t.Root, selfNode, selfPath,
109+
// Found path to a value
110+
func(path string) {
111+
results.Insert(path)
112+
},
113+
// Found template call
114+
func(templateName string, context string) {
115+
usages.Insert(templateUsage{templateName, context})
116+
},
117+
// Found local variable usage
118+
func(varname string, path string) {
119+
getSet(templateResults, joinPath(selfNode, varname)).Insert(path)
120+
},
121+
// Found local variable definition
122+
func(varname string, node, path string) {
123+
getSet(templateUsages, node).Insert(templateUsage{joinPath(selfNode, varname), path})
124+
},
125+
)
128126
}
129127

130-
followPath("$", templateUsage, func(path, tmplPath string) {
131-
for key := range templateResults[tmplPath] {
132-
templateResults["$"][fmt.Sprintf("%s%s", path, key)] = struct{}{}
128+
followPath(RootNode, Set[string]{}, templateUsages, func(node, path string) {
129+
for key := range templateResults[node] {
130+
if strings.HasPrefix(key, RootPath) {
131+
templateResults[RootNode].Insert(key)
132+
} else {
133+
templateResults[RootNode].Insert(joinPath(path, key))
134+
}
133135
}
134136
})
135137

136138
paths := map[string]struct{}{}
137-
for key := range templateResults["$"] {
138-
if !strings.HasPrefix(key, "$.Values") {
139+
for key := range templateResults[RootNode] {
140+
if !strings.HasPrefix(key, joinPath(RootPath, "Values")) {
139141
continue
140142
}
141-
key = strings.TrimPrefix(key, "$.Values")
142-
key = "$" + key
143-
143+
key = strings.TrimPrefix(key, joinPath(RootPath, "Values"))
144144
paths[key] = struct{}{}
145145
}
146146

@@ -156,124 +156,166 @@ func ListTemplatePathsFromTemplates(
156156
}
157157

158158
func followPath(
159-
key string,
160-
templateUsage map[string][][]string,
161-
run func(path string, tpath string),
159+
node string,
160+
visited Set[string],
161+
templateUsage map[string]Set[templateUsage],
162+
run func(node string, path string),
162163
) {
163-
for _, usage := range templateUsage[key] {
164-
followPath(usage[0], templateUsage, func(path, tpath string) {
165-
run(fmt.Sprintf("%s%s", usage[1], path), tpath)
164+
if visited.Has(node) {
165+
return
166+
}
167+
visited.Insert(node)
168+
169+
for usage := range templateUsage[node] {
170+
// Recursively follow the path until we reach the <root-node>
171+
followPath(usage.node, visited, templateUsage, func(node, path string) {
172+
run(node, joinPath(usage.context, path))
166173
})
167174
}
168-
run("", key)
175+
run(node, "")
176+
177+
visited.Delete(node)
169178
}
170179

171180
func walk(
172181
node parse.Node,
182+
parentNode string,
173183
parentPath string,
174-
foundPathFn func(path string, node parse.Node, templateName string),
175-
foundLocalVar func(varname string, assignPath, assign, access *string),
184+
// foundPathFn is called when a used path is found
185+
foundPathFn func(path string),
186+
// foundTemplateFn is called when a template-like call is found
187+
foundTemplateFn func(templateName string, context string),
188+
// foundVarUsageFn is called when a variable is used
189+
foundVarUsageFn func(varname string, path string),
190+
// foundVarDefFn is called when a variable is defined
191+
foundVarDefFn func(varname string, node, path string),
176192
) {
177193
if node == nil || reflect.ValueOf(node).IsNil() {
178194
return
179195
}
180196

181-
path := parentPath
182197
switch tn := node.(type) {
198+
case *parse.DotNode:
199+
foundPathFn(parentPath)
183200
case *parse.FieldNode:
184201
if len(tn.Ident) == 0 {
185202
} else if tn.Ident[0] == "$" {
186-
path = strings.Join(tn.Ident, ".")
203+
foundPathFn(joinPath(RootPath, tn.Ident[1:]...))
187204
} else {
188-
path = fmt.Sprintf("%s.%s", parentPath, strings.Join(tn.Ident, "."))
205+
foundPathFn(joinPath(parentPath, tn.Ident...))
189206
}
207+
return
190208
case *parse.VariableNode:
191209
if len(tn.Ident) == 0 {
192210
} else if tn.Ident[0] == "$" {
193-
path = strings.Join(tn.Ident, ".")
211+
foundPathFn(joinPath(RootPath, tn.Ident[1:]...))
194212
} else if len(tn.Ident[0]) >= 1 && tn.Ident[0][0] == '$' {
195-
path := "." + strings.Join(tn.Ident[1:], ".")
196-
foundLocalVar(tn.Ident[0], nil, nil, &path)
197-
return
213+
foundVarUsageFn(tn.Ident[0], joinPath("", tn.Ident[1:]...))
198214
} else {
199-
path = fmt.Sprintf("%s.%s", parentPath, strings.Join(tn.Ident, "."))
215+
foundPathFn(joinPath(parentPath, tn.Ident...))
200216
}
217+
return
201218
}
202219

203-
foundPathFn(path, node, "")
204-
205220
switch tn := node.(type) {
206221
case *parse.ActionNode:
207-
walk(tn.Pipe, path, foundPathFn, foundLocalVar)
222+
walk(tn.Pipe, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
208223
case *parse.ChainNode:
209-
walk(tn.Node, path, foundPathFn, foundLocalVar)
224+
walk(tn.Node, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
210225
case *parse.CommandNode:
211226
// handle 'include "test.labels" .' separately
212227
if len(tn.Args) >= 3 && tn.Args[0].String() == "include" && tn.Args[1].Type() == parse.NodeString {
213-
foundPathFn(getPath(tn.Args[2], path), nil, tn.Args[1].(*parse.StringNode).Text)
228+
foundTemplateFn(
229+
tn.Args[1].(*parse.StringNode).Text,
230+
getPath(tn.Args[2], parentNode, parentPath),
231+
)
214232
}
215233
for _, snode := range tn.Args {
216-
walk(snode, path, foundPathFn, foundLocalVar)
234+
walk(snode, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
217235
}
218236
case *parse.BranchNode:
219-
walk(tn.Pipe, path, foundPathFn, foundLocalVar)
220-
walk(tn.List, path, foundPathFn, foundLocalVar)
221-
walk(tn.ElseList, path, foundPathFn, foundLocalVar)
237+
walk(tn.Pipe, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
238+
walk(tn.List, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
239+
walk(tn.ElseList, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
222240
case *parse.ListNode:
223241
for _, snode := range tn.Nodes {
224-
walk(snode, path, foundPathFn, foundLocalVar)
242+
walk(snode, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
225243
}
226244
case *parse.PipeNode:
227245
for _, cmd := range tn.Cmds {
228-
walk(cmd, path, foundPathFn, foundLocalVar)
246+
walk(cmd, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
229247
}
230248

231249
for _, decl := range tn.Decl {
232250
for _, cmd := range tn.Cmds {
233-
walk(cmd, path, func(path string, _ parse.Node, _ string) {
234-
empty := ""
235-
foundLocalVar(decl.String(), &empty, &path, nil)
236-
}, func(varname string, _, _, access *string) {
237-
if access == nil {
238-
return
239-
}
240-
foundLocalVar(decl.String(), &varname, access, nil)
241-
})
251+
walk(cmd, parentNode, parentPath,
252+
func(path string) {
253+
foundVarDefFn(decl.String(), parentNode, path)
254+
},
255+
func(templateName, context string) {
256+
foundVarDefFn(decl.String(), templateName, context)
257+
},
258+
func(varname string, path string) {
259+
foundVarDefFn(decl.String(), joinPath(parentNode, varname), path)
260+
},
261+
func(varname string, node, path string) {
262+
// TODO: maybe do something here?
263+
},
264+
)
242265

243266
}
244267
}
245268
case *parse.TemplateNode:
246-
path := getPath(tn.Pipe, path)
247-
foundPathFn(path, nil, tn.Name)
269+
walk(tn.Pipe, parentNode, parentPath,
270+
func(path string) {
271+
foundTemplateFn(tn.Name, path)
272+
},
273+
func(templateName, context string) {},
274+
func(varname string, path string) {},
275+
func(varname string, node, path string) {},
276+
)
248277
case *parse.IfNode:
249-
walk(&tn.BranchNode, path, foundPathFn, foundLocalVar)
278+
walk(&tn.BranchNode, parentNode, parentPath, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
250279
case *parse.RangeNode:
251-
walk(tn.Pipe, path,
252-
func(path string, node parse.Node, templateName string) {},
253-
func(varname string, assignPath, assign, access *string) {
254-
if assign == nil {
255-
return
256-
}
257-
newAssign := *assign + ".[*]"
258-
foundLocalVar(varname, assignPath, &newAssign, access)
280+
walk(tn.Pipe, parentNode, parentPath,
281+
func(path string) {
282+
foundPathFn(path + "[*]")
283+
},
284+
func(templateName, context string) {
285+
foundTemplateFn(templateName, context+"[*]")
286+
},
287+
func(varname string, path string) {
288+
foundVarUsageFn(varname, path+"[*]")
289+
},
290+
func(varname string, node, path string) {
291+
foundVarDefFn(varname, node, path+"[*]")
259292
},
260293
)
261-
path := getPath(tn.Pipe, path) + ".[*]"
262-
walk(tn.List, path, foundPathFn, foundLocalVar)
263-
walk(tn.ElseList, path, foundPathFn, foundLocalVar)
294+
path := getPath(tn.Pipe, parentNode, parentPath) + "[*]"
295+
walk(tn.List, parentNode, path, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
296+
walk(tn.ElseList, parentNode, path, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
264297
case *parse.WithNode:
265-
path := getPath(tn.Pipe, path)
266-
walk(tn.List, path, foundPathFn, foundLocalVar)
267-
walk(tn.ElseList, path, foundPathFn, foundLocalVar)
298+
path := getPath(tn.Pipe, parentNode, parentPath)
299+
walk(tn.List, parentNode, path, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
300+
walk(tn.ElseList, parentNode, path, foundPathFn, foundTemplateFn, foundVarUsageFn, foundVarDefFn)
268301
}
269302
}
270303

271-
func getPath(node parse.Node, parentPath string) string {
272-
longestPath := ""
273-
walk(node, parentPath, func(path string, _ parse.Node, _ string) {
274-
if len(path) > len(longestPath) {
275-
longestPath = path
276-
}
277-
}, func(varname string, assignPath, assign, access *string) {})
304+
func getPath(node parse.Node, parentNode string, parentPath string) string {
305+
longestPath := parentPath
306+
walk(node, parentNode, parentPath,
307+
func(path string) {
308+
if len(path) > len(longestPath) {
309+
longestPath = path
310+
}
311+
},
312+
func(_, context string) {
313+
if len(context) > len(longestPath) {
314+
longestPath = context
315+
}
316+
},
317+
func(varname string, path string) {},
318+
func(varname string, node, path string) {},
319+
)
278320
return longestPath
279321
}

0 commit comments

Comments
 (0)