if isInterfacePtr(x.typ) {
                        why = check.interfacePtrError(x.typ)
                } else {
-                       why = check.sprintf("type %s has no field or method %s", x.typ, sel)
-                       // check if there's a field or method with different capitalization
-                       if obj, _, _ = lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel, true); obj != nil {
-                               var what string // empty or description with trailing space " " (default case, should never be reached)
-                               switch obj.(type) {
-                               case *Var:
-                                       what = "field "
-                               case *Func:
-                                       what = "method "
-                               }
-                               if samePkg(obj.Pkg(), check.pkg) || obj.Exported() {
-                                       why = check.sprintf("%s, but does have %s%s", why, what, obj.Name())
-                               } else if obj.Name() == sel {
-                                       why = check.sprintf("%s%s is not exported", what, obj.Name())
-                               }
-                       }
+                       alt, _, _ := lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel, true)
+                       why = check.lookupError(x.typ, sel, alt, false)
                }
                check.errorf(e.Sel, MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
                goto Error
 
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements support functions for error messages.
+
+package types2
+
+// lookupError returns a case-specific error when a lookup of selector sel in the
+// given type fails but an object with alternative spelling (case folding) is found.
+// If structLit is set, the error message is specifically for struct literal fields.
+func (check *Checker) lookupError(typ Type, sel string, obj Object, structLit bool) string {
+       // Provide more detail if there is an unexported object, or one with different capitalization.
+       // If selector and object are in the same package (==), export doesn't matter, otherwise (!=) it does.
+       // Messages depend on whether it's a general lookup or a field lookup in a struct literal.
+       //
+       // case           sel     pkg   have   message (examples for general lookup)
+       // ---------------------------------------------------------------------------------------------------------
+       // ok             x.Foo   ==    Foo
+       // misspelled     x.Foo   ==    FoO    type X has no field or method Foo, but does have field FoO
+       // misspelled     x.Foo   ==    foo    type X has no field or method Foo, but does have field foo
+       // misspelled     x.Foo   ==    foO    type X has no field or method Foo, but does have field foO
+       //
+       // misspelled     x.foo   ==    Foo    type X has no field or method foo, but does have field Foo
+       // misspelled     x.foo   ==    FoO    type X has no field or method foo, but does have field FoO
+       // ok             x.foo   ==    foo
+       // misspelled     x.foo   ==    foO    type X has no field or method foo, but does have field foO
+       //
+       // ok             x.Foo   !=    Foo
+       // misspelled     x.Foo   !=    FoO    type X has no field or method Foo, but does have field FoO
+       // unexported     x.Foo   !=    foo    type X has no field or method Foo, but does have unexported field foo
+       // missing        x.Foo   !=    foO    type X has no field or method Foo
+       //
+       // misspelled     x.foo   !=    Foo    type X has no field or method foo, but does have field Foo
+       // missing        x.foo   !=    FoO    type X has no field or method foo
+       // inaccessible   x.foo   !=    foo    cannot refer to unexported field foo
+       // missing        x.foo   !=    foO    type X has no field or method foo
+
+       const (
+               ok           = iota
+               missing      // no object found
+               misspelled   // found object with different spelling
+               unexported   // found object with name differing only in first letter
+               inaccessible // found object with matching name but inaccessible from the current package
+       )
+
+       // determine case
+       e := missing
+       var alt string // alternative spelling of selector; if any
+       if obj != nil {
+               alt = obj.Name()
+               if obj.Pkg() == check.pkg {
+                       assert(alt != sel) // otherwise there is no lookup error
+                       e = misspelled
+               } else if isExported(sel) {
+                       if isExported(alt) {
+                               e = misspelled
+                       } else if tail(sel) == tail(alt) {
+                               e = unexported
+                       }
+               } else if isExported(alt) {
+                       if tail(sel) == tail(alt) {
+                               e = misspelled
+                       }
+               } else if sel == alt {
+                       e = inaccessible
+               }
+       }
+
+       if structLit {
+               switch e {
+               case missing:
+                       return check.sprintf("unknown field %s in struct literal of type %s", sel, typ)
+               case misspelled:
+                       return check.sprintf("unknown field %s in struct literal of type %s, but does have %s", sel, typ, alt)
+               case unexported:
+                       return check.sprintf("unknown field %s in struct literal of type %s, but does have unexported %s", sel, typ, alt)
+               case inaccessible:
+                       return check.sprintf("cannot refer to unexported field %s in struct literal of type %s", alt, typ)
+               }
+       } else {
+               what := "object"
+               switch obj.(type) {
+               case *Var:
+                       what = "field"
+               case *Func:
+                       what = "method"
+               }
+               switch e {
+               case missing:
+                       return check.sprintf("type %s has no field or method %s", typ, sel)
+               case misspelled:
+                       return check.sprintf("type %s has no field or method %s, but does have %s %s", typ, sel, what, alt)
+               case unexported:
+                       return check.sprintf("type %s has no field or method %s, but does have unexported %s %s", typ, sel, what, alt)
+               case inaccessible:
+                       return check.sprintf("cannot refer to unexported %s %s", what, alt)
+               }
+       }
+
+       panic("unreachable")
+}
+
+// tail returns the string s without its first (UTF-8) character.
+// If len(s) == 0, the result is s.
+func tail(s string) string {
+       for i, _ := range s {
+               if i > 0 {
+                       return s[i:]
+               }
+       }
+       return s
+}
 
                                                check.errorf(kv, InvalidLitField, "invalid field name %s in struct literal", kv.Key)
                                                continue
                                        }
