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 // This file implements support functions for error messages. 6 7 package types2 8 9 // lookupError returns a case-specific error when a lookup of selector sel in the 10 // given type fails but an object with alternative spelling (case folding) is found. 11 // If structLit is set, the error message is specifically for struct literal fields. 12 func (check *Checker) lookupError(typ Type, sel string, obj Object, structLit bool) string { 13 // Provide more detail if there is an unexported object, or one with different capitalization. 14 // If selector and object are in the same package (==), export doesn't matter, otherwise (!=) it does. 15 // Messages depend on whether it's a general lookup or a field lookup in a struct literal. 16 // 17 // case sel pkg have message (examples for general lookup) 18 // --------------------------------------------------------------------------------------------------------- 19 // ok x.Foo == Foo 20 // misspelled x.Foo == FoO type X has no field or method Foo, but does have field FoO 21 // misspelled x.Foo == foo type X has no field or method Foo, but does have field foo 22 // misspelled x.Foo == foO type X has no field or method Foo, but does have field foO 23 // 24 // misspelled x.foo == Foo type X has no field or method foo, but does have field Foo 25 // misspelled x.foo == FoO type X has no field or method foo, but does have field FoO 26 // ok x.foo == foo 27 // misspelled x.foo == foO type X has no field or method foo, but does have field foO 28 // 29 // ok x.Foo != Foo 30 // misspelled x.Foo != FoO type X has no field or method Foo, but does have field FoO 31 // unexported x.Foo != foo type X has no field or method Foo, but does have unexported field foo 32 // missing x.Foo != foO type X has no field or method Foo 33 // 34 // misspelled x.foo != Foo type X has no field or method foo, but does have field Foo 35 // missing x.foo != FoO type X has no field or method foo 36 // inaccessible x.foo != foo cannot refer to unexported field foo 37 // missing x.foo != foO type X has no field or method foo 38 39 const ( 40 ok = iota 41 missing // no object found 42 misspelled // found object with different spelling 43 unexported // found object with name differing only in first letter 44 inaccessible // found object with matching name but inaccessible from the current package 45 ) 46 47 // determine case 48 e := missing 49 var alt string // alternative spelling of selector; if any 50 if obj != nil { 51 alt = obj.Name() 52 if obj.Pkg() == check.pkg { 53 assert(alt != sel) // otherwise there is no lookup error 54 e = misspelled 55 } else if isExported(sel) { 56 if isExported(alt) { 57 e = misspelled 58 } else if tail(sel) == tail(alt) { 59 e = unexported 60 } 61 } else if isExported(alt) { 62 if tail(sel) == tail(alt) { 63 e = misspelled 64 } 65 } else if sel == alt { 66 e = inaccessible 67 } 68 } 69 70 if structLit { 71 switch e { 72 case missing: 73 return check.sprintf("unknown field %s in struct literal of type %s", sel, typ) 74 case misspelled: 75 return check.sprintf("unknown field %s in struct literal of type %s, but does have %s", sel, typ, alt) 76 case unexported: 77 return check.sprintf("unknown field %s in struct literal of type %s, but does have unexported %s", sel, typ, alt) 78 case inaccessible: 79 return check.sprintf("cannot refer to unexported field %s in struct literal of type %s", alt, typ) 80 } 81 } else { 82 what := "object" 83 switch obj.(type) { 84 case *Var: 85 what = "field" 86 case *Func: 87 what = "method" 88 } 89 switch e { 90 case missing: 91 return check.sprintf("type %s has no field or method %s", typ, sel) 92 case misspelled: 93 return check.sprintf("type %s has no field or method %s, but does have %s %s", typ, sel, what, alt) 94 case unexported: 95 return check.sprintf("type %s has no field or method %s, but does have unexported %s %s", typ, sel, what, alt) 96 case inaccessible: 97 return check.sprintf("cannot refer to unexported %s %s", what, alt) 98 } 99 } 100 101 panic("unreachable") 102 } 103 104 // tail returns the string s without its first (UTF-8) character. 105 // If len(s) == 0, the result is s. 106 func tail(s string) string { 107 for i, _ := range s { 108 if i > 0 { 109 return s[i:] 110 } 111 } 112 return s 113 } 114