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 []*operand:
    28  			var buf strings.Builder
    29  			buf.WriteByte('[')
    30  			for i, x := range a {
    31  				if i > 0 {
    32  					buf.WriteString(", ")
    33  				}
    34  				buf.WriteString(operandString(x, qf))
    35  			}
    36  			buf.WriteByte(']')
    37  			arg = buf.String()
    38  		case token.Pos:
    39  			if fset != nil {
    40  				arg = fset.Position(a).String()
    41  			}
    42  		case ast.Expr:
    43  			arg = ExprString(a)
    44  		case []ast.Expr:
    45  			var buf bytes.Buffer
    46  			buf.WriteByte('[')
    47  			writeExprList(&buf, a)
    48  			buf.WriteByte(']')
    49  			arg = buf.String()
    50  		case Object:
    51  			arg = ObjectString(a, qf)
    52  		case Type:
    53  			var buf bytes.Buffer
    54  			w := newTypeWriter(&buf, qf)
    55  			w.tpSubscripts = tpSubscripts
    56  			w.typ(a)
    57  			arg = buf.String()
    58  		case []Type:
    59  			var buf bytes.Buffer
    60  			w := newTypeWriter(&buf, qf)
    61  			w.tpSubscripts = tpSubscripts
    62  			buf.WriteByte('[')
    63  			for i, x := range a {
    64  				if i > 0 {
    65  					buf.WriteString(", ")
    66  				}
    67  				w.typ(x)
    68  			}
    69  			buf.WriteByte(']')
    70  			arg = buf.String()
    71  		case []*TypeParam:
    72  			var buf bytes.Buffer
    73  			w := newTypeWriter(&buf, qf)
    74  			w.tpSubscripts = tpSubscripts
    75  			buf.WriteByte('[')
    76  			for i, x := range a {
    77  				if i > 0 {
    78  					buf.WriteString(", ")
    79  				}
    80  				w.typ(x)
    81  			}
    82  			buf.WriteByte(']')
    83  			arg = buf.String()
    84  		}
    85  		args[i] = arg
    86  	}
    87  	return fmt.Sprintf(format, args...)
    88  }
    89  
    90  // check may be nil.
    91  func (check *Checker) sprintf(format string, args ...any) string {
    92  	var fset *token.FileSet
    93  	var qf Qualifier
    94  	if check != nil {
    95  		fset = check.fset
    96  		qf = check.qualifier
    97  	}
    98  	return sprintf(fset, qf, false, format, args...)
    99  }
   100  
   101  func (check *Checker) trace(pos token.Pos, format string, args ...any) {
   102  	pos1 := check.fset.Position(pos)
   103  	// Use the width of line and pos values to align the ":" by adding padding before it.
   104  	// Cap padding at 5: 3 digits for the line, 2 digits for the column number, which is
   105  	// ok for most cases.
   106  	w := ndigits(pos1.Line) + ndigits(pos1.Column)
   107  	pad := "     "[:max(5-w, 0)]
   108  	fmt.Printf("%s%s:  %s%s\n",
   109  		pos1,
   110  		pad,
   111  		strings.Repeat(".  ", check.indent),
   112  		sprintf(check.fset, check.qualifier, true, format, args...),
   113  	)
   114  }
   115  
   116  // ndigits returns the number of decimal digits in x.
   117  // For x < 10, the result is always 1.
   118  // For x > 100, the result is always 3.
   119  func ndigits(x int) int {
   120  	switch {
   121  	case x < 10:
   122  		return 1
   123  	case x < 100:
   124  		return 2
   125  	default:
   126  		return 3
   127  	}
   128  }
   129  
   130  // dump is only needed for debugging
   131  func (check *Checker) dump(format string, args ...any) {
   132  	fmt.Println(sprintf(check.fset, check.qualifier, true, format, args...))
   133  }
   134  
   135  func (check *Checker) qualifier(pkg *Package) string {
   136  	// Qualify the package unless it's the package being type-checked.
   137  	if pkg != check.pkg {
   138  		if check.pkgPathMap == nil {
   139  			check.pkgPathMap = make(map[string]map[string]bool)
   140  			check.seenPkgMap = make(map[*Package]bool)
   141  			check.markImports(check.pkg)
   142  		}
   143  		// If the same package name was used by multiple packages, display the full path.
   144  		if len(check.pkgPathMap[pkg.name]) > 1 {
   145  			return strconv.Quote(pkg.path)
   146  		}
   147  		return pkg.name
   148  	}
   149  	return ""
   150  }
   151  
   152  // markImports recursively walks pkg and its imports, to record unique import
   153  // paths in pkgPathMap.
   154  func (check *Checker) markImports(pkg *Package) {
   155  	if check.seenPkgMap[pkg] {
   156  		return
   157  	}
   158  	check.seenPkgMap[pkg] = true
   159  
   160  	forName, ok := check.pkgPathMap[pkg.name]
   161  	if !ok {
   162  		forName = make(map[string]bool)
   163  		check.pkgPathMap[pkg.name] = forName
   164  	}
   165  	forName[pkg.path] = true
   166  
   167  	for _, imp := range pkg.imports {
   168  		check.markImports(imp)
   169  	}
   170  }
   171  
   172  // stripAnnotations removes internal (type) annotations from s.
   173  func stripAnnotations(s string) string {
   174  	var buf strings.Builder
   175  	for _, r := range s {
   176  		// strip #'s and subscript digits
   177  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   178  			buf.WriteRune(r)
   179  		}
   180  	}
   181  	if buf.Len() < len(s) {
   182  		return buf.String()
   183  	}
   184  	return s
   185  }
   186  

View as plain text