github.com/v2fly/tools@v0.100.0/internal/lsp/semantic.go (about) 1 // Copyright 2020 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 lsp 6 7 import ( 8 "bytes" 9 "context" 10 "fmt" 11 "go/ast" 12 "go/token" 13 "go/types" 14 "log" 15 "sort" 16 "strings" 17 "time" 18 19 "github.com/v2fly/tools/internal/event" 20 "github.com/v2fly/tools/internal/lsp/protocol" 21 "github.com/v2fly/tools/internal/lsp/source" 22 errors "golang.org/x/xerrors" 23 ) 24 25 // reject full semantic token requests for large files 26 const maxFullFileSize int = 100000 27 28 func (s *Server) semanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) { 29 ret, err := s.computeSemanticTokens(ctx, p.TextDocument, nil) 30 return ret, err 31 } 32 33 func (s *Server) semanticTokensFullDelta(ctx context.Context, p *protocol.SemanticTokensDeltaParams) (interface{}, error) { 34 return nil, errors.Errorf("implement SemanticTokensFullDelta") 35 } 36 37 func (s *Server) semanticTokensRange(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { 38 ret, err := s.computeSemanticTokens(ctx, p.TextDocument, &p.Range) 39 return ret, err 40 } 41 42 func (s *Server) semanticTokensRefresh(ctx context.Context) error { 43 // in the code, but not in the protocol spec 44 return errors.Errorf("implement SemanticTokensRefresh") 45 } 46 47 func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocumentIdentifier, rng *protocol.Range) (*protocol.SemanticTokens, error) { 48 ans := protocol.SemanticTokens{ 49 Data: []uint32{}, 50 } 51 snapshot, _, ok, release, err := s.beginFileRequest(ctx, td.URI, source.Go) 52 defer release() 53 if !ok { 54 return nil, err 55 } 56 vv := snapshot.View() 57 if !vv.Options().SemanticTokens { 58 // return an error, so if the option changes 59 // the client won't remember the wrong answer 60 return nil, errors.Errorf("semantictokens are disabled") 61 } 62 pkg, err := snapshot.PackageForFile(ctx, td.URI.SpanURI(), source.TypecheckFull, source.WidestPackage) 63 if err != nil { 64 return nil, err 65 } 66 info := pkg.GetTypesInfo() 67 pgf, err := pkg.File(td.URI.SpanURI()) 68 if err != nil { 69 return nil, err 70 } 71 if pgf.ParseErr != nil { 72 return nil, pgf.ParseErr 73 } 74 if rng == nil && len(pgf.Src) > maxFullFileSize { 75 err := fmt.Errorf("semantic tokens: file %s too large for full (%d>%d)", 76 td.URI.SpanURI().Filename(), len(pgf.Src), maxFullFileSize) 77 return nil, err 78 } 79 e := &encoded{ 80 ctx: ctx, 81 pgf: pgf, 82 rng: rng, 83 ti: info, 84 fset: snapshot.FileSet(), 85 tokTypes: s.session.Options().SemanticTypes, 86 tokMods: s.session.Options().SemanticMods, 87 } 88 if err := e.init(); err != nil { 89 return nil, err 90 } 91 e.semantics() 92 ans.Data, err = e.Data() 93 if err != nil { 94 // this is an internal error, likely caused by a typo 95 // for a token or modifier 96 return nil, err 97 } 98 // for small cache, some day. for now, the client ignores this 99 ans.ResultID = fmt.Sprintf("%v", time.Now()) 100 return &ans, nil 101 } 102 103 func (e *encoded) semantics() { 104 f := e.pgf.File 105 e.token(f.Package, len("package"), tokKeyword, nil) 106 e.token(f.Name.NamePos, len(f.Name.Name), tokNamespace, nil) 107 inspect := func(n ast.Node) bool { 108 return e.inspector(n) 109 } 110 for _, d := range f.Decls { 111 // only look at the decls that overlap the range 112 start, end := d.Pos(), d.End() 113 if end <= e.start || start >= e.end { 114 continue 115 } 116 ast.Inspect(d, inspect) 117 } 118 } 119 120 type tokenType string 121 122 const ( 123 tokNamespace tokenType = "namespace" 124 tokType tokenType = "type" 125 tokInterface tokenType = "interface" 126 tokParameter tokenType = "parameter" 127 tokVariable tokenType = "variable" 128 tokMember tokenType = "member" 129 tokFunction tokenType = "function" 130 tokKeyword tokenType = "keyword" 131 tokComment tokenType = "comment" 132 tokString tokenType = "string" 133 tokNumber tokenType = "number" 134 tokOperator tokenType = "operator" 135 ) 136 137 func (e *encoded) token(start token.Pos, leng int, typ tokenType, mods []string) { 138 if start == 0 { 139 e.unexpected("token at token.NoPos") 140 } 141 if start >= e.end || start+token.Pos(leng) <= e.start { 142 return 143 } 144 // want a line and column from start (in LSP coordinates) 145 // [//line directives should be ignored] 146 rng := source.NewMappedRange(e.fset, e.pgf.Mapper, start, start+token.Pos(leng)) 147 lspRange, err := rng.Range() 148 if err != nil { 149 // possibly a //line directive. TODO(pjw): fix this somehow 150 // "column mapper is for file...instead of..." 151 // "line is beyond end of file..." 152 // see line 116 of internal/span/token.go which uses Position not PositionFor 153 event.Error(e.ctx, "failed to convert to range", err) 154 return 155 } 156 if lspRange.End.Line != lspRange.Start.Line { 157 // abrupt end of file, without \n. TODO(pjw): fix? 158 pos := e.fset.PositionFor(start, false) 159 msg := fmt.Sprintf("token at %s:%d.%d overflows", pos.Filename, pos.Line, pos.Column) 160 event.Log(e.ctx, msg) 161 return 162 } 163 // token is all on one line 164 length := lspRange.End.Character - lspRange.Start.Character 165 e.add(lspRange.Start.Line, lspRange.Start.Character, length, typ, mods) 166 } 167 168 func (e *encoded) add(line, start uint32, len uint32, tok tokenType, mod []string) { 169 x := semItem{line, start, len, tok, mod} 170 e.items = append(e.items, x) 171 } 172 173 // semItem represents a token found walking the parse tree 174 type semItem struct { 175 line, start uint32 176 len uint32 177 typeStr tokenType 178 mods []string 179 } 180 181 type encoded struct { 182 // the generated data 183 items []semItem 184 185 ctx context.Context 186 tokTypes, tokMods []string 187 pgf *source.ParsedGoFile 188 rng *protocol.Range 189 ti *types.Info 190 fset *token.FileSet 191 // allowed starting and ending token.Pos, set by init 192 // used to avoid looking at declarations not in range 193 start, end token.Pos 194 // path from the root of the parse tree, used for debugging 195 stack []ast.Node 196 } 197 198 // convert the stack to a string, for debugging 199 func (e *encoded) strStack() string { 200 msg := []string{"["} 201 for _, s := range e.stack { 202 msg = append(msg, fmt.Sprintf("%T", s)[5:]) 203 } 204 if len(e.stack) > 0 { 205 loc := e.stack[len(e.stack)-1].Pos() 206 add := e.pgf.Tok.PositionFor(loc, false) 207 msg = append(msg, fmt.Sprintf("(line:%d,col:%d)", add.Line, add.Column)) 208 } 209 msg = append(msg, "]") 210 return strings.Join(msg, " ") 211 } 212 213 func (e *encoded) inspector(n ast.Node) bool { 214 pop := func() { 215 e.stack = e.stack[:len(e.stack)-1] 216 } 217 if n == nil { 218 pop() 219 return true 220 } 221 e.stack = append(e.stack, n) 222 switch x := n.(type) { 223 case *ast.ArrayType: 224 case *ast.AssignStmt: 225 e.token(x.TokPos, len(x.Tok.String()), tokOperator, nil) 226 case *ast.BasicLit: 227 // if it extends across a line, skip it 228 // better would be to mark each line as string TODO(pjw) 229 if strings.Contains(x.Value, "\n") { 230 break 231 } 232 ln := len(x.Value) 233 what := tokNumber 234 if x.Kind == token.STRING { 235 what = tokString 236 if _, ok := e.stack[len(e.stack)-2].(*ast.Field); ok { 237 // struct tags (this is probably pointless, as the 238 // TextMate grammar will treat all the other comments the same) 239 what = tokComment 240 } 241 } 242 e.token(x.Pos(), ln, what, nil) 243 case *ast.BinaryExpr: 244 e.token(x.OpPos, len(x.Op.String()), tokOperator, nil) 245 case *ast.BlockStmt: 246 case *ast.BranchStmt: 247 e.token(x.TokPos, len(x.Tok.String()), tokKeyword, nil) 248 // There's no semantic encoding for labels 249 case *ast.CallExpr: 250 if x.Ellipsis != token.NoPos { 251 e.token(x.Ellipsis, len("..."), tokOperator, nil) 252 } 253 case *ast.CaseClause: 254 iam := "case" 255 if x.List == nil { 256 iam = "default" 257 } 258 e.token(x.Case, len(iam), tokKeyword, nil) 259 case *ast.ChanType: 260 // chan | chan <- | <- chan 261 if x.Arrow == token.NoPos || x.Arrow != x.Begin { 262 e.token(x.Begin, len("chan"), tokKeyword, nil) 263 break 264 } 265 pos := e.findKeyword("chan", x.Begin+2, x.Value.Pos()) 266 e.token(pos, len("chan"), tokKeyword, nil) 267 case *ast.CommClause: 268 iam := len("case") 269 if x.Comm == nil { 270 iam = len("default") 271 } 272 e.token(x.Case, iam, tokKeyword, nil) 273 case *ast.CompositeLit: 274 case *ast.DeclStmt: 275 case *ast.DeferStmt: 276 e.token(x.Defer, len("defer"), tokKeyword, nil) 277 case *ast.Ellipsis: 278 e.token(x.Ellipsis, len("..."), tokOperator, nil) 279 case *ast.EmptyStmt: 280 case *ast.ExprStmt: 281 case *ast.Field: 282 case *ast.FieldList: 283 case *ast.ForStmt: 284 e.token(x.For, len("for"), tokKeyword, nil) 285 case *ast.FuncDecl: 286 case *ast.FuncLit: 287 case *ast.FuncType: 288 if x.Func != token.NoPos { 289 e.token(x.Func, len("func"), tokKeyword, nil) 290 } 291 case *ast.GenDecl: 292 e.token(x.TokPos, len(x.Tok.String()), tokKeyword, nil) 293 case *ast.GoStmt: 294 e.token(x.Go, len("go"), tokKeyword, nil) 295 case *ast.Ident: 296 e.ident(x) 297 case *ast.IfStmt: 298 e.token(x.If, len("if"), tokKeyword, nil) 299 if x.Else != nil { 300 // x.Body.End() or x.Body.End()+1, not that it matters 301 pos := e.findKeyword("else", x.Body.End(), x.Else.Pos()) 302 e.token(pos, len("else"), tokKeyword, nil) 303 } 304 case *ast.ImportSpec: 305 e.importSpec(x) 306 pop() 307 return false 308 case *ast.IncDecStmt: 309 e.token(x.TokPos, len(x.Tok.String()), tokOperator, nil) 310 case *ast.IndexExpr: 311 case *ast.InterfaceType: 312 e.token(x.Interface, len("interface"), tokKeyword, nil) 313 case *ast.KeyValueExpr: 314 case *ast.LabeledStmt: 315 case *ast.MapType: 316 e.token(x.Map, len("map"), tokKeyword, nil) 317 case *ast.ParenExpr: 318 case *ast.RangeStmt: 319 e.token(x.For, len("for"), tokKeyword, nil) 320 // x.TokPos == token.NoPos is legal (for range foo {}) 321 offset := x.TokPos 322 if offset == token.NoPos { 323 offset = x.For 324 } 325 pos := e.findKeyword("range", offset, x.X.Pos()) 326 e.token(pos, len("range"), tokKeyword, nil) 327 case *ast.ReturnStmt: 328 e.token(x.Return, len("return"), tokKeyword, nil) 329 case *ast.SelectStmt: 330 e.token(x.Select, len("select"), tokKeyword, nil) 331 case *ast.SelectorExpr: 332 case *ast.SendStmt: 333 e.token(x.Arrow, len("<-"), tokOperator, nil) 334 case *ast.SliceExpr: 335 case *ast.StarExpr: 336 e.token(x.Star, len("*"), tokOperator, nil) 337 case *ast.StructType: 338 e.token(x.Struct, len("struct"), tokKeyword, nil) 339 case *ast.SwitchStmt: 340 e.token(x.Switch, len("switch"), tokKeyword, nil) 341 case *ast.TypeAssertExpr: 342 if x.Type == nil { 343 pos := e.findKeyword("type", x.Lparen, x.Rparen) 344 e.token(pos, len("type"), tokKeyword, nil) 345 } 346 case *ast.TypeSpec: 347 case *ast.TypeSwitchStmt: 348 e.token(x.Switch, len("switch"), tokKeyword, nil) 349 case *ast.UnaryExpr: 350 e.token(x.OpPos, len(x.Op.String()), tokOperator, nil) 351 case *ast.ValueSpec: 352 // things we won't see 353 case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt, 354 *ast.File, *ast.Package: 355 log.Printf("implement %T %s", x, e.pgf.Tok.PositionFor(x.Pos(), false)) 356 // things we knowingly ignore 357 case *ast.Comment, *ast.CommentGroup: 358 pop() 359 return false 360 default: // just to be super safe. 361 e.unexpected(fmt.Sprintf("failed to implement %T", x)) 362 } 363 return true 364 } 365 func (e *encoded) ident(x *ast.Ident) { 366 def := e.ti.Defs[x] 367 if def != nil { 368 what, mods := e.definitionFor(x) 369 if what != "" { 370 e.token(x.Pos(), len(x.String()), what, mods) 371 } 372 return 373 } 374 use := e.ti.Uses[x] 375 switch y := use.(type) { 376 case nil: 377 e.token(x.NamePos, len(x.Name), tokVariable, []string{"definition"}) 378 case *types.Builtin: 379 e.token(x.NamePos, len(x.Name), tokFunction, []string{"defaultLibrary"}) 380 case *types.Const: 381 mods := []string{"readonly"} 382 tt := y.Type() 383 if _, ok := tt.(*types.Basic); ok { 384 e.token(x.Pos(), len(x.String()), tokVariable, mods) 385 break 386 } 387 if ttx, ok := tt.(*types.Named); ok { 388 if x.String() == "iota" { 389 e.unexpected(fmt.Sprintf("iota:%T", ttx)) 390 } 391 if _, ok := ttx.Underlying().(*types.Basic); ok { 392 e.token(x.Pos(), len(x.String()), tokVariable, mods) 393 break 394 } 395 e.unexpected(fmt.Sprintf("%q/%T", x.String(), tt)) 396 } 397 // can this happen? Don't think so 398 e.unexpected(fmt.Sprintf("%s %T %#v", x.String(), tt, tt)) 399 case *types.Func: 400 e.token(x.Pos(), len(x.Name), tokFunction, nil) 401 case *types.Label: 402 // nothing to map it to 403 case *types.Nil: 404 // nil is a predeclared identifier 405 e.token(x.Pos(), len("nil"), tokVariable, []string{"readonly", "defaultLibrary"}) 406 case *types.PkgName: 407 e.token(x.Pos(), len(x.Name), tokNamespace, nil) 408 case *types.TypeName: 409 var mods []string 410 if _, ok := y.Type().(*types.Basic); ok { 411 mods = []string{"defaultLibrary"} 412 } 413 e.token(x.Pos(), len(x.String()), tokType, mods) 414 case *types.Var: 415 e.token(x.Pos(), len(x.Name), tokVariable, nil) 416 default: 417 // replace with panic after extensive testing 418 if use == nil { 419 msg := fmt.Sprintf("%#v/%#v %#v %#v", x, x.Obj, e.ti.Defs[x], e.ti.Uses[x]) 420 e.unexpected(msg) 421 } 422 if use.Type() != nil { 423 e.unexpected(fmt.Sprintf("%s %T/%T,%#v", x.String(), use, use.Type(), use)) 424 } else { 425 e.unexpected(fmt.Sprintf("%s %T", x.String(), use)) 426 } 427 } 428 } 429 430 func isDeprecated(n *ast.CommentGroup) bool { 431 if n == nil { 432 return false 433 } 434 for _, c := range n.List { 435 if strings.HasPrefix(c.Text, "// Deprecated") { 436 return true 437 } 438 } 439 return false 440 } 441 442 func (e *encoded) definitionFor(x *ast.Ident) (tokenType, []string) { 443 mods := []string{"definition"} 444 for i := len(e.stack) - 1; i >= 0; i-- { 445 s := e.stack[i] 446 switch y := s.(type) { 447 case *ast.AssignStmt, *ast.RangeStmt: 448 if x.Name == "_" { 449 return "", nil // not really a variable 450 } 451 return "variable", mods 452 case *ast.GenDecl: 453 if isDeprecated(y.Doc) { 454 mods = append(mods, "deprecated") 455 } 456 if y.Tok == token.CONST { 457 mods = append(mods, "readonly") 458 } 459 return tokVariable, mods 460 case *ast.FuncDecl: 461 // If x is immediately under a FuncDecl, it is a function or method 462 if i == len(e.stack)-2 { 463 if isDeprecated(y.Doc) { 464 mods = append(mods, "deprecated") 465 } 466 if y.Recv != nil { 467 return tokMember, mods 468 } 469 return tokFunction, mods 470 } 471 // if x < ... < FieldList < FuncDecl, this is the receiver, a variable 472 if _, ok := e.stack[i+1].(*ast.FieldList); ok { 473 return tokVariable, nil 474 } 475 // if x < ... < FieldList < FuncType < FuncDecl, this is a param 476 return tokParameter, mods 477 case *ast.InterfaceType: 478 return tokMember, mods 479 case *ast.TypeSpec: 480 // GenDecl/Typespec/FuncType/FieldList/Field/Ident 481 // (type A func(b uint64)) (err error) 482 // b and err should not be tokType, but tokVaraible 483 // and in GenDecl/TpeSpec/StructType/FieldList/Field/Ident 484 // (type A struct{b uint64}) 485 fldm := e.stack[len(e.stack)-2] 486 if _, ok := fldm.(*ast.Field); ok { 487 return tokVariable, mods 488 } 489 return tokType, mods 490 } 491 } 492 // panic after extensive testing 493 msg := fmt.Sprintf("failed to find the decl for %s", e.pgf.Tok.PositionFor(x.Pos(), false)) 494 e.unexpected(msg) 495 return "", []string{""} 496 } 497 498 // findKeyword finds a keyword rather than guessing its location 499 func (e *encoded) findKeyword(keyword string, start, end token.Pos) token.Pos { 500 offset := int(start) - e.pgf.Tok.Base() 501 last := int(end) - e.pgf.Tok.Base() 502 buf := e.pgf.Src 503 idx := bytes.Index(buf[offset:last], []byte(keyword)) 504 if idx != -1 { 505 return start + token.Pos(idx) 506 } 507 // can't happen 508 e.unexpected(fmt.Sprintf("not found:%s %v", keyword, e.fset.PositionFor(start, false))) 509 return token.NoPos 510 } 511 512 func (e *encoded) init() error { 513 e.start = token.Pos(e.pgf.Tok.Base()) 514 e.end = e.start + token.Pos(e.pgf.Tok.Size()) 515 if e.rng == nil { 516 return nil 517 } 518 span, err := e.pgf.Mapper.RangeSpan(*e.rng) 519 if err != nil { 520 return errors.Errorf("range span (%v) error for %s", err, e.pgf.File.Name) 521 } 522 e.end = e.start + token.Pos(span.End().Offset()) 523 e.start += token.Pos(span.Start().Offset()) 524 return nil 525 } 526 527 func (e *encoded) Data() ([]uint32, error) { 528 // binary operators, at least, will be out of order 529 sort.Slice(e.items, func(i, j int) bool { 530 if e.items[i].line != e.items[j].line { 531 return e.items[i].line < e.items[j].line 532 } 533 return e.items[i].start < e.items[j].start 534 }) 535 typeMap, modMap := e.maps() 536 // each semantic token needs five values 537 // (see Integer Encoding for Tokens in the LSP spec) 538 x := make([]uint32, 5*len(e.items)) 539 for i := 0; i < len(e.items); i++ { 540 j := 5 * i 541 if i == 0 { 542 x[0] = e.items[0].line 543 } else { 544 x[j] = e.items[i].line - e.items[i-1].line 545 } 546 x[j+1] = e.items[i].start 547 if i > 0 && e.items[i].line == e.items[i-1].line { 548 x[j+1] = e.items[i].start - e.items[i-1].start 549 } 550 x[j+2] = e.items[i].len 551 x[j+3] = uint32(typeMap[e.items[i].typeStr]) 552 mask := 0 553 for _, s := range e.items[i].mods { 554 mask |= modMap[s] 555 } 556 x[j+4] = uint32(mask) 557 } 558 return x, nil 559 } 560 561 func (e *encoded) importSpec(d *ast.ImportSpec) { 562 // a local package name or the last component of the Path 563 if d.Name != nil { 564 nm := d.Name.String() 565 // import . x => x is not a namespace 566 // import _ x => x is a namespace 567 if nm != "_" && nm != "." { 568 e.token(d.Name.Pos(), len(nm), tokNamespace, nil) 569 return 570 } 571 if nm == "." { 572 return 573 } 574 // and fall through for _ 575 } 576 nm := d.Path.Value[1 : len(d.Path.Value)-1] // trailing " 577 v := strings.LastIndex(nm, "/") 578 if v != -1 { 579 nm = nm[v+1:] 580 } 581 start := d.Path.End() - token.Pos(1+len(nm)) 582 e.token(start, len(nm), tokNamespace, nil) 583 } 584 585 // panic on unexpected state 586 func (e *encoded) unexpected(msg string) { 587 log.Print(msg) 588 log.Print(e.strStack()) 589 panic(msg) 590 } 591 592 // SemType returns a string equivalent of the type, for gopls semtok 593 func SemType(n int) string { 594 tokTypes := SemanticTypes() 595 tokMods := SemanticModifiers() 596 if n >= 0 && n < len(tokTypes) { 597 return tokTypes[n] 598 } 599 return fmt.Sprintf("?%d[%d,%d]?", n, len(tokTypes), len(tokMods)) 600 } 601 602 // SemMods returns the []string equivalent of the mods, for gopls semtok. 603 func SemMods(n int) []string { 604 tokMods := SemanticModifiers() 605 mods := []string{} 606 for i := 0; i < len(tokMods); i++ { 607 if (n & (1 << uint(i))) != 0 { 608 mods = append(mods, tokMods[i]) 609 } 610 } 611 return mods 612 } 613 614 func (e *encoded) maps() (map[tokenType]int, map[string]int) { 615 tmap := make(map[tokenType]int) 616 mmap := make(map[string]int) 617 for i, t := range e.tokTypes { 618 tmap[tokenType(t)] = i 619 } 620 for i, m := range e.tokMods { 621 mmap[m] = 1 << uint(i) // go 1.12 compatibility 622 } 623 return tmap, mmap 624 } 625 626 // SemanticTypes to use in case there is no client, as in the command line, or tests 627 func SemanticTypes() []string { 628 return semanticTypes[:] 629 } 630 631 // SemanticModifiers to use in case there is no client. 632 func SemanticModifiers() []string { 633 return semanticModifiers[:] 634 } 635 636 var ( 637 semanticTypes = [...]string{ 638 "namespace", "type", "class", "enum", "interface", 639 "struct", "typeParameter", "parameter", "variable", "property", "enumMember", 640 "event", "function", "member", "macro", "keyword", "modifier", "comment", 641 "string", "number", "regexp", "operator"} 642 semanticModifiers = [...]string{ 643 "declaration", "definition", "readonly", "static", 644 "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary"} 645 )