@@ -10,6 +10,8 @@ import (
10
10
"go/ast"
11
11
"go/parser"
12
12
"go/token"
13
+ "reflect"
14
+ "regexp"
13
15
"runtime"
14
16
"strings"
15
17
"testing"
@@ -863,7 +865,7 @@ func TestIssue8518(t *testing.T) {
863
865
}
864
866
865
867
const libSrc = `
866
- package a
868
+ package a
867
869
import "missing"
868
870
const C1 = foo
869
871
const C2 = missing.C
@@ -953,3 +955,105 @@ func sameSlice(a, b []int) bool {
953
955
}
954
956
return true
955
957
}
958
+
959
+ // TestScopeLookupParent ensures that (*Scope).LookupParent returns
960
+ // the correct result at various positions with the source.
961
+ func TestScopeLookupParent (t * testing.T ) {
962
+ fset := token .NewFileSet ()
963
+ conf := Config {
964
+ Packages : make (map [string ]* Package ),
965
+ Import : func (imports map [string ]* Package , path string ) (* Package , error ) {
966
+ return imports [path ], nil
967
+ },
968
+ }
969
+ mustParse := func (src string ) * ast.File {
970
+ f , err := parser .ParseFile (fset , "dummy.go" , src , parser .ParseComments )
971
+ if err != nil {
972
+ t .Fatal (err )
973
+ }
974
+ return f
975
+ }
976
+ var info Info
977
+ makePkg := func (path string , files ... * ast.File ) {
978
+ conf .Packages [path ], _ = conf .Check (path , fset , files , & info )
979
+ }
980
+
981
+ makePkg ("lib" , mustParse ("package lib; var X int" ))
982
+ // Each /*name=kind:line*/ comment makes the test look up the
983
+ // name at that point and checks that it resolves to a decl of
984
+ // the specified kind and line number. "undef" means undefined.
985
+ mainSrc := `
986
+ package main
987
+ import "lib"
988
+ var Y = lib.X
989
+ func f() {
990
+ print(Y) /*Y=var:4*/
991
+ z /*z=undef*/ := /*z=undef*/ 1 /*z=var:7*/
992
+ print(z)
993
+ /*f=func:5*/ /*lib=pkgname:3*/
994
+ type /*T=undef*/ T /*T=typename:10*/ *T
995
+ }
996
+ `
997
+ info .Uses = make (map [* ast.Ident ]Object )
998
+ f := mustParse (mainSrc )
999
+ makePkg ("main" , f )
1000
+ mainScope := conf .Packages ["main" ].Scope ()
1001
+ rx := regexp .MustCompile (`^/\*(\w*)=([\w:]*)\*/$` )
1002
+ for _ , group := range f .Comments {
1003
+ for _ , comment := range group .List {
1004
+ // Parse the assertion in the comment.
1005
+ m := rx .FindStringSubmatch (comment .Text )
1006
+ if m == nil {
1007
+ t .Errorf ("%s: bad comment: %s" ,
1008
+ fset .Position (comment .Pos ()), comment .Text )
1009
+ continue
1010
+ }
1011
+ name , want := m [1 ], m [2 ]
1012
+
1013
+ // Look up the name in the innermost enclosing scope.
1014
+ inner := mainScope .Innermost (comment .Pos ())
1015
+ if inner == nil {
1016
+ t .Errorf ("%s: at %s: can't find innermost scope" ,
1017
+ fset .Position (comment .Pos ()), comment .Text )
1018
+ continue
1019
+ }
1020
+ got := "undef"
1021
+ if _ , obj := inner .LookupParent (name , comment .Pos ()); obj != nil {
1022
+ kind := strings .ToLower (strings .TrimPrefix (reflect .TypeOf (obj ).String (), "*types." ))
1023
+ got = fmt .Sprintf ("%s:%d" , kind , fset .Position (obj .Pos ()).Line )
1024
+ }
1025
+ if got != want {
1026
+ t .Errorf ("%s: at %s: %s resolved to %s, want %s" ,
1027
+ fset .Position (comment .Pos ()), comment .Text , name , got , want )
1028
+ }
1029
+ }
1030
+ }
1031
+
1032
+ // Check that for each referring identifier,
1033
+ // a lookup of its name on the innermost
1034
+ // enclosing scope returns the correct object.
1035
+
1036
+ for id , wantObj := range info .Uses {
1037
+ inner := mainScope .Innermost (id .Pos ())
1038
+ if inner == nil {
1039
+ t .Errorf ("%s: can't find innermost scope enclosing %q" ,
1040
+ fset .Position (id .Pos ()), id .Name )
1041
+ continue
1042
+ }
1043
+
1044
+ // Exclude selectors and qualified identifiers---lexical
1045
+ // refs only. (Ideally, we'd see if the AST parent is a
1046
+ // SelectorExpr, but that requires PathEnclosingInterval
1047
+ // from golang.org/x/tools/go/ast/astutil.)
1048
+ if id .Name == "X" {
1049
+ continue
1050
+ }
1051
+
1052
+ _ , gotObj := inner .LookupParent (id .Name , id .Pos ())
1053
+ if gotObj != wantObj {
1054
+ t .Errorf ("%s: got %v, want %v" ,
1055
+ fset .Position (id .Pos ()), gotObj , wantObj )
1056
+ continue
1057
+ }
1058
+ }
1059
+ }
0 commit comments