Source file
    src/go/parser/resolver_test.go
  
  
     1  
     2  
     3  
     4  
     5  package parser
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/scanner"
    11  	"go/token"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    28  
    29  
    30  func TestResolution(t *testing.T) {
    31  	dir := filepath.Join("testdata", "resolution")
    32  	fis, err := os.ReadDir(dir)
    33  	if err != nil {
    34  		t.Fatal(err)
    35  	}
    36  
    37  	for _, fi := range fis {
    38  		t.Run(fi.Name(), func(t *testing.T) {
    39  			fset := token.NewFileSet()
    40  			path := filepath.Join(dir, fi.Name())
    41  			src := readFile(path) 
    42  			var mode Mode
    43  			file, err := ParseFile(fset, path, src, mode)
    44  			if err != nil {
    45  				t.Fatal(err)
    46  			}
    47  
    48  			
    49  			
    50  
    51  			handle := fset.File(file.Package)
    52  			fromParser := declsFromParser(file)
    53  			fromComments := declsFromComments(handle, src)
    54  
    55  			pos := func(pos token.Pos) token.Position {
    56  				p := handle.Position(pos)
    57  				
    58  				
    59  				p.Filename = ""
    60  				return p
    61  			}
    62  			for k, want := range fromComments {
    63  				if got := fromParser[k]; got != want {
    64  					t.Errorf("%s resolved to %s, want %s", pos(k), pos(got), pos(want))
    65  				}
    66  				delete(fromParser, k)
    67  			}
    68  			
    69  			for k, got := range fromParser {
    70  				t.Errorf("%s resolved to %s, want no object", pos(k), pos(got))
    71  			}
    72  		})
    73  	}
    74  }
    75  
    76  
    77  
    78  func declsFromParser(file *ast.File) map[token.Pos]token.Pos {
    79  	objmap := map[token.Pos]token.Pos{}
    80  	ast.Inspect(file, func(node ast.Node) bool {
    81  		
    82  		if ident, _ := node.(*ast.Ident); ident != nil && ident.Obj != nil && ident.Name != "_" {
    83  			objmap[ident.Pos()] = ident.Obj.Pos()
    84  		}
    85  		return true
    86  	})
    87  	return objmap
    88  }
    89  
    90  
    91  
    92  
    93  func declsFromComments(handle *token.File, src []byte) map[token.Pos]token.Pos {
    94  	decls, uses := positionMarkers(handle, src)
    95  
    96  	objmap := make(map[token.Pos]token.Pos)
    97  	
    98  	for name, posns := range uses {
    99  		declpos, ok := decls[name]
   100  		if !ok {
   101  			panic(fmt.Sprintf("missing declaration for %s", name))
   102  		}
   103  		for _, pos := range posns {
   104  			objmap[pos] = declpos
   105  		}
   106  	}
   107  	return objmap
   108  }
   109  
   110  
   111  
   112  
   113  
   114  func positionMarkers(handle *token.File, src []byte) (decls map[string]token.Pos, uses map[string][]token.Pos) {
   115  	var s scanner.Scanner
   116  	s.Init(handle, src, nil, scanner.ScanComments)
   117  	decls = make(map[string]token.Pos)
   118  	uses = make(map[string][]token.Pos)
   119  	var prev token.Pos 
   120  
   121  scanFile:
   122  	for {
   123  		pos, tok, lit := s.Scan()
   124  		switch tok {
   125  		case token.EOF:
   126  			break scanFile
   127  		case token.COMMENT:
   128  			name, decl, use := annotatedObj(lit)
   129  			if len(name) > 0 {
   130  				if decl {
   131  					if _, ok := decls[name]; ok {
   132  						panic(fmt.Sprintf("duplicate declaration markers for %s", name))
   133  					}
   134  					decls[name] = prev
   135  				}
   136  				if use {
   137  					uses[name] = append(uses[name], prev)
   138  				}
   139  			}
   140  		case token.SEMICOLON:
   141  			
   142  			if lit == "\n" {
   143  				continue scanFile
   144  			}
   145  			fallthrough
   146  		default:
   147  			prev = pos
   148  		}
   149  	}
   150  	return decls, uses
   151  }
   152  
   153  func annotatedObj(lit string) (name string, decl, use bool) {
   154  	if lit[1] == '*' {
   155  		lit = lit[:len(lit)-2] 
   156  	}
   157  	lit = strings.TrimSpace(lit[2:])
   158  
   159  scanLit:
   160  	for idx, r := range lit {
   161  		switch r {
   162  		case '=':
   163  			decl = true
   164  		case '@':
   165  			use = true
   166  		default:
   167  			name = lit[idx:]
   168  			break scanLit
   169  		}
   170  	}
   171  	return
   172  }
   173  
View as plain text