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  func sprintf(fset *token.FileSet, qf Qualifier, tpSubscripts bool, format string, args ...any) string {
    19  	for i, arg := range args {
    20  		switch a := arg.(type) {
    21  		case nil:
    22  			arg = "<nil>"
    23  		case operand:
    24  			panic("got operand instead of *operand")
    25  		case *operand:
    26  			arg = operandString(a, qf)
    27  		case token.Pos:
    28  			if fset != nil {
    29  				arg = fset.Position(a).String()
    30  			}
    31  		case ast.Expr:
    32  			arg = ExprString(a)
    33  		case []ast.Expr:
    34  			var buf bytes.Buffer
    35  			buf.WriteByte('[')
    36  			writeExprList(&buf, a)
    37  			buf.WriteByte(']')
    38  			arg = buf.String()
    39  		case Object:
    40  			arg = ObjectString(a, qf)
    41  		case Type:
    42  			var buf bytes.Buffer
    43  			w := newTypeWriter(&buf, qf)
    44  			w.tpSubscripts = tpSubscripts
    45  			w.typ(a)
    46  			arg = buf.String()
    47  		case []Type:
    48  			var buf bytes.Buffer
    49  			w := newTypeWriter(&buf, qf)
    50  			w.tpSubscripts = tpSubscripts
    51  			buf.WriteByte('[')
    52  			for i, x := range a {
    53  				if i > 0 {
    54  					buf.WriteString(", ")
    55  				}
    56  				w.typ(x)
    57  			}
    58  			buf.WriteByte(']')
    59  			arg = buf.String()
    60  		case []*TypeParam:
    61  			var buf bytes.Buffer
    62  			w := newTypeWriter(&buf, qf)
    63  			w.tpSubscripts = tpSubscripts
    64  			buf.WriteByte('[')
    65  			for i, x := range a {
    66  				if i > 0 {
    67  					buf.WriteString(", ")
    68  				}
    69  				w.typ(x)
    70  			}
    71  			buf.WriteByte(']')
    72  			arg = buf.String()
    73  		}
    74  		args[i] = arg
    75  	}
    76  	return fmt.Sprintf(format, args...)
    77  }
    78  
    79  // check may be nil.
    80  func (check *Checker) sprintf(format string, args ...any) string {
    81  	var fset *token.FileSet
    82  	var qf Qualifier
    83  	if check != nil {
    84  		fset = check.fset
    85  		qf = check.qualifier
    86  	}
    87  	return sprintf(fset, qf, false, format, args...)
    88  }
    89  
    90  func (check *Checker) trace(pos token.Pos, format string, args ...any) {
    91  	fmt.Printf("%s:\t%s%s\n",
    92  		check.fset.Position(pos),
    93  		strings.Repeat(".  ", check.indent),
    94  		sprintf(check.fset, check.qualifier, true, format, args...),
    95  	)
    96  }
    97  
    98  // dump is only needed for debugging
    99  func (check *Checker) dump(format string, args ...any) {
   100  	fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
   101  }
   102  
   103  func (check *Checker) qualifier(pkg *Package) string {
   104  	// Qualify the package unless it's the package being type-checked.
   105  	if pkg != check.pkg {
   106  		if check.pkgPathMap == nil {
   107  			check.pkgPathMap = make(map[string]map[string]bool)
   108  			check.seenPkgMap = make(map[*Package]bool)
   109  			check.markImports(check.pkg)
   110  		}
   111  		// If the same package name was used by multiple packages, display the full path.
   112  		if len(check.pkgPathMap[pkg.name]) > 1 {
   113  			return strconv.Quote(pkg.path)
   114  		}
   115  		return pkg.name
   116  	}
   117  	return ""
   118  }
   119  
   120  // markImports recursively walks pkg and its imports, to record unique import
   121  // paths in pkgPathMap.
   122  func (check *Checker) markImports(pkg *Package) {
   123  	if check.seenPkgMap[pkg] {
   124  		return
   125  	}
   126  	check.seenPkgMap[pkg] = true
   127  
   128  	forName, ok := check.pkgPathMap[pkg.name]
   129  	if !ok {
   130  		forName = make(map[string]bool)
   131  		check.pkgPathMap[pkg.name] = forName
   132  	}
   133  	forName[pkg.path] = true
   134  
   135  	for _, imp := range pkg.imports {
   136  		check.markImports(imp)
   137  	}
   138  }
   139  
   140  // stripAnnotations removes internal (type) annotations from s.
   141  func stripAnnotations(s string) string {
   142  	var buf strings.Builder
   143  	for _, r := range s {
   144  		// strip #'s and subscript digits
   145  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   146  			buf.WriteRune(r)
   147  		}
   148  	}
   149  	if buf.Len() < len(s) {
   150  		return buf.String()
   151  	}
   152  	return s
   153  }
   154  

View as plain text