Source file src/go/types/format.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 (error and trace) message formatting support.
     6  
     7  package types
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/token"
    14  	"strconv"
    15  	"strings"
    16  )
    17  
    18  // quote encloses s in `' quotes, as in `foo', except for _,
    19  // which is left alone.
    20  //
    21  // Use to prevent confusion when user supplied names alter the
    22  // meaning of an error message.
    23  //
    24  // For instance, report
    25  //
    26  //	duplicate method `wanted'
    27  //
    28  // rather than
    29  //
    30  //	duplicate method wanted
    31  //
    32  // Exceptions:
    33  //
    34  //   - don't quote _:
    35  //     `_' is ugly and not necessary
    36  //   - don't quote after a ":" as there's no need for it:
    37  //     undefined name: foo
    38  //   - don't quote if the name is used correctly in a statement:
    39  //     goto L jumps over variable declaration
    40  //
    41  // quote encloses s in `' quotes, as in `foo',
    42  // except for _ which is left alone.
    43  func quote(s string) string {
    44  	if s == "_" {
    45  		// `_' is ugly and not necessary
    46  		return s
    47  	}
    48  	return "`" + s + "'"
    49  }
    50  
    51  func sprintf(fset *token.FileSet, qf Qualifier, tpSubscripts bool, format string, args ...any) string {
    52  	for i, arg := range args {
    53  		switch a := arg.(type) {
    54  		case nil:
    55  			arg = "<nil>"
    56  		case operand:
    57  			panic("got operand instead of *operand")
    58  		case *operand:
    59  			arg = operandString(a, qf)
    60  		case token.Pos:
    61  			if fset != nil {
    62  				arg = fset.Position(a).String()
    63  			}
    64  		case ast.Expr:
    65  			arg = ExprString(a)
    66  		case []ast.Expr:
    67  			var buf bytes.Buffer
    68  			buf.WriteByte('[')
    69  			writeExprList(&buf, a)
    70  			buf.WriteByte(']')
    71  			arg = buf.String()
    72  		case Object:
    73  			arg = ObjectString(a, qf)
    74  		case Type:
    75  			var buf bytes.Buffer
    76  			w := newTypeWriter(&buf, qf)
    77  			w.tpSubscripts = tpSubscripts
    78  			w.typ(a)
    79  			arg = buf.String()
    80  		case []Type:
    81  			var buf bytes.Buffer
    82  			w := newTypeWriter(&buf, qf)
    83  			w.tpSubscripts = tpSubscripts
    84  			buf.WriteByte('[')
    85  			for i, x := range a {
    86  				if i > 0 {
    87  					buf.WriteString(", ")
    88  				}
    89  				w.typ(x)
    90  			}
    91  			buf.WriteByte(']')
    92  			arg = buf.String()
    93  		case []*TypeParam:
    94  			var buf bytes.Buffer
    95  			w := newTypeWriter(&buf, qf)
    96  			w.tpSubscripts = tpSubscripts
    97  			buf.WriteByte('[')
    98  			for i, x := range a {
    99  				if i > 0 {
   100  					buf.WriteString(", ")
   101  				}
   102  				w.typ(x)
   103  			}
   104  			buf.WriteByte(']')
   105  			arg = buf.String()
   106  		}
   107  		args[i] = arg
   108  	}
   109  	return fmt.Sprintf(format, args...)
   110  }
   111  
   112  // check may be nil.
   113  func (check *Checker) sprintf(format string, args ...any) string {
   114  	var fset *token.FileSet
   115  	var qf Qualifier
   116  	if check != nil {
   117  		fset = check.fset
   118  		qf = check.qualifier
   119  	}
   120  	return sprintf(fset, qf, false, format, args...)
   121  }
   122  
   123  func (check *Checker) trace(pos token.Pos, format string, args ...any) {
   124  	fmt.Printf("%s:\t%s%s\n",
   125  		check.fset.Position(pos),
   126  		strings.Repeat(".  ", check.indent),
   127  		sprintf(check.fset, check.qualifier, true, format, args...),
   128  	)
   129  }
   130  
   131  // dump is only needed for debugging
   132  func (check *Checker) dump(format string, args ...any) {
   133  	fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
   134  }
   135  
   136  func (check *Checker) qualifier(pkg *Package) string {
   137  	// Qualify the package unless it's the package being type-checked.
   138  	if pkg != check.pkg {
   139  		if check.pkgPathMap == nil {
   140  			check.pkgPathMap = make(map[string]map[string]bool)
   141  			check.seenPkgMap = make(map[*Package]bool)
   142  			check.markImports(check.pkg)
   143  		}
   144  		// If the same package name was used by multiple packages, display the full path.
   145  		if len(check.pkgPathMap[pkg.name]) > 1 {
   146  			return strconv.Quote(pkg.path)
   147  		}
   148  		return pkg.name
   149  	}
   150  	return ""
   151  }
   152  
   153  // markImports recursively walks pkg and its imports, to record unique import
   154  // paths in pkgPathMap.
   155  func (check *Checker) markImports(pkg *Package) {
   156  	if check.seenPkgMap[pkg] {
   157  		return
   158  	}
   159  	check.seenPkgMap[pkg] = true
   160  
   161  	forName, ok := check.pkgPathMap[pkg.name]
   162  	if !ok {
   163  		forName = make(map[string]bool)
   164  		check.pkgPathMap[pkg.name] = forName
   165  	}
   166  	forName[pkg.path] = true
   167  
   168  	for _, imp := range pkg.imports {
   169  		check.markImports(imp)
   170  	}
   171  }
   172  
   173  // stripAnnotations removes internal (type) annotations from s.
   174  func stripAnnotations(s string) string {
   175  	var buf strings.Builder
   176  	for _, r := range s {
   177  		// strip #'s and subscript digits
   178  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   179  			buf.WriteRune(r)
   180  		}
   181  	}
   182  	if buf.Len() < len(s) {
   183  		return buf.String()
   184  	}
   185  	return s
   186  }
   187  

View as plain text