-                                       i := fieldIndex(utyp.fields, check.pkg, key.Value, false)
+                                       i := fieldIndex(fields, check.pkg, key.Value, false)
                                        if i < 0 {
-                                               check.errorf(kv.Key, MissingLitField, "unknown field %s in struct literal of type %s", key.Value, base)
+                                               var alt Object
+                                               if j := fieldIndex(fields, check.pkg, key.Value, true); j >= 0 {
+                                                       alt = fields[j]
+                                               }
+                                               msg := check.lookupError(base, key.Value, alt, true)
+                                               check.error(kv.Key, MissingLitField, msg)
                                                continue
                                        }
                                        fld := fields[i]
 
                if isInterfacePtr(x.typ) {
                        why = check.interfacePtrError(x.typ)
                } else {
-                       why = check.sprintf("type %s has no field or method %s", x.typ, sel)
-                       // check if there's a field or method with different capitalization
-                       if obj, _, _ = lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel, true); obj != nil {
-                               var what string // empty or description with trailing space " " (default case, should never be reached)
-                               switch obj.(type) {
-                               case *Var:
-                                       what = "field "
-                               case *Func:
-                                       what = "method "
-                               }
-                               if samePkg(obj.Pkg(), check.pkg) || obj.Exported() {
-                                       why = check.sprintf("%s, but does have %s%s", why, what, obj.Name())
-                               } else if obj.Name() == sel {
-                                       why = check.sprintf("%s%s is not exported", what, obj.Name())
-                               }
-                       }
+                       alt, _, _ := lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel, true)
+                       why = check.lookupError(x.typ, sel, alt, false)
                }
                check.errorf(e.Sel, MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
                goto Error
 
--- /dev/null
+// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
+
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements support functions for error messages.
+
+package types
+
+// lookupError returns a case-specific error when a lookup of selector sel in the
+// given type fails but an object with alternative spelling (case folding) is found.
+// If structLit is set, the error message is specifically for struct literal fields.
+func (check *Checker) lookupError(typ Type, sel string, obj Object, structLit bool) string {
+       // Provide more detail if there is an unexported object, or one with different capitalization.
+       // If selector and object are in the same package (==), export doesn't matter, otherwise (!=) it does.
+       // Messages depend on whether it's a general lookup or a field lookup in a struct literal.
+       //
+       // case           sel     pkg   have   message (examples for general lookup)
+       // ---------------------------------------------------------------------------------------------------------
+       // ok             x.Foo   ==    Foo
+       // misspelled     x.Foo   ==    FoO    type X has no field or method Foo, but does have field FoO
+       // misspelled     x.Foo   ==    foo    type X has no field or method Foo, but does have field foo
+       // misspelled     x.Foo   ==    foO    type X has no field or method Foo, but does have field foO
+       //
+       // misspelled     x.foo   ==    Foo    type X has no field or method foo, but does have field Foo
+       // misspelled     x.foo   ==    FoO    type X has no field or method foo, but does have field FoO
+       // ok             x.foo   ==    foo
+       // misspelled     x.foo   ==    foO    type X has no field or method foo, but does have field foO
+       //
+       // ok             x.Foo   !=    Foo
+       // misspelled     x.Foo   !=    FoO    type X has no field or method Foo, but does have field FoO
+       // unexported     x.Foo   !=    foo    type X has no field or method Foo, but does have unexported field foo
+       // missing        x.Foo   !=    foO    type X has no field or method Foo
+       //
+       // misspelled     x.foo   !=    Foo    type X has no field or method foo, but does have field Foo
+       // missing        x.foo   !=    FoO    type X has no field or method foo
+       // inaccessible   x.foo   !=    foo    cannot refer to unexported field foo
+       // missing        x.foo   !=    foO    type X has no field or method foo
+
+       const (
+               ok           = iota
+               missing      // no object found
+               misspelled   // found object with different spelling
+               unexported   // found object with name differing only in first letter
+               inaccessible // found object with matching name but inaccessible from the current package
+       )
+
+       // determine case
+       e := missing
+       var alt string // alternative spelling of selector; if any
+       if obj != nil {
+               alt = obj.Name()
+               if obj.Pkg() == check.pkg {
+                       assert(alt != sel) // otherwise there is no lookup error
+                       e = misspelled
+               } else if isExported(sel) {
+                       if isExported(alt) {
+                               e = misspelled
+                       } else if tail(sel) == tail(alt) {
+                               e = unexported
+                       }
+               } else if isExported(alt) {
+                       if tail(sel) == tail(alt) {
+                               e = misspelled
+                       }
+               } else if sel == alt {
+                       e = inaccessible
+               }
+       }
+
+       if structLit {
+               switch e {
+               case missing:
+                       return check.sprintf("unknown field %s in struct literal of type %s", sel, typ)
+               case misspelled:
+                       return check.sprintf("unknown field %s in struct literal of type %s, but does have %s", sel, typ, alt)
+               case unexported:
+                       return check.sprintf("unknown field %s in struct literal of type %s, but does have unexported %s", sel, typ, alt)
+               case inaccessible:
+                       return check.sprintf("cannot refer to unexported field %s in struct literal of type %s", alt, typ)
+               }
+       } else {
+               what := "object"
+               switch obj.(type) {
+               case *Var:
+                       what = "field"
+               case *Func:
+                       what = "method"
+               }
+               switch e {
+               case missing:
+                       return check.sprintf("type %s has no field or method %s", typ, sel)
+               case misspelled:
+                       return check.sprintf("type %s has no field or method %s, but does have %s %s", typ, sel, what, alt)
+               case unexported:
+                       return check.sprintf("type %s has no field or method %s, but does have unexported %s %s", typ, sel, what, alt)
+               case inaccessible:
+                       return check.sprintf("cannot refer to unexported %s %s", what, alt)
+               }
+       }
+
+       panic("unreachable")
+}
+
+// tail returns the string s without its first (UTF-8) character.
+// If len(s) == 0, the result is s.
+func tail(s string) string {
+       for i, _ := range s {
+               if i > 0 {
+                       return s[i:]
+               }
+       }
+       return s
+}
 
                                        }
                                        i := fieldIndex(utyp.fields, check.pkg, key.Name, false)
                                        if i < 0 {
-                                               check.errorf(kv, MissingLitField, "unknown field %s in struct literal of type %s", key.Name, base)
+                                               var alt Object
+                                               if j := fieldIndex(fields, check.pkg, key.Name, true); j >= 0 {
+                                                       alt = fields[j]
+                                               }
+                                               msg := check.lookupError(base, key.Name, alt, true)
+                                               check.error(kv.Key, MissingLitField, msg)
                                                continue
                                        }
                                        fld := fields[i]
 
        "const.go":          func(f *ast.File) { fixTokenPos(f) },
        "context.go":        nil,
        "context_test.go":   nil,
