Source file src/cmd/go/internal/load/godebug.go

     1  // Copyright 2023 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  package load
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"go/build"
    11  	"internal/godebugs"
    12  	"maps"
    13  	"sort"
    14  	"strconv"
    15  	"strings"
    16  
    17  	"cmd/go/internal/fips140"
    18  	"cmd/go/internal/gover"
    19  	"cmd/go/internal/modload"
    20  )
    21  
    22  var ErrNotGoDebug = errors.New("not //go:debug line")
    23  
    24  func ParseGoDebug(text string) (key, value string, err error) {
    25  	if !strings.HasPrefix(text, "//go:debug") {
    26  		return "", "", ErrNotGoDebug
    27  	}
    28  	i := strings.IndexAny(text, " \t")
    29  	if i < 0 {
    30  		if strings.TrimSpace(text) == "//go:debug" {
    31  			return "", "", fmt.Errorf("missing key=value")
    32  		}
    33  		return "", "", ErrNotGoDebug
    34  	}
    35  	k, v, ok := strings.Cut(strings.TrimSpace(text[i:]), "=")
    36  	if !ok {
    37  		return "", "", fmt.Errorf("missing key=value")
    38  	}
    39  	if err := modload.CheckGodebug("//go:debug setting", k, v); err != nil {
    40  		return "", "", err
    41  	}
    42  	return k, v, nil
    43  }
    44  
    45  func defaultGODEBUGGoVersion(loaderstate *modload.State, p *Package) string {
    46  	if !loaderstate.Enabled() {
    47  		// GOPATH mode. Use Go 1.20.
    48  		return "1.20"
    49  	}
    50  	if loaderstate.RootMode == modload.NoRoot && p.Module != nil {
    51  		// This is go install pkg@version or go run pkg@version.
    52  		// Use the Go version from the package.
    53  		// If there isn't one, then assume Go 1.20, the last
    54  		// version before GODEBUGs were introduced (#56986).
    55  		if goVersion := p.Module.GoVersion; goVersion != "" {
    56  			return goVersion
    57  		}
    58  		return "1.20"
    59  	}
    60  	return loaderstate.MainModules.GoVersion(loaderstate)
    61  }
    62  
    63  // defaultGODEBUG returns the default GODEBUG setting for the main package p.
    64  // When building a test binary, directives, testDirectives, and xtestDirectives
    65  // list additional directives from the package under test.
    66  func defaultGODEBUG(loaderstate *modload.State, p *Package, directives, testDirectives, xtestDirectives []build.Directive) string {
    67  	if p.Name != "main" {
    68  		return ""
    69  	}
    70  
    71  	goVersion := defaultGODEBUGGoVersion(loaderstate, p)
    72  
    73  	var m map[string]string
    74  
    75  	// If GOFIPS140 is set to anything but "off",
    76  	// default to GODEBUG=fips140=on.
    77  	if fips140.Enabled() {
    78  		m = map[string]string{"fips140": "on"}
    79  	}
    80  
    81  	// Add directives from main module go.mod.
    82  	for _, g := range loaderstate.MainModules.Godebugs(loaderstate) {
    83  		if m == nil {
    84  			m = make(map[string]string)
    85  		}
    86  		m[g.Key] = g.Value
    87  	}
    88  
    89  	// Add directives from packages.
    90  	for _, list := range [][]build.Directive{p.Internal.Build.Directives, directives, testDirectives, xtestDirectives} {
    91  		for _, d := range list {
    92  			k, v, err := ParseGoDebug(d.Text)
    93  			if err != nil {
    94  				continue
    95  			}
    96  			if m == nil {
    97  				m = make(map[string]string)
    98  			}
    99  			m[k] = v
   100  		}
   101  	}
   102  	if v, ok := m["default"]; ok {
   103  		delete(m, "default")
   104  		v = strings.TrimPrefix(v, "go")
   105  		if gover.IsValid(v) {
   106  			goVersion = v
   107  		}
   108  	}
   109  
   110  	defaults := godebugForGoVersion(goVersion)
   111  	if defaults != nil {
   112  		// Apply m on top of defaults.
   113  		maps.Copy(defaults, m)
   114  		m = defaults
   115  	}
   116  
   117  	keys := make([]string, 0, len(m))
   118  	for k := range m {
   119  		keys = append(keys, k)
   120  	}
   121  	sort.Strings(keys)
   122  	var b strings.Builder
   123  	for _, k := range keys {
   124  		if b.Len() > 0 {
   125  			b.WriteString(",")
   126  		}
   127  		b.WriteString(k)
   128  		b.WriteString("=")
   129  		b.WriteString(m[k])
   130  	}
   131  	return b.String()
   132  }
   133  
   134  func godebugForGoVersion(v string) map[string]string {
   135  	if strings.Count(v, ".") >= 2 {
   136  		i := strings.Index(v, ".")
   137  		j := i + 1 + strings.Index(v[i+1:], ".")
   138  		v = v[:j]
   139  	}
   140  
   141  	if !strings.HasPrefix(v, "1.") {
   142  		return nil
   143  	}
   144  	n, err := strconv.Atoi(v[len("1."):])
   145  	if err != nil {
   146  		return nil
   147  	}
   148  
   149  	def := make(map[string]string)
   150  	for _, info := range godebugs.All {
   151  		if n < info.Changed {
   152  			def[info.Name] = info.Old
   153  		}
   154  	}
   155  	return def
   156  }
   157  

View as plain text