Ensure errors are handled or logged
if err != nil {
1// Copyright 2013 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package types267import (8 "cmd/compile/internal/syntax"9 "cmp"10 "fmt"11 "go/constant"12 . "internal/types/errors"13 "slices"14 "strconv"15 "strings"16 "unicode"17)1819// A declInfo describes a package-level const, type, var, or func declaration.20type declInfo struct {21 file *Scope // scope of file containing this declaration22 version goVersion // Go version of file containing this declaration23 lhs []*Var // lhs of n:1 variable declarations, or nil24 vtyp syntax.Expr // type, or nil (for const and var declarations only)25 init syntax.Expr // init/orig expression, or nil (for const and var declarations only)26 inherited bool // if set, the init expression is inherited from a previous constant declaration27 tdecl *syntax.TypeDecl // type declaration, or nil28 fdecl *syntax.FuncDecl // func declaration, or nil2930 // The deps field tracks initialization expression dependencies.31 deps map[Object]bool // lazily initialized32}3334// hasInitializer reports whether the declared object has an initialization35// expression or function body.36func (d *declInfo) hasInitializer() bool {37 return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil38}3940// addDep adds obj to the set of objects d's init expression depends on.41func (d *declInfo) addDep(obj Object) {42 m := d.deps43 if m == nil {44 m = make(map[Object]bool)45 d.deps = m46 }47 m[obj] = true48}4950// arity checks that the lhs and rhs of a const or var decl51// have a matching number of names and initialization values.52// If inherited is set, the initialization values are from53// another (constant) declaration.54func (check *Checker) arity(pos syntax.Pos, names []*syntax.Name, inits []syntax.Expr, constDecl, inherited bool) {55 l := len(names)56 r := len(inits)5758 const code = WrongAssignCount59 switch {60 case l < r:61 n := inits[l]62 if inherited {63 check.errorf(pos, code, "extra init expr at %s", n.Pos())64 } else {65 check.errorf(n, code, "extra init expr %s", n)66 }67 case l > r && (constDecl || r != 1): // if r == 1 it may be a multi-valued function and we can't say anything yet68 n := names[r]69 check.errorf(n, code, "missing init expr for %s", n.Value)70 }71}7273func validatedImportPath(path string) (string, error) {74 s, err := strconv.Unquote(path)75 if err != nil {76 return "", err77 }78 if s == "" {79 return "", fmt.Errorf("empty string")80 }81 const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"82 for _, r := range s {83 if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {84 return s, fmt.Errorf("invalid character %#U", r)85 }86 }87 return s, nil88}8990// declarePkgObj declares obj in the package scope, records its ident -> obj mapping,91// and updates check.objMap. The object must not be a function or method.92func (check *Checker) declarePkgObj(ident *syntax.Name, obj Object, d *declInfo) {93 assert(ident.Value == obj.Name())9495 // spec: "A package-scope or file-scope identifier with name init96 // may only be declared to be a function with this (func()) signature."97 if ident.Value == "init" {98 check.error(ident, InvalidInitDecl, "cannot declare init - must be func")99 return100 }101102 // spec: "The main package must have package name main and declare103 // a function main that takes no arguments and returns no value."104 if ident.Value == "main" && check.pkg.name == "main" {105 check.error(ident, InvalidMainDecl, "cannot declare main - must be func")106 return107 }108109 check.declare(check.pkg.scope, ident, obj, nopos)110 check.objMap[obj] = d111 obj.setOrder(uint32(len(check.objMap)))112}113114// filename returns a filename suitable for debugging output.115func (check *Checker) filename(fileNo int) string {116 file := check.files[fileNo]117 if pos := file.Pos(); pos.IsKnown() {118 // return check.fset.File(pos).Name()119 // TODO(gri) do we need the actual file name here?120 return pos.RelFilename()121 }122 return fmt.Sprintf("file[%d]", fileNo)123}124125func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package {126 // If we already have a package for the given (path, dir)127 // pair, use it instead of doing a full import.128 // Checker.impMap only caches packages that are marked Complete129 // or fake (dummy packages for failed imports). Incomplete but130 // non-fake packages do require an import to complete them.131 key := importKey{path, dir}132 imp := check.impMap[key]133 if imp != nil {134 return imp135 }136137 // no package yet => import it138 if path == "C" && (check.conf.FakeImportC || check.conf.go115UsesCgo) {139 if check.conf.FakeImportC && check.conf.go115UsesCgo {140 check.error(pos, BadImportPath, "cannot use FakeImportC and go115UsesCgo together")141 }142 imp = NewPackage("C", "C")143 imp.fake = true // package scope is not populated144 imp.cgo = check.conf.go115UsesCgo145 } else {146 // ordinary import147 var err error148 if importer := check.conf.Importer; importer == nil {149 err = fmt.Errorf("Config.Importer not installed")150 } else if importerFrom, ok := importer.(ImporterFrom); ok {151 imp, err = importerFrom.ImportFrom(path, dir, 0)152 if imp == nil && err == nil {153 err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)154 }155 } else {156 imp, err = importer.Import(path)157 if imp == nil && err == nil {158 err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)159 }160 }161 // make sure we have a valid package name162 // (errors here can only happen through manipulation of packages after creation)163 if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {164 err = fmt.Errorf("invalid package name: %q", imp.name)165 imp = nil // create fake package below166 }167 if err != nil {168 check.errorf(pos, BrokenImport, "could not import %s (%s)", path, err)169 if imp == nil {170 // create a new fake package171 // come up with a sensible package name (heuristic)172 name := strings.TrimSuffix(path, "/")173 if i := strings.LastIndex(name, "/"); i >= 0 {174 name = name[i+1:]175 }176 imp = NewPackage(path, name)177 }178 // continue to use the package as best as we can179 imp.fake = true // avoid follow-up lookup failures180 }181 }182183 // package should be complete or marked fake, but be cautious184 if imp.complete || imp.fake {185 check.impMap[key] = imp186 // Once we've formatted an error message, keep the pkgPathMap187 // up-to-date on subsequent imports. It is used for package188 // qualification in error messages.189 if check.pkgPathMap != nil {190 check.markImports(imp)191 }192 return imp193 }194195 // something went wrong (importer may have returned incomplete package without error)196 return nil197}198199// collectObjects collects all file and package objects and inserts them200// into their respective scopes. It also performs imports and associates201// methods with receiver base type names.202func (check *Checker) collectObjects() {203 pkg := check.pkg204205 // pkgImports is the set of packages already imported by any package file seen206 // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate207 // it (pkg.imports may not be empty if we are checking test files incrementally).208 // Note that pkgImports is keyed by package (and thus package path), not by an209 // importKey value. Two different importKey values may map to the same package210 // which is why we cannot use the check.impMap here.211 var pkgImports = make(map[*Package]bool)212 for _, imp := range pkg.imports {213 pkgImports[imp] = true214 }215216 type methodInfo struct {217 obj *Func // method218 ptr bool // true if pointer receiver219 recv *syntax.Name // receiver type name220 }221 var methods []methodInfo // collected methods with valid receivers and non-blank _ names222223 fileScopes := make([]*Scope, len(check.files)) // fileScopes[i] corresponds to check.files[i]224 for fileNo, file := range check.files {225 check.version = asGoVersion(check.versions[file.Pos().FileBase()])226227 // The package identifier denotes the current package,228 // but there is no corresponding package object.229 check.recordDef(file.PkgName, nil)230231 fileScope := NewScope(pkg.scope, syntax.StartPos(file), syntax.EndPos(file), check.filename(fileNo))232 fileScopes[fileNo] = fileScope233 check.recordScope(file, fileScope)234235 // determine file directory, necessary to resolve imports236 // FileName may be "" (typically for tests) in which case237 // we get "." as the directory which is what we would want.238 fileDir := dir(file.PkgName.Pos().RelFilename()) // TODO(gri) should this be filename?239240 first := -1 // index of first ConstDecl in the current group, or -1241 var last *syntax.ConstDecl // last ConstDecl with init expressions, or nil242 for index, decl := range file.DeclList {243 if _, ok := decl.(*syntax.ConstDecl); !ok {244 first = -1 // we're not in a constant declaration245 }246247 switch s := decl.(type) {248 case *syntax.ImportDecl:249 // import package250 if s.Path == nil || s.Path.Bad {251 continue // error reported during parsing252 }253 path, err := validatedImportPath(s.Path.Value)254 if err != nil {255 check.errorf(s.Path, BadImportPath, "invalid import path (%s)", err)256 continue257 }258259 imp := check.importPackage(s.Path.Pos(), path, fileDir)260 if imp == nil {261 continue262 }263264 // local name overrides imported package name265 name := imp.name266 if s.LocalPkgName != nil {267 name = s.LocalPkgName.Value268 if path == "C" {269 // match 1.17 cmd/compile (not prescribed by spec)270 check.error(s.LocalPkgName, ImportCRenamed, `cannot rename import "C"`)271 continue272 }273 }274275 if name == "init" {276 check.error(s, InvalidInitDecl, "cannot import package as init - init must be a func")277 continue278 }279280 // add package to list of explicit imports281 // (this functionality is provided as a convenience282 // for clients; it is not needed for type-checking)283 if !pkgImports[imp] {284 pkgImports[imp] = true285 pkg.imports = append(pkg.imports, imp)286 }287288 pkgName := NewPkgName(s.Pos(), pkg, name, imp)289 if s.LocalPkgName != nil {290 // in a dot-import, the dot represents the package291 check.recordDef(s.LocalPkgName, pkgName)292 } else {293 check.recordImplicit(s, pkgName)294 }295296 if imp.fake {297 // match 1.17 cmd/compile (not prescribed by spec)298 check.usedPkgNames[pkgName] = true299 }300301 // add import to file scope302 check.imports = append(check.imports, pkgName)303 if name == "." {304 // dot-import305 if check.dotImportMap == nil {306 check.dotImportMap = make(map[dotImportKey]*PkgName)307 }308 // merge imported scope with file scope309 for name, obj := range imp.scope.elems {310 // Note: Avoid eager resolve(name, obj) here, so we only311 // resolve dot-imported objects as needed.312313 // A package scope may contain non-exported objects,314 // do not import them!315 if isExported(name) {316 // declare dot-imported object317 // (Do not use check.declare because it modifies the object318 // via Object.setScopePos, which leads to a race condition;319 // the object may be imported into more than one file scope320 // concurrently. See go.dev/issue/32154.)321 if alt := fileScope.Lookup(name); alt != nil {322 err := check.newError(DuplicateDecl)323 err.addf(s.LocalPkgName, "%s redeclared in this block", alt.Name())324 err.addAltDecl(alt)325 err.report()326 } else {327 fileScope.insert(name, obj)328 check.dotImportMap[dotImportKey{fileScope, name}] = pkgName329 }330 }331 }332 } else {333 // declare imported package object in file scope334 // (no need to provide s.LocalPkgName since we called check.recordDef earlier)335 check.declare(fileScope, nil, pkgName, nopos)336 }337338 case *syntax.ConstDecl:339 // iota is the index of the current constDecl within the group340 if first < 0 || s.Group == nil || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {341 first = index342 last = nil343 }344 iota := constant.MakeInt64(int64(index - first))345346 // determine which initialization expressions to use347 inherited := true348 switch {349 case s.Type != nil || s.Values != nil:350 last = s351 inherited = false352 case last == nil:353 last = new(syntax.ConstDecl) // make sure last exists354 inherited = false355 }356357 // declare all constants358 values := syntax.UnpackListExpr(last.Values)359 for i, name := range s.NameList {360 obj := NewConst(name.Pos(), pkg, name.Value, nil, iota)361362 var init syntax.Expr363 if i < len(values) {364 init = values[i]365 }366367 d := &declInfo{file: fileScope, version: check.version, vtyp: last.Type, init: init, inherited: inherited}368 check.declarePkgObj(name, obj, d)369 }370371 // Constants must always have init values.372 check.arity(s.Pos(), s.NameList, values, true, inherited)373374 case *syntax.VarDecl:375 lhs := make([]*Var, len(s.NameList))376 // If there's exactly one rhs initializer, use377 // the same declInfo d1 for all lhs variables378 // so that each lhs variable depends on the same379 // rhs initializer (n:1 var declaration).380 var d1 *declInfo381 if _, ok := s.Values.(*syntax.ListExpr); !ok {382 // The lhs elements are only set up after the for loop below,383 // but that's ok because declarePkgObj only collects the declInfo384 // for a later phase.385 d1 = &declInfo{file: fileScope, version: check.version, lhs: lhs, vtyp: s.Type, init: s.Values}386 }387388 // declare all variables389 values := syntax.UnpackListExpr(s.Values)390 for i, name := range s.NameList {391 obj := newVar(PackageVar, name.Pos(), pkg, name.Value, nil)392 lhs[i] = obj393394 d := d1395 if d == nil {396 // individual assignments397 var init syntax.Expr398 if i < len(values) {399 init = values[i]400 }401 d = &declInfo{file: fileScope, version: check.version, vtyp: s.Type, init: init}402 }403404 check.declarePkgObj(name, obj, d)405 }406407 // If we have no type, we must have values.408 if s.Type == nil || values != nil {409 check.arity(s.Pos(), s.NameList, values, false, false)410 }411412 case *syntax.TypeDecl:413 obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)414 check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, version: check.version, tdecl: s})415416 case *syntax.FuncDecl:417 name := s.Name.Value418 obj := NewFunc(s.Name.Pos(), pkg, name, nil) // signature set later419 var tparam0 *syntax.Field420 if len(s.TParamList) > 0 {421 tparam0 = s.TParamList[0]422 }423 if s.Recv == nil {424 // regular function425 if name == "init" || name == "main" && pkg.name == "main" {426 // init and main functions must not declare type and ordinary parameters or results427 code := InvalidInitDecl428 if name == "main" {429 code = InvalidMainDecl430 }431 if tparam0 != nil {432 check.softErrorf(tparam0, code, "func %s must have no type parameters", name)433 }434 if t := s.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {435 check.softErrorf(s.Name, code, "func %s must have no arguments and no return values", name)436 }437 } else {438 _ = tparam0 != nil && check.verifyVersionf(tparam0, go1_18, "type parameter")439 }440 // don't declare init functions in the package scope - they are invisible441 if name == "init" {442 obj.parent = pkg.scope443 check.recordDef(s.Name, obj)444 if s.Body == nil {445 check.softErrorf(obj.pos, MissingInitBody, "func init must have a body")446 }447 } else {448 check.declare(pkg.scope, s.Name, obj, nopos)449 }450 } else {451 // method452 // d.Recv != nil453 ptr, base, _ := check.unpackRecv(s.Recv.Type, false)454 // Methods with invalid receiver cannot be associated to a type, and455 // methods with blank _ names are never found; no need to collect any456 // of them. They will still be type-checked with all the other functions.457 if recv, _ := base.(*syntax.Name); recv != nil && name != "_" {458 methods = append(methods, methodInfo{obj, ptr, recv})459 }460 _ = tparam0 != nil && check.verifyVersionf(tparam0, go1_27, "generic method")461 check.recordDef(s.Name, obj)462 }463 info := &declInfo{file: fileScope, version: check.version, fdecl: s}464 // Methods are not package-level objects but we still track them in the465 // object map so that we can handle them like regular functions (if the466 // receiver is invalid); also we need their fdecl info when associating467 // them with their receiver base type, below.468 check.objMap[obj] = info469 obj.setOrder(uint32(len(check.objMap)))470471 default:472 check.errorf(s, InvalidSyntaxTree, "unknown syntax.Decl node %T", s)473 }474 }475 }476477 // verify that objects in package and file scopes have different names478 for _, scope := range fileScopes {479 for name, obj := range scope.elems {480 if alt := pkg.scope.Lookup(name); alt != nil {481 obj = resolve(name, obj)482 err := check.newError(DuplicateDecl)483 if pkg, ok := obj.(*PkgName); ok {484 err.addf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported())485 err.addAltDecl(pkg)486 } else {487 err.addf(alt, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())488 // TODO(gri) dot-imported objects don't have a position; addAltDecl won't print anything489 err.addAltDecl(obj)490 }491 err.report()492 }493 }494 }495496 // Now that we have all package scope objects and all methods,497 // associate methods with receiver base type name where possible.498 // Ignore methods that have an invalid receiver. They will be499 // type-checked later, with regular functions.500 if methods == nil {501 return502 }503504 check.methods = make(map[*TypeName][]*Func)505 for i := range methods {506 m := &methods[i]507 // Determine the receiver base type and associate m with it.508 ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)509 if base != nil {510 m.obj.hasPtrRecv_ = ptr511 check.methods[base] = append(check.methods[base], m.obj)512 }513 }514}515516// sortObjects sorts package-level objects by source-order for reproducible processing517func (check *Checker) sortObjects() {518 check.objList = make([]Object, len(check.objMap))519 i := 0520 for obj := range check.objMap {521 check.objList[i] = obj522 i++523 }524 slices.SortFunc(check.objList, func(a, b Object) int {525 return cmp.Compare(a.order(), b.order())526 })527}528529// unpackRecv unpacks a receiver type expression and returns its components: ptr indicates530// whether rtyp is a pointer receiver, base is the receiver base type expression stripped531// of its type parameters (if any), and tparams are its type parameter names, if any. The532// type parameters are only unpacked if unpackParams is set. For instance, given the rtyp533//534// *T[A, _]535//536// ptr is true, base is T, and tparams is [A, _] (assuming unpackParams is set).537// Note that base may not be a *syntax.Name for erroneous programs.538func (check *Checker) unpackRecv(rtyp syntax.Expr, unpackParams bool) (ptr bool, base syntax.Expr, tparams []*syntax.Name) {539 // unpack receiver type540 base = syntax.Unparen(rtyp)541 if t, _ := base.(*syntax.Operation); t != nil && t.Op == syntax.Mul && t.Y == nil {542 ptr = true543 base = syntax.Unparen(t.X)544 }545546 // unpack type parameters, if any547 if ptyp, _ := base.(*syntax.IndexExpr); ptyp != nil {548 base = ptyp.X549 if unpackParams {550 for _, arg := range syntax.UnpackListExpr(ptyp.Index) {551 var par *syntax.Name552 switch arg := arg.(type) {553 case *syntax.Name:554 par = arg555 case *syntax.BadExpr:556 // ignore - error already reported by parser557 case nil:558 check.error(ptyp, InvalidSyntaxTree, "parameterized receiver contains nil parameters")559 default:560 check.errorf(arg, BadDecl, "receiver type parameter %s must be an identifier", arg)561 }562 if par == nil {563 par = syntax.NewName(arg.Pos(), "_")564 }565 tparams = append(tparams, par)566 }567568 }569 }570571 return572}573574// resolveBaseTypeName returns the non-alias base type name for the given name, and whether575// there was a pointer indirection to get to it. The base type name must be declared576// in package scope, and there can be at most one pointer indirection. Traversals577// through generic alias types are not permitted. If no such type name exists, the578// returned base is nil.579func (check *Checker) resolveBaseTypeName(ptr bool, name *syntax.Name) (ptr_ bool, base *TypeName) {580 // Algorithm: Starting from name, which is expected to denote a type,581 // we follow that type through non-generic alias declarations until582 // we reach a non-alias type name.583 var seen map[*TypeName]bool584 for name != nil {585 // name must denote an object found in the current package scope586 // (note that dot-imported objects are not in the package scope!)587 obj := check.pkg.scope.Lookup(name.Value)588 if obj == nil {589 break590 }591592 // the object must be a type name...593 tname, _ := obj.(*TypeName)594 if tname == nil {595 break596 }597598 // ... which we have not seen before599 if seen[tname] {600 break601 }602603 // we're done if tdecl describes a defined type (not an alias)604 tdecl := check.objMap[tname].tdecl // must exist for objects in package scope605 if !tdecl.Alias {606 return ptr, tname607 }608609 // an alias must not be generic610 // (importantly, we must not collect such methods - was https://go.dev/issue/70417)611 if tdecl.TParamList != nil {612 break613 }614615 // otherwise, remember this type name and continue resolving616 if seen == nil {617 seen = make(map[*TypeName]bool)618 }619 seen[tname] = true620621 // The syntax parser strips unnecessary parentheses; call Unparen for consistency with go/types.622 typ := syntax.Unparen(tdecl.Type)623624 // dereference a pointer type625 if pexpr, _ := typ.(*syntax.Operation); pexpr != nil && pexpr.Op == syntax.Mul && pexpr.Y == nil {626 // if we've already seen a pointer, we're done627 if ptr {628 break629 }630 ptr = true631 typ = syntax.Unparen(pexpr.X) // continue with pointer base type632 }633634 // After dereferencing, typ must be a locally defined type name.635 // Referring to other packages (qualified identifiers) or going636 // through instantiated types (index expressions) is not permitted,637 // so we can ignore those.638 name, _ = typ.(*syntax.Name)639 }640641 // no base type found642 return false, nil643}644645// packageObjects typechecks all package objects, but not function bodies.646func (check *Checker) packageObjects() {647 // add new methods to already type-checked types (from a prior Checker.Files call)648 for _, obj := range check.objList {649 if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {650 check.collectMethods(obj)651 }652 }653654 if false {655 // TODO: determine if we can enable this code now or656 // if there are still problems with cycles and657 // aliases.658 //659 // For example, in GOROOT/test/typeparam/issue50259.go,660 //661 // type T[_ any] struct{}662 // type A T[B]663 // type B = T[A]664 //665 // TypeName A has Type Named during checking, but by666 // the time the unified export data is written out,667 // its Type is Invalid.668 //669 // Investigate and reenable this branch.670 for _, obj := range check.objList {671 check.objDecl(obj)672 }673 } else {674 // To avoid problems with cycles, process non-alias type declarations first, followed by675 // alias declarations, and then everything else. This appears to avoid most situations676 // where the type of an alias is needed before it is available.677 // There may still be cases where this is not good enough (see also go.dev/issue/25838).678 // In those cases Checker.ident will report an error ("invalid use of type alias").679 var aliasList []*TypeName680 var othersList []Object // everything that's not a type681 // phase 1: non-alias type declarations682 for _, obj := range check.objList {683 if tname, _ := obj.(*TypeName); tname != nil {684 if check.objMap[tname].tdecl.Alias {685 aliasList = append(aliasList, tname)686 } else {687 check.objDecl(obj)688 }689 } else {690 othersList = append(othersList, obj)691 }692 }693 // phase 2: alias type declarations694 for _, obj := range aliasList {695 check.objDecl(obj)696 }697 // phase 3: all other declarations698 for _, obj := range othersList {699 check.objDecl(obj)700 }701 }702703 // At this point we may have a non-empty check.methods map; this means that not all704 // entries were deleted at the end of typeDecl because the respective receiver base705 // types were not found. In that case, an error was reported when declaring those706 // methods. We can now safely discard this map.707 check.methods = nil708}709710// unusedImports checks for unused imports.711func (check *Checker) unusedImports() {712 // If function bodies are not checked, packages' uses are likely missing - don't check.713 if check.conf.IgnoreFuncBodies {714 return715 }716717 // spec: "It is illegal (...) to directly import a package without referring to718 // any of its exported identifiers. To import a package solely for its side-effects719 // (initialization), use the blank identifier as explicit package name."720721 for _, obj := range check.imports {722 if obj.name != "_" && !check.usedPkgNames[obj] {723 check.errorUnusedPkg(obj)724 }725 }726}727728func (check *Checker) errorUnusedPkg(obj *PkgName) {729 // If the package was imported with a name other than the final730 // import path element, show it explicitly in the error message.731 // Note that this handles both renamed imports and imports of732 // packages containing unconventional package declarations.733 // Note that this uses / always, even on Windows, because Go import734 // paths always use forward slashes.735 path := obj.imported.path736 elem := path737 if i := strings.LastIndex(elem, "/"); i >= 0 {738 elem = elem[i+1:]739 }740 if obj.name == "" || obj.name == "." || obj.name == elem {741 check.softErrorf(obj, UnusedImport, "%q imported and not used", path)742 } else {743 check.softErrorf(obj, UnusedImport, "%q imported as %s and not used", path, obj.name)744 }745}746747// dir makes a good-faith attempt to return the directory748// portion of path. If path is empty, the result is ".".749// (Per the go/build package dependency tests, we cannot import750// path/filepath and simply use filepath.Dir.)751func dir(path string) string {752 if i := strings.LastIndexAny(path, `/\`); i > 0 {753 return path[:i]754 }755 // i <= 0756 return "."757}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.