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

View as plain text