golang.org/x/tools/gopls@v0.15.3/internal/golang/semtok.go (about) 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 golang 6 7 // This file defines the Semantic Tokens operation for Go source. 8 9 import ( 10 "bytes" 11 "context" 12 "errors" 13 "fmt" 14 "go/ast" 15 "go/token" 16 "go/types" 17 "log" 18 "path/filepath" 19 "strings" 20 "time" 21 22 "golang.org/x/tools/gopls/internal/cache" 23 "golang.org/x/tools/gopls/internal/cache/metadata" 24 "golang.org/x/tools/gopls/internal/file" 25 "golang.org/x/tools/gopls/internal/protocol" 26 "golang.org/x/tools/gopls/internal/protocol/semtok" 27 "golang.org/x/tools/gopls/internal/util/safetoken" 28 "golang.org/x/tools/internal/event" 29 ) 30 31 // to control comprehensive logging of decisions (gopls semtok foo.go > /dev/null shows log output) 32 // semDebug should NEVER be true in checked-in code 33 const semDebug = false 34 35 // The LSP says that errors for the semantic token requests should only be returned 36 // for exceptions (a word not otherwise defined). This code treats a too-large file 37 // as an exception. On parse errors, the code does what it can. 38 39 // reject full semantic token requests for large files 40 const maxFullFileSize int = 100000 41 42 func SemanticTokens(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng *protocol.Range) (*protocol.SemanticTokens, error) { 43 pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) 44 if err != nil { 45 return nil, err 46 } 47 48 // Select range. 49 var start, end token.Pos 50 if rng != nil { 51 var err error 52 start, end, err = pgf.RangePos(*rng) 53 if err != nil { 54 return nil, err // e.g. invalid range 55 } 56 } else { 57 tok := pgf.Tok 58 start, end = tok.Pos(0), tok.Pos(tok.Size()) // entire file 59 } 60 if int(end-start) > maxFullFileSize { 61 err := fmt.Errorf("semantic tokens: range %s too large (%d > %d)", 62 fh.URI().Path(), end-start, maxFullFileSize) 63 return nil, err 64 } 65 66 tv := &tokenVisitor{ 67 ctx: ctx, 68 metadataSource: snapshot, 69 pgf: pgf, 70 start: start, 71 end: end, 72 ti: pkg.GetTypesInfo(), 73 pkg: pkg, 74 fset: pkg.FileSet(), 75 } 76 tv.visit() 77 return &protocol.SemanticTokens{ 78 Data: semtok.Encode( 79 tv.items, 80 snapshot.Options().NoSemanticString, 81 snapshot.Options().NoSemanticNumber, 82 snapshot.Options().SemanticTypes, 83 snapshot.Options().SemanticMods), 84 // For delta requests, but we've never seen any. 85 ResultID: time.Now().String(), 86 }, nil 87 } 88 89 type tokenVisitor struct { 90 items []semtok.Token 91 ctx context.Context // for event logging 92 // metadataSource is used to resolve imports 93 metadataSource metadata.Source 94 pgf *ParsedGoFile 95 start, end token.Pos // range of interest 96 ti *types.Info 97 pkg *cache.Package 98 fset *token.FileSet 99 // path from the root of the parse tree, used for debugging 100 stack []ast.Node 101 } 102 103 func (tv *tokenVisitor) visit() { 104 f := tv.pgf.File 105 // may not be in range, but harmless 106 tv.token(f.Package, len("package"), semtok.TokKeyword, nil) 107 tv.token(f.Name.NamePos, len(f.Name.Name), semtok.TokNamespace, nil) 108 inspect := func(n ast.Node) bool { 109 return tv.inspector(n) 110 } 111 for _, d := range f.Decls { 112 // only look at the decls that overlap the range 113 start, end := d.Pos(), d.End() 114 if end <= tv.start || start >= tv.end { 115 continue 116 } 117 ast.Inspect(d, inspect) 118 } 119 for _, cg := range f.Comments { 120 for _, c := range cg.List { 121 if strings.HasPrefix(c.Text, "//go:") { 122 tv.godirective(c) 123 continue 124 } 125 if !strings.Contains(c.Text, "\n") { 126 tv.token(c.Pos(), len(c.Text), semtok.TokComment, nil) 127 continue 128 } 129 tv.multiline(c.Pos(), c.End(), c.Text, semtok.TokComment) 130 } 131 } 132 } 133 134 func (tv *tokenVisitor) token(start token.Pos, leng int, typ semtok.TokenType, mods []string) { 135 if leng <= 0 { 136 return // vscode doesn't like 0-length Tokens 137 } 138 if !start.IsValid() { 139 // This is not worth reporting. TODO(pjw): does it still happen? 140 return 141 } 142 if start >= tv.end || start+token.Pos(leng) <= tv.start { 143 return 144 } 145 // want a line and column from start (in LSP coordinates). Ignore line directives. 146 lspRange, err := tv.pgf.PosRange(start, start+token.Pos(leng)) 147 if err != nil { 148 event.Error(tv.ctx, "failed to convert to range", err) 149 return 150 } 151 if lspRange.End.Line != lspRange.Start.Line { 152 // this happens if users are typing at the end of the file, but report nothing 153 return 154 } 155 tv.items = append(tv.items, semtok.Token{ 156 Line: lspRange.Start.Line, 157 Start: lspRange.Start.Character, 158 Len: lspRange.End.Character - lspRange.Start.Character, // all on one line 159 Type: typ, 160 Modifiers: mods, 161 }) 162 } 163 164 // convert the stack to a string, for debugging 165 func (tv *tokenVisitor) strStack() string { 166 msg := []string{"["} 167 for i := len(tv.stack) - 1; i >= 0; i-- { 168 s := tv.stack[i] 169 msg = append(msg, fmt.Sprintf("%T", s)[5:]) 170 } 171 if len(tv.stack) > 0 { 172 loc := tv.stack[len(tv.stack)-1].Pos() 173 if _, err := safetoken.Offset(tv.pgf.Tok, loc); err != nil { 174 msg = append(msg, fmt.Sprintf("invalid position %v for %s", loc, tv.pgf.URI)) 175 } else { 176 add := safetoken.Position(tv.pgf.Tok, loc) 177 nm := filepath.Base(add.Filename) 178 msg = append(msg, fmt.Sprintf("(%s:%d,col:%d)", nm, add.Line, add.Column)) 179 } 180 } 181 msg = append(msg, "]") 182 return strings.Join(msg, " ") 183 } 184 185 // find the line in the source 186 func (tv *tokenVisitor) srcLine(x ast.Node) string { 187 file := tv.pgf.Tok 188 line := safetoken.Line(file, x.Pos()) 189 start, err := safetoken.Offset(file, file.LineStart(line)) 190 if err != nil { 191 return "" 192 } 193 end := start 194 for ; end < len(tv.pgf.Src) && tv.pgf.Src[end] != '\n'; end++ { 195 196 } 197 ans := tv.pgf.Src[start:end] 198 return string(ans) 199 } 200 201 func (tv *tokenVisitor) inspector(n ast.Node) bool { 202 pop := func() { 203 tv.stack = tv.stack[:len(tv.stack)-1] 204 } 205 if n == nil { 206 pop() 207 return true 208 } 209 tv.stack = append(tv.stack, n) 210 switch x := n.(type) { 211 case *ast.ArrayType: 212 case *ast.AssignStmt: 213 tv.token(x.TokPos, len(x.Tok.String()), semtok.TokOperator, nil) 214 case *ast.BasicLit: 215 if strings.Contains(x.Value, "\n") { 216 // has to be a string. 217 tv.multiline(x.Pos(), x.End(), x.Value, semtok.TokString) 218 break 219 } 220 ln := len(x.Value) 221 what := semtok.TokNumber 222 if x.Kind == token.STRING { 223 what = semtok.TokString 224 } 225 tv.token(x.Pos(), ln, what, nil) 226 case *ast.BinaryExpr: 227 tv.token(x.OpPos, len(x.Op.String()), semtok.TokOperator, nil) 228 case *ast.BlockStmt: 229 case *ast.BranchStmt: 230 tv.token(x.TokPos, len(x.Tok.String()), semtok.TokKeyword, nil) 231 // There's no semantic encoding for labels 232 case *ast.CallExpr: 233 if x.Ellipsis != token.NoPos { 234 tv.token(x.Ellipsis, len("..."), semtok.TokOperator, nil) 235 } 236 case *ast.CaseClause: 237 iam := "case" 238 if x.List == nil { 239 iam = "default" 240 } 241 tv.token(x.Case, len(iam), semtok.TokKeyword, nil) 242 case *ast.ChanType: 243 // chan | chan <- | <- chan 244 switch { 245 case x.Arrow == token.NoPos: 246 tv.token(x.Begin, len("chan"), semtok.TokKeyword, nil) 247 case x.Arrow == x.Begin: 248 tv.token(x.Arrow, 2, semtok.TokOperator, nil) 249 pos := tv.findKeyword("chan", x.Begin+2, x.Value.Pos()) 250 tv.token(pos, len("chan"), semtok.TokKeyword, nil) 251 case x.Arrow != x.Begin: 252 tv.token(x.Begin, len("chan"), semtok.TokKeyword, nil) 253 tv.token(x.Arrow, 2, semtok.TokOperator, nil) 254 } 255 case *ast.CommClause: 256 iam := len("case") 257 if x.Comm == nil { 258 iam = len("default") 259 } 260 tv.token(x.Case, iam, semtok.TokKeyword, nil) 261 case *ast.CompositeLit: 262 case *ast.DeclStmt: 263 case *ast.DeferStmt: 264 tv.token(x.Defer, len("defer"), semtok.TokKeyword, nil) 265 case *ast.Ellipsis: 266 tv.token(x.Ellipsis, len("..."), semtok.TokOperator, nil) 267 case *ast.EmptyStmt: 268 case *ast.ExprStmt: 269 case *ast.Field: 270 case *ast.FieldList: 271 case *ast.ForStmt: 272 tv.token(x.For, len("for"), semtok.TokKeyword, nil) 273 case *ast.FuncDecl: 274 case *ast.FuncLit: 275 case *ast.FuncType: 276 if x.Func != token.NoPos { 277 tv.token(x.Func, len("func"), semtok.TokKeyword, nil) 278 } 279 case *ast.GenDecl: 280 tv.token(x.TokPos, len(x.Tok.String()), semtok.TokKeyword, nil) 281 case *ast.GoStmt: 282 tv.token(x.Go, len("go"), semtok.TokKeyword, nil) 283 case *ast.Ident: 284 tv.ident(x) 285 case *ast.IfStmt: 286 tv.token(x.If, len("if"), semtok.TokKeyword, nil) 287 if x.Else != nil { 288 // x.Body.End() or x.Body.End()+1, not that it matters 289 pos := tv.findKeyword("else", x.Body.End(), x.Else.Pos()) 290 tv.token(pos, len("else"), semtok.TokKeyword, nil) 291 } 292 case *ast.ImportSpec: 293 tv.importSpec(x) 294 pop() 295 return false 296 case *ast.IncDecStmt: 297 tv.token(x.TokPos, len(x.Tok.String()), semtok.TokOperator, nil) 298 case *ast.IndexExpr: 299 case *ast.IndexListExpr: 300 case *ast.InterfaceType: 301 tv.token(x.Interface, len("interface"), semtok.TokKeyword, nil) 302 case *ast.KeyValueExpr: 303 case *ast.LabeledStmt: 304 case *ast.MapType: 305 tv.token(x.Map, len("map"), semtok.TokKeyword, nil) 306 case *ast.ParenExpr: 307 case *ast.RangeStmt: 308 tv.token(x.For, len("for"), semtok.TokKeyword, nil) 309 // x.TokPos == token.NoPos is legal (for range foo {}) 310 offset := x.TokPos 311 if offset == token.NoPos { 312 offset = x.For 313 } 314 pos := tv.findKeyword("range", offset, x.X.Pos()) 315 tv.token(pos, len("range"), semtok.TokKeyword, nil) 316 case *ast.ReturnStmt: 317 tv.token(x.Return, len("return"), semtok.TokKeyword, nil) 318 case *ast.SelectStmt: 319 tv.token(x.Select, len("select"), semtok.TokKeyword, nil) 320 case *ast.SelectorExpr: 321 case *ast.SendStmt: 322 tv.token(x.Arrow, len("<-"), semtok.TokOperator, nil) 323 case *ast.SliceExpr: 324 case *ast.StarExpr: 325 tv.token(x.Star, len("*"), semtok.TokOperator, nil) 326 case *ast.StructType: 327 tv.token(x.Struct, len("struct"), semtok.TokKeyword, nil) 328 case *ast.SwitchStmt: 329 tv.token(x.Switch, len("switch"), semtok.TokKeyword, nil) 330 case *ast.TypeAssertExpr: 331 if x.Type == nil { 332 pos := tv.findKeyword("type", x.Lparen, x.Rparen) 333 tv.token(pos, len("type"), semtok.TokKeyword, nil) 334 } 335 case *ast.TypeSpec: 336 case *ast.TypeSwitchStmt: 337 tv.token(x.Switch, len("switch"), semtok.TokKeyword, nil) 338 case *ast.UnaryExpr: 339 tv.token(x.OpPos, len(x.Op.String()), semtok.TokOperator, nil) 340 case *ast.ValueSpec: 341 // things only seen with parsing or type errors, so ignore them 342 case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt: 343 return true 344 // not going to see these 345 case *ast.File, *ast.Package: 346 tv.unexpected(fmt.Sprintf("implement %T %s", x, safetoken.Position(tv.pgf.Tok, x.Pos()))) 347 // other things we knowingly ignore 348 case *ast.Comment, *ast.CommentGroup: 349 pop() 350 return false 351 default: 352 tv.unexpected(fmt.Sprintf("failed to implement %T", x)) 353 } 354 return true 355 } 356 357 func (tv *tokenVisitor) ident(x *ast.Ident) { 358 if tv.ti == nil { 359 what, mods := tv.unkIdent(x) 360 if what != "" { 361 tv.token(x.Pos(), len(x.String()), what, mods) 362 } 363 if semDebug { 364 log.Printf(" nil %s/nil/nil %q %v %s", x.String(), what, mods, tv.strStack()) 365 } 366 return 367 } 368 def := tv.ti.Defs[x] 369 if def != nil { 370 what, mods := tv.definitionFor(x, def) 371 if what != "" { 372 tv.token(x.Pos(), len(x.String()), what, mods) 373 } 374 if semDebug { 375 log.Printf(" for %s/%T/%T got %s %v (%s)", x.String(), def, def.Type(), what, mods, tv.strStack()) 376 } 377 return 378 } 379 use := tv.ti.Uses[x] 380 tok := func(pos token.Pos, lng int, tok semtok.TokenType, mods []string) { 381 tv.token(pos, lng, tok, mods) 382 q := "nil" 383 if use != nil { 384 q = fmt.Sprintf("%T", use.Type()) 385 } 386 if semDebug { 387 log.Printf(" use %s/%T/%s got %s %v (%s)", x.String(), use, q, tok, mods, tv.strStack()) 388 } 389 } 390 391 switch y := use.(type) { 392 case nil: 393 what, mods := tv.unkIdent(x) 394 if what != "" { 395 tok(x.Pos(), len(x.String()), what, mods) 396 } else if semDebug { 397 // tok() wasn't called, so didn't log 398 log.Printf(" nil %s/%T/nil %q %v (%s)", x.String(), use, what, mods, tv.strStack()) 399 } 400 return 401 case *types.Builtin: 402 tok(x.NamePos, len(x.Name), semtok.TokFunction, []string{"defaultLibrary"}) 403 case *types.Const: 404 mods := []string{"readonly"} 405 tt := y.Type() 406 if _, ok := tt.(*types.Basic); ok { 407 tok(x.Pos(), len(x.String()), semtok.TokVariable, mods) 408 break 409 } 410 if ttx, ok := tt.(*types.Named); ok { 411 if x.String() == "iota" { 412 tv.unexpected(fmt.Sprintf("iota:%T", ttx)) 413 } 414 if _, ok := ttx.Underlying().(*types.Basic); ok { 415 tok(x.Pos(), len(x.String()), semtok.TokVariable, mods) 416 break 417 } 418 tv.unexpected(fmt.Sprintf("%q/%T", x.String(), tt)) 419 } 420 // can this happen? Don't think so 421 tv.unexpected(fmt.Sprintf("%s %T %#v", x.String(), tt, tt)) 422 case *types.Func: 423 tok(x.Pos(), len(x.Name), semtok.TokFunction, nil) 424 case *types.Label: 425 // nothing to map it to 426 case *types.Nil: 427 // nil is a predeclared identifier 428 tok(x.Pos(), len("nil"), semtok.TokVariable, []string{"readonly", "defaultLibrary"}) 429 case *types.PkgName: 430 tok(x.Pos(), len(x.Name), semtok.TokNamespace, nil) 431 case *types.TypeName: // could be a TokTypeParam 432 var mods []string 433 if _, ok := y.Type().(*types.Basic); ok { 434 mods = []string{"defaultLibrary"} 435 } else if _, ok := y.Type().(*types.TypeParam); ok { 436 tok(x.Pos(), len(x.String()), semtok.TokTypeParam, mods) 437 break 438 } 439 tok(x.Pos(), len(x.String()), semtok.TokType, mods) 440 case *types.Var: 441 if isSignature(y) { 442 tok(x.Pos(), len(x.Name), semtok.TokFunction, nil) 443 } else if tv.isParam(use.Pos()) { 444 // variable, unless use.pos is the pos of a Field in an ancestor FuncDecl 445 // or FuncLit and then it's a parameter 446 tok(x.Pos(), len(x.Name), semtok.TokParameter, nil) 447 } else { 448 tok(x.Pos(), len(x.Name), semtok.TokVariable, nil) 449 } 450 451 default: 452 // can't happen 453 if use == nil { 454 msg := fmt.Sprintf("%#v %#v %#v", x, tv.ti.Defs[x], tv.ti.Uses[x]) 455 tv.unexpected(msg) 456 } 457 if use.Type() != nil { 458 tv.unexpected(fmt.Sprintf("%s %T/%T,%#v", x.String(), use, use.Type(), use)) 459 } else { 460 tv.unexpected(fmt.Sprintf("%s %T", x.String(), use)) 461 } 462 } 463 } 464 465 func (tv *tokenVisitor) isParam(pos token.Pos) bool { 466 for i := len(tv.stack) - 1; i >= 0; i-- { 467 switch n := tv.stack[i].(type) { 468 case *ast.FuncDecl: 469 for _, f := range n.Type.Params.List { 470 for _, id := range f.Names { 471 if id.Pos() == pos { 472 return true 473 } 474 } 475 } 476 case *ast.FuncLit: 477 for _, f := range n.Type.Params.List { 478 for _, id := range f.Names { 479 if id.Pos() == pos { 480 return true 481 } 482 } 483 } 484 } 485 } 486 return false 487 } 488 489 func isSignature(use types.Object) bool { 490 if _, ok := use.(*types.Var); !ok { 491 return false 492 } 493 v := use.Type() 494 if v == nil { 495 return false 496 } 497 if _, ok := v.(*types.Signature); ok { 498 return true 499 } 500 return false 501 } 502 503 // both tv.ti.Defs and tv.ti.Uses are nil. use the parse stack. 504 // a lot of these only happen when the package doesn't compile 505 // but in that case it is all best-effort from the parse tree 506 func (tv *tokenVisitor) unkIdent(x *ast.Ident) (semtok.TokenType, []string) { 507 def := []string{"definition"} 508 n := len(tv.stack) - 2 // parent of Ident 509 if n < 0 { 510 tv.unexpected("no stack?") 511 return "", nil 512 } 513 switch nd := tv.stack[n].(type) { 514 case *ast.BinaryExpr, *ast.UnaryExpr, *ast.ParenExpr, *ast.StarExpr, 515 *ast.IncDecStmt, *ast.SliceExpr, *ast.ExprStmt, *ast.IndexExpr, 516 *ast.ReturnStmt, *ast.ChanType, *ast.SendStmt, 517 *ast.ForStmt, // possibly incomplete 518 *ast.IfStmt, /* condition */ 519 *ast.KeyValueExpr, // either key or value 520 *ast.IndexListExpr: 521 return semtok.TokVariable, nil 522 case *ast.Ellipsis: 523 return semtok.TokType, nil 524 case *ast.CaseClause: 525 if n-2 >= 0 { 526 if _, ok := tv.stack[n-2].(*ast.TypeSwitchStmt); ok { 527 return semtok.TokType, nil 528 } 529 } 530 return semtok.TokVariable, nil 531 case *ast.ArrayType: 532 if x == nd.Len { 533 // or maybe a Type Param, but we can't just from the parse tree 534 return semtok.TokVariable, nil 535 } else { 536 return semtok.TokType, nil 537 } 538 case *ast.MapType: 539 return semtok.TokType, nil 540 case *ast.CallExpr: 541 if x == nd.Fun { 542 return semtok.TokFunction, nil 543 } 544 return semtok.TokVariable, nil 545 case *ast.SwitchStmt: 546 return semtok.TokVariable, nil 547 case *ast.TypeAssertExpr: 548 if x == nd.X { 549 return semtok.TokVariable, nil 550 } else if x == nd.Type { 551 return semtok.TokType, nil 552 } 553 case *ast.ValueSpec: 554 for _, p := range nd.Names { 555 if p == x { 556 return semtok.TokVariable, def 557 } 558 } 559 for _, p := range nd.Values { 560 if p == x { 561 return semtok.TokVariable, nil 562 } 563 } 564 return semtok.TokType, nil 565 case *ast.SelectorExpr: // e.ti.Selections[nd] is nil, so no help 566 if n-1 >= 0 { 567 if ce, ok := tv.stack[n-1].(*ast.CallExpr); ok { 568 // ... CallExpr SelectorExpr Ident (_.x()) 569 if ce.Fun == nd && nd.Sel == x { 570 return semtok.TokFunction, nil 571 } 572 } 573 } 574 return semtok.TokVariable, nil 575 case *ast.AssignStmt: 576 for _, p := range nd.Lhs { 577 // x := ..., or x = ... 578 if p == x { 579 if nd.Tok != token.DEFINE { 580 def = nil 581 } 582 return semtok.TokVariable, def // '_' in _ = ... 583 } 584 } 585 // RHS, = x 586 return semtok.TokVariable, nil 587 case *ast.TypeSpec: // it's a type if it is either the Name or the Type 588 if x == nd.Type { 589 def = nil 590 } 591 return semtok.TokType, def 592 case *ast.Field: 593 // ident could be type in a field, or a method in an interface type, or a variable 594 if x == nd.Type { 595 return semtok.TokType, nil 596 } 597 if n-2 >= 0 { 598 _, okit := tv.stack[n-2].(*ast.InterfaceType) 599 _, okfl := tv.stack[n-1].(*ast.FieldList) 600 if okit && okfl { 601 return semtok.TokMethod, def 602 } 603 } 604 return semtok.TokVariable, nil 605 case *ast.LabeledStmt, *ast.BranchStmt: 606 // nothing to report 607 case *ast.CompositeLit: 608 if nd.Type == x { 609 return semtok.TokType, nil 610 } 611 return semtok.TokVariable, nil 612 case *ast.RangeStmt: 613 if nd.Tok != token.DEFINE { 614 def = nil 615 } 616 return semtok.TokVariable, def 617 case *ast.FuncDecl: 618 return semtok.TokFunction, def 619 default: 620 msg := fmt.Sprintf("%T undexpected: %s %s%q", nd, x.Name, tv.strStack(), tv.srcLine(x)) 621 tv.unexpected(msg) 622 } 623 return "", nil 624 } 625 626 func isDeprecated(n *ast.CommentGroup) bool { 627 if n == nil { 628 return false 629 } 630 for _, c := range n.List { 631 if strings.HasPrefix(c.Text, "// Deprecated") { 632 return true 633 } 634 } 635 return false 636 } 637 638 func (tv *tokenVisitor) definitionFor(x *ast.Ident, def types.Object) (semtok.TokenType, []string) { 639 // PJW: def == types.Label? probably a nothing 640 // PJW: look into replacing these syntactic tests with types more generally 641 mods := []string{"definition"} 642 for i := len(tv.stack) - 1; i >= 0; i-- { 643 s := tv.stack[i] 644 switch y := s.(type) { 645 case *ast.AssignStmt, *ast.RangeStmt: 646 if x.Name == "_" { 647 return "", nil // not really a variable 648 } 649 return semtok.TokVariable, mods 650 case *ast.GenDecl: 651 if isDeprecated(y.Doc) { 652 mods = append(mods, "deprecated") 653 } 654 if y.Tok == token.CONST { 655 mods = append(mods, "readonly") 656 } 657 return semtok.TokVariable, mods 658 case *ast.FuncDecl: 659 // If x is immediately under a FuncDecl, it is a function or method 660 if i == len(tv.stack)-2 { 661 if isDeprecated(y.Doc) { 662 mods = append(mods, "deprecated") 663 } 664 if y.Recv != nil { 665 return semtok.TokMethod, mods 666 } 667 return semtok.TokFunction, mods 668 } 669 // if x < ... < FieldList < FuncDecl, this is the receiver, a variable 670 // PJW: maybe not. it might be a typeparameter in the type of the receiver 671 if _, ok := tv.stack[i+1].(*ast.FieldList); ok { 672 if _, ok := def.(*types.TypeName); ok { 673 return semtok.TokTypeParam, mods 674 } 675 return semtok.TokVariable, nil 676 } 677 // if x < ... < FieldList < FuncType < FuncDecl, this is a param 678 return semtok.TokParameter, mods 679 case *ast.FuncType: // is it in the TypeParams? 680 if isTypeParam(x, y) { 681 return semtok.TokTypeParam, mods 682 } 683 return semtok.TokParameter, mods 684 case *ast.InterfaceType: 685 return semtok.TokMethod, mods 686 case *ast.TypeSpec: 687 // GenDecl/Typespec/FuncType/FieldList/Field/Ident 688 // (type A func(b uint64)) (err error) 689 // b and err should not be semtok.TokType, but semtok.TokVariable 690 // and in GenDecl/TpeSpec/StructType/FieldList/Field/Ident 691 // (type A struct{b uint64} 692 // but on type B struct{C}), C is a type, but is not being defined. 693 // GenDecl/TypeSpec/FieldList/Field/Ident is a typeParam 694 if _, ok := tv.stack[i+1].(*ast.FieldList); ok { 695 return semtok.TokTypeParam, mods 696 } 697 fldm := tv.stack[len(tv.stack)-2] 698 if fld, ok := fldm.(*ast.Field); ok { 699 // if len(fld.names) == 0 this is a semtok.TokType, being used 700 if len(fld.Names) == 0 { 701 return semtok.TokType, nil 702 } 703 return semtok.TokVariable, mods 704 } 705 return semtok.TokType, mods 706 } 707 } 708 // can't happen 709 msg := fmt.Sprintf("failed to find the decl for %s", safetoken.Position(tv.pgf.Tok, x.Pos())) 710 tv.unexpected(msg) 711 return "", []string{""} 712 } 713 714 func isTypeParam(x *ast.Ident, y *ast.FuncType) bool { 715 tp := y.TypeParams 716 if tp == nil { 717 return false 718 } 719 for _, p := range tp.List { 720 for _, n := range p.Names { 721 if x == n { 722 return true 723 } 724 } 725 } 726 return false 727 } 728 729 func (tv *tokenVisitor) multiline(start, end token.Pos, val string, tok semtok.TokenType) { 730 f := tv.fset.File(start) 731 // the hard part is finding the lengths of lines. include the \n 732 leng := func(line int) int { 733 n := f.LineStart(line) 734 if line >= f.LineCount() { 735 return f.Size() - int(n) 736 } 737 return int(f.LineStart(line+1) - n) 738 } 739 spos := safetoken.StartPosition(tv.fset, start) 740 epos := safetoken.EndPosition(tv.fset, end) 741 sline := spos.Line 742 eline := epos.Line 743 // first line is from spos.Column to end 744 tv.token(start, leng(sline)-spos.Column, tok, nil) // leng(sline)-1 - (spos.Column-1) 745 for i := sline + 1; i < eline; i++ { 746 // intermediate lines are from 1 to end 747 tv.token(f.LineStart(i), leng(i)-1, tok, nil) // avoid the newline 748 } 749 // last line is from 1 to epos.Column 750 tv.token(f.LineStart(eline), epos.Column-1, tok, nil) // columns are 1-based 751 } 752 753 // findKeyword finds a keyword rather than guessing its location 754 func (tv *tokenVisitor) findKeyword(keyword string, start, end token.Pos) token.Pos { 755 offset := int(start) - tv.pgf.Tok.Base() 756 last := int(end) - tv.pgf.Tok.Base() 757 buf := tv.pgf.Src 758 idx := bytes.Index(buf[offset:last], []byte(keyword)) 759 if idx != -1 { 760 return start + token.Pos(idx) 761 } 762 //(in unparsable programs: type _ <-<-chan int) 763 tv.unexpected(fmt.Sprintf("not found:%s %v", keyword, safetoken.StartPosition(tv.fset, start))) 764 return token.NoPos 765 } 766 767 func (tv *tokenVisitor) importSpec(d *ast.ImportSpec) { 768 // a local package name or the last component of the Path 769 if d.Name != nil { 770 nm := d.Name.String() 771 if nm != "_" && nm != "." { 772 tv.token(d.Name.Pos(), len(nm), semtok.TokNamespace, nil) 773 } 774 return // don't mark anything for . or _ 775 } 776 importPath := metadata.UnquoteImportPath(d) 777 if importPath == "" { 778 return 779 } 780 // Import strings are implementation defined. Try to match with parse information. 781 depID := tv.pkg.Metadata().DepsByImpPath[importPath] 782 if depID == "" { 783 return 784 } 785 depMD := tv.metadataSource.Metadata(depID) 786 if depMD == nil { 787 // unexpected, but impact is that maybe some import is not colored 788 return 789 } 790 // Check whether the original literal contains the package's declared name. 791 j := strings.LastIndex(d.Path.Value, string(depMD.Name)) 792 if j == -1 { 793 // Package name does not match import path, so there is nothing to report. 794 return 795 } 796 // Report virtual declaration at the position of the substring. 797 start := d.Path.Pos() + token.Pos(j) 798 tv.token(start, len(depMD.Name), semtok.TokNamespace, nil) 799 } 800 801 // log unexpected state 802 func (tv *tokenVisitor) unexpected(msg string) { 803 if semDebug { 804 panic(msg) 805 } 806 event.Error(tv.ctx, tv.strStack(), errors.New(msg)) 807 } 808 809 var godirectives = map[string]struct{}{ 810 // https://pkg.go.dev/cmd/compile 811 "noescape": {}, 812 "uintptrescapes": {}, 813 "noinline": {}, 814 "norace": {}, 815 "nosplit": {}, 816 "linkname": {}, 817 818 // https://pkg.go.dev/go/build 819 "build": {}, 820 "binary-only-package": {}, 821 "embed": {}, 822 } 823 824 // Tokenize godirective at the start of the comment c, if any, and the surrounding comment. 825 // If there is any failure, emits the entire comment as a TokComment token. 826 // Directives are highlighted as-is, even if used incorrectly. Typically there are 827 // dedicated analyzers that will warn about misuse. 828 func (tv *tokenVisitor) godirective(c *ast.Comment) { 829 // First check if '//go:directive args...' is a valid directive. 830 directive, args, _ := strings.Cut(c.Text, " ") 831 kind, _ := stringsCutPrefix(directive, "//go:") 832 if _, ok := godirectives[kind]; !ok { 833 // Unknown go: directive. 834 tv.token(c.Pos(), len(c.Text), semtok.TokComment, nil) 835 return 836 } 837 838 // Make the 'go:directive' part stand out, the rest is comments. 839 tv.token(c.Pos(), len("//"), semtok.TokComment, nil) 840 841 directiveStart := c.Pos() + token.Pos(len("//")) 842 tv.token(directiveStart, len(directive[len("//"):]), semtok.TokNamespace, nil) 843 844 if len(args) > 0 { 845 tailStart := c.Pos() + token.Pos(len(directive)+len(" ")) 846 tv.token(tailStart, len(args), semtok.TokComment, nil) 847 } 848 } 849 850 // Go 1.20 strings.CutPrefix. 851 func stringsCutPrefix(s, prefix string) (after string, found bool) { 852 if !strings.HasPrefix(s, prefix) { 853 return s, false 854 } 855 return s[len(prefix):], true 856 }