Reject duplicate top level names
diff --git a/lang/ast/ast.go b/lang/ast/ast.go
index 78a7819..65304af 100644
--- a/lang/ast/ast.go
+++ b/lang/ast/ast.go
@@ -473,6 +473,8 @@
type Var Node
func (n *Var) AsNode() *Node { return (*Node)(n) }
+func (n *Var) Filename() string { return n.filename }
+func (n *Var) Line() uint32 { return n.line }
func (n *Var) Name() t.ID { return n.id2 }
func (n *Var) XType() *TypeExpr { return n.lhs.AsTypeExpr() }
diff --git a/lang/check/check.go b/lang/check/check.go
index be1c892..a2da22a 100644
--- a/lang/check/check.go
+++ b/lang/check/check.go
@@ -88,15 +88,16 @@
resolveUse: resolveUse,
reasonMap: rMap,
- consts: map[t.QID]*a.Const{},
+ topLevelNames: map[t.ID]a.Kind{
+ t.IDBase: a.KUse,
+ },
+
+ consts: map[t.QID]*a.Const{},
+ statuses: map[t.QID]*a.Status{},
+ structs: map[t.QID]*a.Struct{},
+
funcs: map[t.QQID]*a.Func{},
localVars: map[t.QQID]typeMap{},
- statuses: map[t.QID]*a.Status{},
- structs: map[t.QID]*a.Struct{},
-
- useBaseNames: map[t.ID]struct{}{
- t.IDBase: struct{}{},
- },
builtInSliceFuncs: map[t.QQID]*a.Func{},
builtInTableFuncs: map[t.QQID]*a.Func{},
@@ -202,7 +203,6 @@
{a.KInvalid, (*Checker).checkInterfacesSatisfied},
{a.KStruct, (*Checker).checkFieldMethodCollisions},
{a.KInvalid, (*Checker).checkAllTypeChecked},
- // TODO: check consts, funcs, structs and uses for name collisions.
}
type reason func(q *checker, n *a.Assert) error
@@ -214,15 +214,20 @@
resolveUse func(usePath string) ([]byte, error)
reasonMap reasonMap
- consts map[t.QID]*a.Const
+ // The topLevelNames map is keyed by the const/status/struct/use
+ // unqualified name (ID, not QID).
+ //
+ // For `use "foo/bar"`, the name is the base name: "bar".
+ topLevelNames map[t.ID]a.Kind
+
+ // These maps are keyed by the const/status/struct name (QID).
+ consts map[t.QID]*a.Const
+ statuses map[t.QID]*a.Status
+ structs map[t.QID]*a.Struct
+
+ // These maps are keyed by the func name (QQID).
funcs map[t.QQID]*a.Func
localVars map[t.QQID]typeMap
- statuses map[t.QID]*a.Status
- structs map[t.QID]*a.Struct
-
- // useBaseNames are the base names of packages referred to by `use
- // "foo/bar"` lines. The keys are `bar`, not `"foo/bar"`.
- useBaseNames map[t.ID]struct{}
builtInSliceFuncs map[t.QQID]*a.Func
builtInTableFuncs map[t.QQID]*a.Func
@@ -243,11 +248,14 @@
baseName, err := c.tm.Insert(path.Base(filename))
if err != nil {
return fmt.Errorf("check: cannot resolve `use %s`: %v", usePath.Str(c.tm), err)
+ } else if c.topLevelNames[baseName] != 0 {
+ return &Error{
+ Err: fmt.Errorf("check: duplicate top level name %q", baseName.Str(c.tm)),
+ Filename: node.AsUse().Filename(),
+ Line: node.AsUse().Line(),
+ }
}
filename += ".wuffs"
- if _, ok := c.useBaseNames[baseName]; ok {
- return fmt.Errorf("check: duplicate `use \"etc\"` base name %q", baseName.Str(c.tm))
- }
if c.resolveUse == nil {
return fmt.Errorf("check: cannot resolve a use declaration")
@@ -291,7 +299,7 @@
}
}
}
- c.useBaseNames[baseName] = struct{}{}
+ c.topLevelNames[baseName] = a.KUse
setPlaceholderMBoundsMType(node)
return nil
}
@@ -308,6 +316,16 @@
OtherLine: other.Line(),
}
}
+ if qid[0] == 0 {
+ if c.topLevelNames[qid[1]] != 0 {
+ return &Error{
+ Err: fmt.Errorf("check: duplicate top level name %q", qid[1].Str(c.tm)),
+ Filename: n.Filename(),
+ Line: n.Line(),
+ }
+ }
+ c.topLevelNames[qid[1]] = a.KStatus
+ }
c.statuses[qid] = n
setPlaceholderMBoundsMType(n.AsNode())
@@ -326,6 +344,16 @@
OtherLine: other.Line(),
}
}
+ if qid[0] == 0 {
+ if c.topLevelNames[qid[1]] != 0 {
+ return &Error{
+ Err: fmt.Errorf("check: duplicate top level name %q", qid[1].Str(c.tm)),
+ Filename: n.Filename(),
+ Line: n.Line(),
+ }
+ }
+ c.topLevelNames[qid[1]] = a.KConst
+ }
c.consts[qid] = n
q := &checker{
@@ -398,6 +426,16 @@
OtherLine: other.Line(),
}
}
+ if qid[0] == 0 {
+ if c.topLevelNames[qid[1]] != 0 {
+ return &Error{
+ Err: fmt.Errorf("check: duplicate top level name %q", qid[1].Str(c.tm)),
+ Filename: n.Filename(),
+ Line: n.Line(),
+ }
+ }
+ c.topLevelNames[qid[1]] = a.KStruct
+ }
c.structs[qid] = n
c.unsortedStructs = append(c.unsortedStructs, n)
setPlaceholderMBoundsMType(n.AsNode())
diff --git a/lang/check/type.go b/lang/check/type.go
index a3f8824..7f53b06 100644
--- a/lang/check/type.go
+++ b/lang/check/type.go
@@ -34,6 +34,12 @@
name := o.Name()
if _, ok := q.localVars[name]; ok {
return fmt.Errorf("check: duplicate var %q", name.Str(q.tm))
+ } else if q.c.topLevelNames[name] != 0 {
+ return &Error{
+ Err: fmt.Errorf("check: var %q shadows top level name", name.Str(q.tm)),
+ Filename: o.Filename(),
+ Line: o.Line(),
+ }
}
if err := q.tcheckTypeExpr(o.XType(), 0); err != nil {
return err
@@ -366,7 +372,7 @@
return nil
}
}
- if _, ok := q.c.useBaseNames[id1]; ok {
+ if q.c.topLevelNames[id1] == a.KUse {
n.SetConstValue(zero)
n.SetMType(typeExprPackage)
return nil