+       "errsupport.go":     nil,
        "gccgosizes.go":     nil,
        "gcsizes.go":        func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
        "hilbert_test.go":   func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
 
 func _() {
        var x big.Float
        _ = x.neg // ERROR "x.neg undefined (type big.Float has no field or method neg, but does have method Neg)"
-       _ = x.nEg // ERROR "x.nEg undefined (type big.Float has no field or method nEg, but does have method Neg)"
+       _ = x.nEg // ERROR "x.nEg undefined (type big.Float has no field or method nEg)"
        _ = x.Neg
        _ = x.NEg // ERROR "x.NEg undefined (type big.Float has no field or method NEg, but does have method Neg)"
 
-       _ = x.form // ERROR "x.form undefined (field form is not exported)"
+       _ = x.form // ERROR "x.form undefined (cannot refer to unexported field form)"
        _ = x.fOrm // ERROR "x.fOrm undefined (type big.Float has no field or method fOrm)"
-       _ = x.Form // ERROR "x.Form undefined (type big.Float has no field or method Form)"
+       _ = x.Form // ERROR "x.Form undefined (type big.Float has no field or method Form, but does have unexported field form)"
        _ = x.FOrm // ERROR "x.FOrm undefined (type big.Float has no field or method FOrm)"
 }
 
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import (
+       "go/ast"
+       "math/big"
+)
+
+// case           sel     pkg   have   message (examples for general lookup)
+// ---------------------------------------------------------------------------------------------------------
+// ok             x.Foo   ==    Foo
+// misspelled     x.Foo   ==    FoO    type X has no field or method Foo, but does have field FoO
+// misspelled     x.Foo   ==    foo    type X has no field or method Foo, but does have field foo
+// misspelled     x.Foo   ==    foO    type X has no field or method Foo, but does have field foO
+//
+// misspelled     x.foo   ==    Foo    type X has no field or method foo, but does have field Foo
+// misspelled     x.foo   ==    FoO    type X has no field or method foo, but does have field FoO
+// ok             x.foo   ==    foo
+// misspelled     x.foo   ==    foO    type X has no field or method foo, but does have field foO
+//
+// ok             x.Foo   !=    Foo
+// misspelled     x.Foo   !=    FoO    type X has no field or method Foo, but does have field FoO
+// unexported     x.Foo   !=    foo    type X has no field or method Foo, but does have unexported field foo
+// missing        x.Foo   !=    foO    type X has no field or method Foo
+//
+// misspelled     x.foo   !=    Foo    type X has no field or method foo, but does have field Foo
+// missing        x.foo   !=    FoO    type X has no field or method foo
+// inaccessible   x.foo   !=    foo    cannot refer to unexported field foo
+// missing        x.foo   !=    foO    type X has no field or method foo
+
+type S struct {
+       Foo1 int
+       FoO2 int
+       foo3 int
+       foO4 int
+}
+
+func _() {
+       var x S
+       _ = x.Foo1 // OK
+       _ = x.Foo2 // ERROR "x.Foo2 undefined (type S has no field or method Foo2, but does have field FoO2)"
+       _ = x.Foo3 // ERROR "x.Foo3 undefined (type S has no field or method Foo3, but does have field foo3)"
+       _ = x.Foo4 // ERROR "x.Foo4 undefined (type S has no field or method Foo4, but does have field foO4)"
+
+       _ = x.foo1 // ERROR "x.foo1 undefined (type S has no field or method foo1, but does have field Foo1)"
+       _ = x.foo2 // ERROR "x.foo2 undefined (type S has no field or method foo2, but does have field FoO2)"
+       _ = x.foo3 // OK
+       _ = x.foo4 // ERROR "x.foo4 undefined (type S has no field or method foo4, but does have field foO4)"
+}
+
+func _() {
+       _ = S{Foo1: 0} // OK
+       _ = S{Foo2 /* ERROR "unknown field Foo2 in struct literal of type S, but does have FoO2" */ : 0}
+       _ = S{Foo3 /* ERROR "unknown field Foo3 in struct literal of type S, but does have foo3" */ : 0}
+       _ = S{Foo4 /* ERROR "unknown field Foo4 in struct literal of type S, but does have foO4" */ : 0}
+
+       _ = S{foo1 /* ERROR "unknown field foo1 in struct literal of type S, but does have Foo1" */ : 0}
+       _ = S{foo2 /* ERROR "unknown field foo2 in struct literal of type S, but does have FoO2" */ : 0}
+       _ = S{foo3: 0} // OK
+       _ = S{foo4 /* ERROR "unknown field foo4 in struct literal of type S, but does have foO4" */ : 0}
+}
+
+// The following tests follow the same pattern as above but operate on an imported type instead of S.
+// Currently our testing framework doesn't make it easy to define an imported package for testing, so
+// instead we use the big.Float and ast.File types as they provide a suitable mix of exported and un-
+// exported fields and methods.
+
+func _() {
+       var x *big.Float
+       _ = x.Neg  // OK
+       _ = x.NeG  // ERROR "x.NeG undefined (type *big.Float has no field or method NeG, but does have method Neg)"
+       _ = x.Form // ERROR "x.Form undefined (type *big.Float has no field or method Form, but does have unexported field form)"
+       _ = x.ForM // ERROR "x.ForM undefined (type *big.Float has no field or method ForM)"
+
+       _ = x.abs  // ERROR "x.abs undefined (type *big.Float has no field or method abs, but does have method Abs)"
+       _ = x.abS  // ERROR "x.abS undefined (type *big.Float has no field or method abS)"
+       _ = x.form // ERROR "x.form undefined (cannot refer to unexported field form)"
+       _ = x.forM // ERROR "x.forM undefined (type *big.Float has no field or method forM)"
+}
+
+func _() {
+       _ = ast.File{Name: nil} // OK
+       _ = ast.File{NamE /* ERROR "unknown field NamE in struct literal of type ast.File, but does have Name" */ : nil}
+       _ = big.Float{Form /* ERROR "unknown field Form in struct literal of type big.Float, but does have unexported form" */ : 0}
+       _ = big.Float{ForM /* ERROR "unknown field ForM in struct literal of type big.Float" */ : 0}
+
+       _ = ast.File{name /* ERROR "unknown field name in struct literal of type ast.File, but does have Name" */ : nil}
+       _ = ast.File{namE /* ERROR "unknown field namE in struct literal of type ast.File" */ : nil}
+       _ = big.Float{form /* ERROR "cannot refer to unexported field form in struct literal of type big.Float" */ : 0}
+       _ = big.Float{forM /* ERROR "unknown field forM in struct literal of type big.Float" */ : 0}
+}
 
