Source file src/go/types/scope2_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types_test
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/token"
    11  	"reflect"
    12  	"regexp"
    13  	"strings"
    14  	"testing"
    15  
    16  	. "go/types"
    17  )
    18  
    19  // TestScopeLookupParent ensures that (*Scope).LookupParent returns
    20  // the correct result at various positions with the source.
    21  func TestScopeLookupParent(t *testing.T) {
    22  	fset := token.NewFileSet()
    23  	imports := make(testImporter)
    24  	conf := Config{Importer: imports}
    25  	var info Info
    26  	makePkg := func(path string, files ...*ast.File) {
    27  		var err error
    28  		imports[path], err = conf.Check(path, fset, files, &info)
    29  		if err != nil {
    30  			t.Fatal(err)
    31  		}
    32  	}
    33  
    34  	makePkg("lib", mustParse(fset, "package lib; var X int"))
    35  	// Each /*name=kind:line*/ comment makes the test look up the
    36  	// name at that point and checks that it resolves to a decl of
    37  	// the specified kind and line number.  "undef" means undefined.
    38  	// Note that type switch case clauses with an empty body (but for
    39  	// comments) need the ";" to ensure that the recorded scope extends
    40  	// past the comments.
    41  	mainSrc := `
    42  /*lib=pkgname:5*/ /*X=var:1*/ /*Pi=const:8*/ /*T=typename:9*/ /*Y=var:10*/ /*F=func:12*/
    43  package main
    44  
    45  import "lib"
    46  import . "lib"
    47  
    48  const Pi = 3.1415
    49  type T struct{}
    50  var Y, _ = lib.X, X
    51  
    52  func F[T *U, U any](param1, param2 int) /*param1=undef*/ (res1 /*res1=undef*/, res2 int) /*param1=var:12*/ /*res1=var:12*/ /*U=typename:12*/ {
    53  	const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
    54  	type /*t=undef*/ t /*t=typename:14*/ *t
    55  	print(Y) /*Y=var:10*/
    56  	x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
    57  	var F = /*F=func:12*/ F[*int, int] /*F=var:17*/ ; _ = F
    58  
    59  	var a []int
    60  	for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
    61  
    62  	var i interface{}
    63  	switch y := i.(type) { /*y=undef*/
    64  	case /*y=undef*/ int /*y=undef*/ : /*y=var:23*/ ;
    65  	case float32, /*y=undef*/ float64 /*y=undef*/ : /*y=var:23*/ ;
    66  	default /*y=undef*/ : /*y=var:23*/
    67  		println(y)
    68  	}
    69  	/*y=undef*/
    70  
    71          switch int := i.(type) {
    72          case /*int=typename:0*/ int /*int=typename:0*/ : /*int=var:31*/
    73          	println(int)
    74          default /*int=typename:0*/ : /*int=var:31*/ ;
    75          }
    76  
    77  	_ = param1
    78  	_ = res1
    79  	return
    80  }
    81  /*main=undef*/
    82  `
    83  
    84  	info.Uses = make(map[*ast.Ident]Object)
    85  	f := mustParse(fset, mainSrc)
    86  	makePkg("main", f)
    87  	mainScope := imports["main"].Scope()
    88  	rx := regexp.MustCompile(`^/\*(\w*)=([\w:]*)\*/$`)
    89  	for _, group := range f.Comments {
    90  		for _, comment := range group.List {
    91  			// Parse the assertion in the comment.
    92  			m := rx.FindStringSubmatch(comment.Text)
    93  			if m == nil {
    94  				t.Errorf("%s: bad comment: %s",
    95  					fset.Position(comment.Pos()), comment.Text)
    96  				continue
    97  			}
    98  			name, want := m[1], m[2]
    99  
   100  			// Look up the name in the innermost enclosing scope.
   101  			inner := mainScope.Innermost(comment.Pos())
   102  			if inner == nil {
   103  				t.Errorf("%s: at %s: can't find innermost scope",
   104  					fset.Position(comment.Pos()), comment.Text)
   105  				continue
   106  			}
   107  			got := "undef"
   108  			if _, obj := inner.LookupParent(name, comment.Pos()); obj != nil {
   109  				kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
   110  				got = fmt.Sprintf("%s:%d", kind, fset.Position(obj.Pos()).Line)
   111  			}
   112  			if got != want {
   113  				t.Errorf("%s: at %s: %s resolved to %s, want %s",
   114  					fset.Position(comment.Pos()), comment.Text, name, got, want)
   115  			}
   116  		}
   117  	}
   118  
   119  	// Check that for each referring identifier,
   120  	// a lookup of its name on the innermost
   121  	// enclosing scope returns the correct object.
   122  
   123  	for id, wantObj := range info.Uses {
   124  		inner := mainScope.Innermost(id.Pos())
   125  		if inner == nil {
   126  			t.Errorf("%s: can't find innermost scope enclosing %q",
   127  				fset.Position(id.Pos()), id.Name)
   128  			continue
   129  		}
   130  
   131  		// Exclude selectors and qualified identifiers---lexical
   132  		// refs only.  (Ideally, we'd see if the AST parent is a
   133  		// SelectorExpr, but that requires PathEnclosingInterval
   134  		// from golang.org/x/tools/go/ast/astutil.)
   135  		if id.Name == "X" {
   136  			continue
   137  		}
   138  
   139  		_, gotObj := inner.LookupParent(id.Name, id.Pos())
   140  		if gotObj != wantObj {
   141  			// Print the scope tree of mainScope in case of error.
   142  			var printScopeTree func(indent string, s *Scope)
   143  			printScopeTree = func(indent string, s *Scope) {
   144  				t.Logf("%sscope %s %v-%v = %v",
   145  					indent,
   146  					ScopeComment(s),
   147  					s.Pos(),
   148  					s.End(),
   149  					s.Names())
   150  				for i := range s.NumChildren() {
   151  					printScopeTree(indent+"  ", s.Child(i))
   152  				}
   153  			}
   154  			printScopeTree("", mainScope)
   155  
   156  			t.Errorf("%s: Scope(%s).LookupParent(%s@%v) got %v, want %v [scopePos=%v]",
   157  				fset.Position(id.Pos()),
   158  				ScopeComment(inner),
   159  				id.Name,
   160  				id.Pos(),
   161  				gotObj,
   162  				wantObj,
   163  				ObjectScopePos(wantObj))
   164  			continue
   165  		}
   166  	}
   167  }
   168  

View as plain text