Repository: goplus/gop
Pull request #2354 (2025-05-14 16:45:24)

ast/parser: implicit ident


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index d7257687..c49f48d5 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -427,7 +427,18 @@ func (x *ChanType) Pos() token.Pos { return x.Begin }
 func (x *BadExpr) End() token.Pos { return x.To }
 
 // End returns position of first character immediately after the node.
-func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
+func (x *Ident) End() token.Pos {
+	if x.Implicit() { // implicitly declared
+		return x.NamePos
+	}
+	return x.NamePos + token.Pos(len(x.Name))
+}
+
+// Implicit reports whether the identifier was implicitly declared
+func (x *Ident) Implicit() bool {
+	o := x.Obj
+	return o != nil && o.Kind >= implicitBase
+}
 
 // End returns position of first character immediately after the node.
 func (x *Ellipsis) End() token.Pos {
@@ -542,6 +553,11 @@ func (*ChanType) exprNode()      {}
 // Useful for ASTs generated by code other than the Go+ parser.
 func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
 
+// NewIdentEx creates a new Ident without position and with the given kind.
+func NewIdentEx(pos token.Pos, name string, kind ObjKind) *Ident {
+	return &Ident{pos, name, NewObj(kind, name)}
+}
+
 // IsExported reports whether name starts with an upper-case letter.
 func IsExported(name string) bool { return token.IsExported(name) }
 
diff --git a/ast/scope.go b/ast/scope.go
index 992d3802..15f25a86 100644
--- a/ast/scope.go
+++ b/ast/scope.go
@@ -154,6 +154,10 @@ const (
 	Var                // variable
 	Fun                // function or method
 	Lbl                // label
+	implicitBase
+
+	ImplicitPkg = ObjKind(implicitBase)
+	ImplicitFun = ObjKind(implicitBase + 1)
 )
 
 var objKindStrings = [...]string{
@@ -164,6 +168,9 @@ var objKindStrings = [...]string{
 	Var: "var",
 	Fun: "func",
 	Lbl: "label",
+
+	ImplicitPkg: "package",
+	ImplicitFun: "func",
 }
 
 func (kind ObjKind) String() string { return objKindStrings[kind] }
diff --git a/parser/parser.go b/parser/parser.go
index 4e33604b..4bc9fa46 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -4072,7 +4072,7 @@ func (p *parser) parseGlobalStmts(sync map[token.Token]bool, pos token.Pos, stmt
 		endPos = list[len(list)-1].End()
 	}
 	return &ast.FuncDecl{
-		Name: &ast.Ident{NamePos: pos, Name: "main"},
+		Name: ast.NewIdentEx(pos, "main", ast.ImplicitFun),
 		Doc:  doc,
 		Type: &ast.FuncType{
 			Func:   pos,
@@ -4124,7 +4124,7 @@ func (p *parser) parseFile() *ast.File {
 	} else {
 		noPkgDecl = true
 		pos = token.NoPos
-		ident = &ast.Ident{NamePos: p.file.Pos(0), Name: "main"}
+		ident = ast.NewIdentEx(p.file.Pos(0), "main", ast.ImplicitPkg)
 	}
 
 	p.openScope()


Test patch:
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 0d167e8c..f28f8df8 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -379,6 +379,18 @@ func TestNumberUnitLit(t *testing.T) {
 	p.toIdent(&ast.NumberUnitLit{})
 }
 
+func TestImplicitIdent(t *testing.T) {
+	if ast.NewIdentEx(100, "foo", ast.ImplicitPkg).End() != 100 {
+		t.Fatal("TestImplicitPkg: not 100")
+	}
+	if ast.NewIdentEx(100, "foo", ast.ImplicitFun).End() != 100 {
+		t.Fatal("TestImplicitFun: not 100")
+	}
+	if ast.NewIdentEx(100, "foo", ast.Fun).End() != 103 {
+		t.Fatal("TestFun: not 103")
+	}
+}
+
 func TestErrGlobalVarWithSyntaxError(t *testing.T) {
 	// Parse the code
 	testShadowEntry(t, `var (