--- /dev/null
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "math/big"
+
+// From go.dev/issue/18419
+func _(x *big.Float) {
+       x.form /* ERROR "x.form undefined (cannot refer to unexported field form)" */ ()
+}
+
+// From go.dev/issue/31053
+func _() {
+       _ = big.Float{form /* ERROR "cannot refer to unexported field form in struct literal of type big.Float" */ : 0}
+}
 
        i1 := it{Floats: true}
        if i1.floats { // ERROR "(type it .* field or method floats, but does have field Floats)|undefined field or method"
        }
-       i2 := &it{floats: false} // ERROR "(but does have field Floats)|unknown field|declared and not used"
+       i2 := &it{floats: false} // ERROR "cannot refer to unexported field floats in struct literal|unknown field|declared and not used"
        _ = &it{InneR: "foo"}    // ERROR "(but does have field inner)|unknown field"
        _ = i2
 }
 
 var s = http.Server{}
 var _ = s.doneChan                  // ERROR "s.doneChan undefined .cannot refer to unexported field or method doneChan.$|unexported field or method|s.doneChan undefined"
 var _ = s.DoneChan                  // ERROR "s.DoneChan undefined .type http.Server has no field or method DoneChan.$|undefined field or method"
-var _ = http.Server{tlsConfig: nil} // ERROR "unknown field tlsConfig in struct literal.+ .but does have TLSConfig.$|unknown field .?tlsConfig.? in .?http.Server|unknown field"
+var _ = http.Server{tlsConfig: nil} // ERROR "cannot refer to unexported field tlsConfig in struct literal|unknown field .?tlsConfig.? in .?http.Server|unknown field"
 var _ = http.Server{DoneChan: nil}  // ERROR "unknown field DoneChan in struct literal of type http.Server$|unknown field .?DoneChan.? in .?http.Server"
 
 type foo struct {
        bar int
 }
 
-var _ = &foo{bAr: 10} // ERROR "unknown field bAr in struct literal.+ .but does have bar.$|unknown field .?bAr.? in .?foo|unknown field"
+var _ = &foo{bAr: 10} // ERROR "cannot refer to unexported field bAr in struct literal|unknown field .?bAr.? in .?foo|unknown field"