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

View as plain text