1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
package tuigen
import (
"regexp"
"strings"
)
func (a *Analyzer) validateNameCollisions(file *File) {
seenFuncTempl := make(map[string]Position)
seenMethodTempl := make(map[string]Position)
for _, comp := range file.Components {
if comp.Receiver == "" {
a.validateFunctionTemplCollisions(file, comp, seenFuncTempl)
} else {
a.validateMethodTemplCollisions(file, comp, seenMethodTempl)
}
}
}
func (a *Analyzer) validateFunctionTemplCollisions(file *File, comp *Component, seen map[string]Position) {
if first, ok := seen[comp.Name]; ok {
a.errors.AddErrorf(comp.Position,
"duplicate templ %q (first defined at %s)", comp.Name, first)
return
}
seen[comp.Name] = comp.Position
if a.pkgCtx != nil && a.pkgCtx.HasTempl(comp.Name) {
a.errors.AddErrorf(comp.Position,
"duplicate templ %q (also defined in another file of this package)", comp.Name)
return
}
if hasTopLevelFunc(file.Decls, file.Funcs, comp.Name) ||
(a.pkgCtx != nil && a.pkgCtx.HasFunc(comp.Name)) {
a.errors.Add(NewErrorWithHint(comp.Position,
"templ \""+comp.Name+"\" conflicts with a Go function of the same name",
"rename the templ or the function"))
}
viewName := comp.Name + "View"
if hasTypeDecl(file.Decls, viewName) ||
(a.pkgCtx != nil && a.pkgCtx.HasType(viewName)) {
a.errors.Add(NewErrorWithHint(comp.Position,
"type \""+viewName+"\" conflicts with the view struct generated for templ \""+comp.Name+"\"",
"rename the type; the generator reserves the <Name>View suffix for function templs"))
}
}
func (a *Analyzer) validateMethodTemplCollisions(file *File, comp *Component, seen map[string]Position) {
typeName := strings.TrimPrefix(comp.ReceiverType, "*")
if first, ok := seen[typeName]; ok {
a.errors.AddErrorf(comp.Position,
"duplicate Render templ for receiver type %s (first defined at %s)", typeName, first)
return
}
seen[typeName] = comp.Position
if a.pkgCtx != nil && a.pkgCtx.HasRenderTempl(typeName) {
a.errors.AddErrorf(comp.Position,
"duplicate Render templ for receiver type %s (also defined in another file of this package)", typeName)
return
}
if hasUserMethod(file.Decls, file.Funcs, comp.ReceiverType, "Render") ||
(a.pkgCtx != nil && a.pkgCtx.HasMethod(typeName, "Render")) {
a.errors.Add(NewErrorWithHint(comp.Position,
typeName+" already declares a Render method; the templ generates Render",
"remove the handwritten Render method or the templ"))
}
for _, helper := range []string{"updatePropsFields", "bindAppFields", "unbindAppFields"} {
if hasUserMethod(file.Decls, file.Funcs, comp.ReceiverType, helper) ||
(a.pkgCtx != nil && a.pkgCtx.HasMethod(typeName, helper)) {
a.errors.Add(NewErrorWithHint(comp.Position,
typeName+" declares method \""+helper+"\", which is a reserved generated helper name",
"rename the method; the generator reserves updatePropsFields, bindAppFields, and unbindAppFields on templ receiver types"))
}
}
}
func hasTopLevelFunc(decls []*GoDecl, funcs []*GoFunc, name string) bool {
pattern := regexp.MustCompile(`func\s+` + regexp.QuoteMeta(name) + `\s*\(`)
for _, decl := range decls {
if decl.Kind == "func" && pattern.MatchString(decl.Code) {
return true
}
}
for _, fn := range funcs {
if pattern.MatchString(fn.Code) {
return true
}
}
return false
}
func hasTypeDecl(decls []*GoDecl, name string) bool {
pattern := regexp.MustCompile(`type\s+` + regexp.QuoteMeta(name) + `\b`)
for _, decl := range decls {
if decl.Kind == "type" && pattern.MatchString(decl.Code) {
return true
}
}
return false
}
|