Source file src/cmd/compile/internal/types2/errsupport.go

     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  

View as plain text