kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/indexer/markedsource.go (about) 1 /* 2 * Copyright 2019 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package indexer 18 19 import ( 20 "fmt" 21 "go/types" 22 "strings" 23 24 "kythe.io/kythe/go/util/kytheuri" 25 "kythe.io/kythe/go/util/log" 26 "kythe.io/kythe/go/util/schema/facts" 27 28 "github.com/golang/protobuf/proto" 29 30 cpb "kythe.io/kythe/proto/common_go_proto" 31 spb "kythe.io/kythe/proto/storage_go_proto" 32 ) 33 34 // MarkedSource returns a MarkedSource message describing obj. 35 // See: http://www.kythe.io/docs/schema/marked-source.html. 36 func (e *emitter) MarkedSource(obj types.Object) *cpb.MarkedSource { 37 pi := e.pi 38 ms := &cpb.MarkedSource{ 39 Child: []*cpb.MarkedSource{{ 40 Kind: cpb.MarkedSource_IDENTIFIER, 41 PreText: objectName(obj), 42 Link: []*cpb.Link{{Definition: []string{kytheuri.ToString(pi.ObjectVName(obj))}}}, 43 }}, 44 } 45 46 // Include the package name as context, and for objects that hang off a 47 // named struct or interface, a label for that type. 48 // 49 // For example, given 50 // package p 51 // var v int // context is "p" 52 // type s struct { v int } // context is "p.s" 53 // func (v) f(x int) {} 54 // ^ ^--------------- context is "p.v.f" 55 // \----------------- context is "p.v" 56 // 57 // The tree structure is: 58 // 59 // (box) 60 // | 61 // (ctx)-----+-------(id) 62 // | | 63 // +----"."----+(".") name 64 // | | 65 // (id) pkg type 66 // 67 if ctx := pi.typeContext(obj); len(ctx) != 0 { 68 ms.Child = append([]*cpb.MarkedSource{{ 69 Kind: cpb.MarkedSource_CONTEXT, 70 PostChildText: ".", 71 AddFinalListToken: true, 72 Child: ctx, 73 }}, ms.Child...) 74 } 75 76 // Handle types with "interesting" superstructure specially. 77 switch t := obj.(type) { 78 case *types.Func: 79 // For functions we include the parameters and return values, and for 80 // methods the receiver. 81 // 82 // Methods: func (R) Name(p1, ...) (r0, ...) 83 // Functions: func Name(p0, ...) (r0, ...) 84 fn := &cpb.MarkedSource{ 85 Kind: cpb.MarkedSource_BOX, 86 Child: []*cpb.MarkedSource{{ 87 Kind: cpb.MarkedSource_MODIFIER, 88 PreText: "func", 89 PostText: " ", 90 }}, 91 } 92 sig := t.Type().(*types.Signature) 93 firstParam := 0 94 if recv := sig.Recv(); recv != nil { 95 // Parenthesized receiver type, e.g. (R). 96 fn.Child = append(fn.Child, &cpb.MarkedSource{ 97 Kind: cpb.MarkedSource_PARAMETER, 98 PreText: "(", 99 PostText: ") ", 100 Child: []*cpb.MarkedSource{{ 101 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 102 }}, 103 }) 104 firstParam = 1 105 } 106 fn.Child = append(fn.Child, ms) 107 108 if sig.TypeParams().Len() > 0 { 109 tps := &cpb.MarkedSource{ 110 Kind: cpb.MarkedSource_PARAMETER_LOOKUP_BY_TPARAM, 111 PreText: "[", 112 PostText: "]", 113 PostChildText: ", ", 114 } 115 fn.Child = append(fn.Child, tps) 116 } 117 118 // If there are no parameters, the lookup will not produce anything. 119 // Ensure when this happens we still get parentheses for notational 120 // purposes. 121 if sig.Params().Len() == 0 { 122 fn.Child = append(fn.Child, &cpb.MarkedSource{ 123 Kind: cpb.MarkedSource_PARAMETER, 124 PreText: "()", 125 }) 126 } else { 127 fn.Child = append(fn.Child, &cpb.MarkedSource{ 128 Kind: cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM, 129 PreText: "(", 130 PostChildText: ", ", 131 PostText: ")", 132 LookupIndex: uint32(firstParam), 133 }) 134 } 135 if res := sig.Results(); res != nil && res.Len() > 0 { 136 rms := &cpb.MarkedSource{Kind: cpb.MarkedSource_TYPE, PreText: " "} 137 var hasNamedReturn bool 138 for i := 0; i < res.Len(); i++ { 139 if v := res.At(i); v.Name() == "" { 140 rms.Child = append(rms.Child, &cpb.MarkedSource{ 141 PreText: typeName(v.Type()), 142 }) 143 } else { 144 hasNamedReturn = true 145 rms.Child = append(rms.Child, &cpb.MarkedSource{ 146 PostChildText: " ", 147 Child: []*cpb.MarkedSource{{ 148 Kind: cpb.MarkedSource_IDENTIFIER, 149 PreText: v.Name(), 150 Link: []*cpb.Link{{Definition: []string{kytheuri.ToString(pi.ObjectVName(v))}}}, 151 }, { 152 Kind: cpb.MarkedSource_TYPE, 153 PreText: typeName(v.Type()), 154 }}, 155 }) 156 } 157 } 158 if res.Len() > 1 || hasNamedReturn { 159 // If there is more than one result type (or the return is named), parenthesize. 160 rms.PreText = " (" 161 rms.PostText = ")" 162 rms.PostChildText = ", " 163 } 164 fn.Child = append(fn.Child, rms) 165 } 166 ms = fn 167 168 case *types.Var: 169 // For variables and fields, include the type. 170 repl := &cpb.MarkedSource{ 171 Kind: cpb.MarkedSource_BOX, 172 PostChildText: " ", 173 Child: []*cpb.MarkedSource{ 174 ms, 175 {Kind: cpb.MarkedSource_LOOKUP_BY_TYPED}, 176 }, 177 } 178 ms = repl 179 180 case *types.TypeName: 181 switch tt := t.Type().(type) { 182 case *types.Named: 183 if pkg := tt.Obj().Pkg(); pkg != nil && pkg.Name() == "builtin" { 184 return ms 185 } 186 ms = &cpb.MarkedSource{ 187 Kind: cpb.MarkedSource_BOX, 188 PostChildText: " ", 189 Child: []*cpb.MarkedSource{ 190 { 191 Kind: cpb.MarkedSource_MODIFIER, 192 PreText: "type", 193 ExcludeOnInclude: []cpb.MarkedSource_Kind{ 194 cpb.MarkedSource_LOOKUP_BY_TYPED, 195 }, 196 }, 197 ms, 198 }, 199 } 200 if tt.TypeParams().Len() > 0 { 201 tps := &cpb.MarkedSource{ 202 Kind: cpb.MarkedSource_PARAMETER_LOOKUP_BY_TPARAM, 203 PreText: "[", 204 PostText: "]", 205 PostChildText: ", ", 206 ExcludeOnInclude: []cpb.MarkedSource_Kind{ 207 cpb.MarkedSource_LOOKUP_BY_TYPED, 208 }, 209 } 210 ms.Child = append(ms.Child[:len(ms.Child)-1], &cpb.MarkedSource{ 211 Child: []*cpb.MarkedSource{ms.GetChild()[len(ms.GetChild())-1], tps}, 212 }) 213 } 214 215 case *types.TypeParam: 216 repl := &cpb.MarkedSource{ 217 Kind: cpb.MarkedSource_BOX, 218 PostChildText: " ", 219 Child: []*cpb.MarkedSource{ 220 ms, 221 { 222 Kind: cpb.MarkedSource_TYPE, 223 PreText: typeName(tt.Constraint()), 224 Link: []*cpb.Link{{Definition: []string{kytheuri.ToString(e.emitType(tt.Constraint()))}}}, 225 ExcludeOnInclude: []cpb.MarkedSource_Kind{ 226 cpb.MarkedSource_LOOKUP_BY_TYPED, 227 }, 228 }, 229 }, 230 } 231 ms = repl 232 } 233 234 default: 235 // TODO(fromberger): Handle other variations from go/types. 236 } 237 return ms 238 } 239 240 // objectName returns a human-readable name for obj if one can be inferred. If 241 // the object has its own non-blank name, that is used; otherwise if the object 242 // is of a named type, that type's name is used. Otherwise the result is "_". 243 func objectName(obj types.Object) string { 244 if name := obj.Name(); name != "" { 245 return name // the object's given name 246 } else if name := typeName(obj.Type()); name != "" { 247 return name // the object's type's name 248 } 249 return "_" // not sure what to call it 250 } 251 252 // typeName returns a human readable name for typ. 253 func typeName(typ types.Type) string { 254 switch t := typ.(type) { 255 case *types.Named: 256 return t.Obj().Name() + typeArgs(typ) 257 case *types.Basic: 258 return t.Name() 259 case *types.Struct: 260 return "struct {...}" 261 case *types.Interface: 262 if t.String() == "any" { 263 return t.String() 264 } 265 return "interface {...}" 266 case *types.Pointer: 267 return "*" + typeName(t.Elem()) 268 case *types.Chan: 269 switch t.Dir() { 270 case types.SendOnly: 271 return "chan<- " + typeName(t.Elem()) 272 case types.RecvOnly: 273 return "<-chan " + typeName(t.Elem()) 274 default: 275 return "chan " + typeName(t.Elem()) 276 } 277 case *types.Array: 278 return fmt.Sprintf("[%d]%s", t.Len(), typeName(t.Elem())) 279 case *types.Map: 280 return fmt.Sprintf("map[%s]%s", typeName(t.Key()), typeName(t.Elem())) 281 case *types.Slice: 282 return "[]" + typeName(t.Elem()) 283 } 284 return typ.String() 285 } 286 287 // typeArgs returns a human readable string for a type's list of type arguments 288 // (or "" if the type does not have any). 289 func typeArgs(typ types.Type) string { 290 n, ok := deref(typ).(*types.Named) 291 if !ok || n.TypeArgs().Len() == 0 { 292 return "" 293 } 294 args := n.TypeArgs() 295 var ss []string 296 for i := 0; i < args.Len(); i++ { 297 ss = append(ss, typeName(args.At(i))) 298 } 299 return "[" + strings.Join(ss, ", ") + "]" 300 } 301 302 // typeContext returns the package, type, and function context identifiers that 303 // qualify the name of obj, if any are applicable. The result is empty if there 304 // are no appropriate qualifiers. 305 func (pi *PackageInfo) typeContext(obj types.Object) []*cpb.MarkedSource { 306 var ms []*cpb.MarkedSource 307 addID := func(s string, v *spb.VName) { 308 id := &cpb.MarkedSource{ 309 Kind: cpb.MarkedSource_IDENTIFIER, 310 PreText: s, 311 } 312 if v != nil { 313 id.Link = []*cpb.Link{{Definition: []string{kytheuri.ToString(v)}}} 314 } 315 ms = append(ms, id) 316 } 317 for cur := pi.owner[obj]; cur != nil; cur = pi.owner[cur] { 318 if t, ok := cur.(interface { 319 Name() string 320 }); ok { 321 addID(t.Name(), pi.ObjectVName(cur)) 322 } else { 323 addID(typeName(cur.Type()), pi.ObjectVName(cur)) 324 } 325 } 326 if pkg := obj.Pkg(); pkg != nil { 327 addID(pi.importPath(pkg), pi.PackageVName[pkg]) 328 } 329 for i, j := 0, len(ms)-1; i < j; { 330 ms[i], ms[j] = ms[j], ms[i] 331 i++ 332 j-- 333 } 334 return ms 335 } 336 337 // rewriteMarkedSourceCorpus finds all tickets in the MarkedSource 338 // and its children and rewrites them to use the given corpus. 339 func rewriteMarkedSourceCorpus(ms *cpb.MarkedSource, corpus string) { 340 for _, link := range ms.Link { 341 for i, def := range link.Definition { 342 v, err := kytheuri.ToVName(def) 343 if err != nil { 344 log.Errorf("parsing ticket %q: %v", def, err) 345 continue 346 } 347 v.Corpus = corpus 348 link.Definition[i] = kytheuri.ToString(v) 349 } 350 } 351 for _, child := range ms.Child { 352 rewriteMarkedSourceCorpus(child, corpus) 353 } 354 } 355 356 // emitCode emits a code fact for the specified marked source message on the 357 // target, or logs a diagnostic. 358 func (e *emitter) emitCode(target *spb.VName, ms *cpb.MarkedSource) { 359 if ms != nil { 360 if e.opts.UseCompilationCorpusForAll { 361 rewriteMarkedSourceCorpus(ms, e.pi.VName.Corpus) 362 } 363 bits, err := proto.Marshal(ms) 364 if err != nil { 365 log.Errorf("Unable to marshal marked source: %v", err) 366 return 367 } 368 e.writeFact(target, facts.Code, string(bits)) 369 } 370 } 371 372 func (e *emitter) emitPackageMarkedSource(pi *PackageInfo) { 373 if !e.opts.emitMarkedSource() || len(pi.Files) == 0 { 374 return 375 } 376 377 ipath := pi.ImportPath 378 ms := &cpb.MarkedSource{ 379 Child: []*cpb.MarkedSource{{ 380 Kind: cpb.MarkedSource_IDENTIFIER, 381 PreText: ipath, 382 }}, 383 } 384 if p := strings.LastIndex(ipath, "/"); p > 0 { 385 ms.Child[0].PreText = ipath[p+1:] 386 ms.Child = append([]*cpb.MarkedSource{{ 387 Kind: cpb.MarkedSource_CONTEXT, 388 Child: []*cpb.MarkedSource{{ 389 Kind: cpb.MarkedSource_IDENTIFIER, 390 PreText: ipath[:p], 391 }}, 392 PostText: "/", 393 }}, ms.Child...) 394 } 395 e.emitCode(pi.VName, ms) 396 } 397 398 func (e *emitter) emitBuiltinMarkedSource(b *spb.VName) { 399 if e.opts.emitMarkedSource() { 400 e.emitCode(b, &cpb.MarkedSource{ 401 Kind: cpb.MarkedSource_TYPE, 402 PreText: strings.TrimSuffix(b.Signature, "#builtin"), 403 }) 404 } 405 } 406 407 var ( 408 sliceTAppMS = &cpb.MarkedSource{ 409 Kind: cpb.MarkedSource_TYPE, 410 Child: []*cpb.MarkedSource{{ 411 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 412 LookupIndex: 1, 413 }}, 414 PreText: "[]", 415 } 416 pointerTAppMS = &cpb.MarkedSource{ 417 Kind: cpb.MarkedSource_TYPE, 418 Child: []*cpb.MarkedSource{{ 419 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 420 LookupIndex: 1, 421 }}, 422 PreText: "*", 423 } 424 mapTAppMS = &cpb.MarkedSource{ 425 Kind: cpb.MarkedSource_TYPE, 426 PreText: "map", 427 Child: []*cpb.MarkedSource{{ 428 Kind: cpb.MarkedSource_BOX, 429 PreText: "[", 430 PostText: "]", 431 Child: []*cpb.MarkedSource{{ 432 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 433 LookupIndex: 1, 434 }}, 435 }, { 436 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 437 LookupIndex: 2, 438 }}, 439 } 440 tupleTAppMS = &cpb.MarkedSource{ 441 Kind: cpb.MarkedSource_TYPE, 442 Child: []*cpb.MarkedSource{{ 443 Kind: cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM, 444 LookupIndex: 1, 445 PostChildText: ", ", 446 }}, 447 PreText: "(", 448 PostText: ")", 449 } 450 variadicTAppMS = &cpb.MarkedSource{ 451 Kind: cpb.MarkedSource_TYPE, 452 Child: []*cpb.MarkedSource{{ 453 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 454 LookupIndex: 1, 455 }}, 456 PreText: "...", 457 } 458 chanOmniTAppMS = &cpb.MarkedSource{ 459 Kind: cpb.MarkedSource_TYPE, 460 Child: []*cpb.MarkedSource{{ 461 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 462 LookupIndex: 1, 463 }}, 464 PreText: "chan ", 465 } 466 chanSendTAppMS = &cpb.MarkedSource{ 467 Kind: cpb.MarkedSource_TYPE, 468 Child: []*cpb.MarkedSource{{ 469 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 470 LookupIndex: 1, 471 }}, 472 PreText: "chan<- ", 473 } 474 chanRecvTAppMS = &cpb.MarkedSource{ 475 Kind: cpb.MarkedSource_TYPE, 476 Child: []*cpb.MarkedSource{{ 477 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 478 LookupIndex: 1, 479 }}, 480 PreText: "<-chan ", 481 } 482 genericTAppMS = &cpb.MarkedSource{ 483 Kind: cpb.MarkedSource_TYPE, 484 Child: []*cpb.MarkedSource{{ 485 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 486 LookupIndex: 0, 487 }, { 488 Kind: cpb.MarkedSource_BOX, 489 PreText: "[", 490 PostText: "]", 491 Child: []*cpb.MarkedSource{{ 492 Kind: cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM, 493 LookupIndex: 1, 494 }}, 495 PostChildText: ", ", 496 }}, 497 } 498 ) 499 500 func arrayTAppMS(length int64) *cpb.MarkedSource { 501 return &cpb.MarkedSource{ 502 Kind: cpb.MarkedSource_TYPE, 503 Child: []*cpb.MarkedSource{{ 504 Kind: cpb.MarkedSource_LOOKUP_BY_PARAM, 505 LookupIndex: 1, 506 }}, 507 PreText: fmt.Sprintf("[%d]", length), 508 } 509 } 510 511 func chanTAppMS(dir types.ChanDir) *cpb.MarkedSource { 512 switch dir { 513 case types.SendOnly: 514 return chanSendTAppMS 515 case types.RecvOnly: 516 return chanRecvTAppMS 517 default: 518 return chanOmniTAppMS 519 } 520 }