blob: 1b9cf606b7771ce53bc42af2df945f863eb528c5 [file] [log] [blame]
// 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 (error and trace) message formatting support.
package types2
import (
"bytes"
"cmd/compile/internal/syntax"
"fmt"
"strconv"
"strings"
)
// quote encloses s in `' quotes, as in `foo', except for _,
// which is left alone.
//
// Use to prevent confusion when user supplied names alter the
// meaning of an error message.
//
// For instance, report
//
// duplicate method `wanted'
//
// rather than
//
// duplicate method wanted
//
// Exceptions:
//
// - don't quote _:
// `_' is ugly and not necessary
// - don't quote after a ":" as there's no need for it:
// undefined name: foo
// - don't quote if the name is used correctly in a statement:
// goto L jumps over variable declaration
//
// quote encloses s in `' quotes, as in `foo',
// except for _ which is left alone.
func quote(s string) string {
if s == "_" {
// `_' is ugly and not necessary
return s
}
return "`" + s + "'"
}
func sprintf(qf Qualifier, tpSubscripts bool, format string, args ...any) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
arg = "<nil>"
case operand:
panic("got operand instead of *operand")
case *operand:
arg = operandString(a, qf)
case syntax.Pos:
arg = a.String()
case syntax.Expr:
arg = ExprString(a)
case []syntax.Expr:
var buf strings.Builder
buf.WriteByte('[')
for i, x := range a {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(ExprString(x))
}
buf.WriteByte(']')
arg = buf.String()
case Object:
arg = ObjectString(a, qf)
case Type:
var buf bytes.Buffer
w := newTypeWriter(&buf, qf)
w.tpSubscripts = tpSubscripts
w.typ(a)
arg = buf.String()
case []Type:
var buf bytes.Buffer
w := newTypeWriter(&buf, qf)
w.tpSubscripts = tpSubscripts
buf.WriteByte('[')
for i, x := range a {
if i > 0 {
buf.WriteString(", ")
}
w.typ(x)
}
buf.WriteByte(']')
arg = buf.String()
case []*TypeParam:
var buf bytes.Buffer
w := newTypeWriter(&buf, qf)
w.tpSubscripts = tpSubscripts
buf.WriteByte('[')
for i, x := range a {
if i > 0 {
buf.WriteString(", ")
}
w.typ(x)
}
buf.WriteByte(']')
arg = buf.String()
}
args[i] = arg
}
return fmt.Sprintf(format, args...)
}
// check may be nil.
func (check *Checker) sprintf(format string, args ...any) string {
var qf Qualifier
if check != nil {
qf = check.qualifier
}
return sprintf(qf, false, format, args...)
}
func (check *Checker) trace(pos syntax.Pos, format string, args ...any) {
fmt.Printf("%s:\t%s%s\n",
pos,
strings.Repeat(". ", check.indent),
sprintf(check.qualifier, true, format, args...),
)
}
// dump is only needed for debugging
func (check *Checker) dump(format string, args ...any) {
fmt.Println(sprintf(check.qualifier, true, format, args...))
}
func (check *Checker) qualifier(pkg *Package) string {
// Qualify the package unless it's the package being type-checked.
if pkg != check.pkg {
if check.pkgPathMap == nil {
check.pkgPathMap = make(map[string]map[string]bool)
check.seenPkgMap = make(map[*Package]bool)
check.markImports(check.pkg)
}
// If the same package name was used by multiple packages, display the full path.
if len(check.pkgPathMap[pkg.name]) > 1 {
return strconv.Quote(pkg.path)
}
return pkg.name
}
return ""
}
// markImports recursively walks pkg and its imports, to record unique import
// paths in pkgPathMap.
func (check *Checker) markImports(pkg *Package) {
if check.seenPkgMap[pkg] {
return
}
check.seenPkgMap[pkg] = true
forName, ok := check.pkgPathMap[pkg.name]
if !ok {
forName = make(map[string]bool)
check.pkgPathMap[pkg.name] = forName
}
forName[pkg.path] = true
for _, imp := range pkg.imports {
check.markImports(imp)
}
}
// stripAnnotations removes internal (type) annotations from s.
func stripAnnotations(s string) string {
var buf strings.Builder
for _, r := range s {
// strip #'s and subscript digits
if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
buf.WriteRune(r)
}
}
if buf.Len() < len(s) {
return buf.String()
}
return s
}