github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/go/types/stdlib_test.go (about)

     1  // Copyright 2013 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 tests types.Check by using it to
     6  // typecheck the standard library and tests.
     7  
     8  package types_test
     9  
    10  import (
    11  	"fmt"
    12  	"go/ast"
    13  	"go/build"
    14  	"go/importer"
    15  	"go/parser"
    16  	"go/scanner"
    17  	"go/token"
    18  	"internal/testenv"
    19  	"os"
    20  	"path/filepath"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	. "go/types"
    26  )
    27  
    28  // The cmd/*/internal packages may have been deleted as part of a binary
    29  // release. Import from source instead.
    30  //
    31  // (See https://golang.org/issue/43232 and
    32  // https://github.com/golang/build/blob/df58bbac082bc87c4a3cdfe336d1ffe60bbaa916/cmd/release/release.go#L533-L545.)
    33  //
    34  // Use the same importer for all std lib tests to
    35  // avoid repeated importing of the same packages.
    36  var stdLibImporter = importer.ForCompiler(token.NewFileSet(), "source", nil)
    37  
    38  func TestStdlib(t *testing.T) {
    39  	testenv.MustHaveGoBuild(t)
    40  
    41  	pkgCount := 0
    42  	duration := walkPkgDirs(filepath.Join(testenv.GOROOT(t), "src"), func(dir string, filenames []string) {
    43  		typecheckFiles(t, dir, filenames)
    44  		pkgCount++
    45  	}, t.Error)
    46  
    47  	if testing.Verbose() {
    48  		fmt.Println(pkgCount, "packages typechecked in", duration)
    49  	}
    50  }
    51  
    52  // firstComment returns the contents of the first non-empty comment in
    53  // the given file, "skip", or the empty string. No matter the present
    54  // comments, if any of them contains a build tag, the result is always
    55  // "skip". Only comments before the "package" token and within the first
    56  // 4K of the file are considered.
    57  func firstComment(filename string) string {
    58  	f, err := os.Open(filename)
    59  	if err != nil {
    60  		return ""
    61  	}
    62  	defer f.Close()
    63  
    64  	var src [4 << 10]byte // read at most 4KB
    65  	n, _ := f.Read(src[:])
    66  
    67  	var first string
    68  	var s scanner.Scanner
    69  	s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil /* ignore errors */, scanner.ScanComments)
    70  	for {
    71  		_, tok, lit := s.Scan()
    72  		switch tok {
    73  		case token.COMMENT:
    74  			// remove trailing */ of multi-line comment
    75  			if lit[1] == '*' {
    76  				lit = lit[:len(lit)-2]
    77  			}
    78  			contents := strings.TrimSpace(lit[2:])
    79  			if strings.HasPrefix(contents, "+build ") {
    80  				return "skip"
    81  			}
    82  			if first == "" {
    83  				first = contents // contents may be "" but that's ok
    84  			}
    85  			// continue as we may still see build tags
    86  
    87  		case token.PACKAGE, token.EOF:
    88  			return first
    89  		}
    90  	}
    91  }
    92  
    93  func testTestDir(t *testing.T, path string, ignore ...string) {
    94  	files, err := os.ReadDir(path)
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	excluded := make(map[string]bool)
   100  	for _, filename := range ignore {
   101  		excluded[filename] = true
   102  	}
   103  
   104  	fset := token.NewFileSet()
   105  	for _, f := range files {
   106  		// filter directory contents
   107  		if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
   108  			continue
   109  		}
   110  
   111  		// get per-file instructions
   112  		expectErrors := false
   113  		filename := filepath.Join(path, f.Name())
   114  		goVersion := ""
   115  		if comment := firstComment(filename); comment != "" {
   116  			fields := strings.Fields(comment)
   117  			switch fields[0] {
   118  			case "skip", "compiledir":
   119  				continue // ignore this file
   120  			case "errorcheck":
   121  				expectErrors = true
   122  				for _, arg := range fields[1:] {
   123  					if arg == "-0" || arg == "-+" || arg == "-std" {
   124  						// Marked explicitly as not expecting errors (-0),
   125  						// or marked as compiling runtime/stdlib, which is only done
   126  						// to trigger runtime/stdlib-only error output.
   127  						// In both cases, the code should typecheck.
   128  						expectErrors = false
   129  						break
   130  					}
   131  					const prefix = "-lang="
   132  					if strings.HasPrefix(arg, prefix) {
   133  						goVersion = arg[len(prefix):]
   134  					}
   135  				}
   136  			}
   137  		}
   138  
   139  		// parse and type-check file
   140  		file, err := parser.ParseFile(fset, filename, nil, 0)
   141  		if err == nil {
   142  			conf := Config{
   143  				GoVersion: goVersion,
   144  				Importer:  stdLibImporter,
   145  			}
   146  			_, err = conf.Check(filename, fset, []*ast.File{file}, nil)
   147  		}
   148  
   149  		if expectErrors {
   150  			if err == nil {
   151  				t.Errorf("expected errors but found none in %s", filename)
   152  			}
   153  		} else {
   154  			if err != nil {
   155  				t.Error(err)
   156  			}
   157  		}
   158  	}
   159  }
   160  
   161  func TestStdTest(t *testing.T) {
   162  	testenv.MustHaveGoBuild(t)
   163  
   164  	if testing.Short() && testenv.Builder() == "" {
   165  		t.Skip("skipping in short mode")
   166  	}
   167  
   168  	testTestDir(t, filepath.Join(testenv.GOROOT(t), "test"),
   169  		"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
   170  		"directive.go",   // tests compiler rejection of bad directive placement - ignore
   171  		"directive2.go",  // tests compiler rejection of bad directive placement - ignore
   172  		"embedfunc.go",   // tests //go:embed
   173  		"embedvers.go",   // tests //go:embed
   174  		"linkname2.go",   // go/types doesn't check validity of //go:xxx directives
   175  		"linkname3.go",   // go/types doesn't check validity of //go:xxx directives
   176  	)
   177  }
   178  
   179  func TestStdFixed(t *testing.T) {
   180  	testenv.MustHaveGoBuild(t)
   181  
   182  	if testing.Short() && testenv.Builder() == "" {
   183  		t.Skip("skipping in short mode")
   184  	}
   185  
   186  	testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "fixedbugs"),
   187  		"bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
   188  		"issue6889.go",   // gc-specific test
   189  		"issue11362.go",  // canonical import path check
   190  		"issue16369.go",  // go/types handles this correctly - not an issue
   191  		"issue18459.go",  // go/types doesn't check validity of //go:xxx directives
   192  		"issue18882.go",  // go/types doesn't check validity of //go:xxx directives
   193  		"issue20529.go",  // go/types does not have constraints on stack size
   194  		"issue22200.go",  // go/types does not have constraints on stack size
   195  		"issue22200b.go", // go/types does not have constraints on stack size
   196  		"issue25507.go",  // go/types does not have constraints on stack size
   197  		"issue20780.go",  // go/types does not have constraints on stack size
   198  		"bug251.go",      // go.dev/issue/34333 which was exposed with fix for go.dev/issue/34151
   199  		"issue42058a.go", // go/types does not have constraints on channel element size
   200  		"issue42058b.go", // go/types does not have constraints on channel element size
   201  		"issue48097.go",  // go/types doesn't check validity of //go:xxx directives, and non-init bodyless function
   202  		"issue48230.go",  // go/types doesn't check validity of //go:xxx directives
   203  		"issue49767.go",  // go/types does not have constraints on channel element size
   204  		"issue49814.go",  // go/types does not have constraints on array size
   205  		"issue56103.go",  // anonymous interface cycles; will be a type checker error in 1.22
   206  
   207  		// These tests requires runtime/cgo.Incomplete, which is only available on some platforms.
   208  		// However, go/types does not know about build constraints.
   209  		"bug514.go",
   210  		"issue40954.go",
   211  		"issue42032.go",
   212  		"issue42076.go",
   213  		"issue46903.go",
   214  		"issue51733.go",
   215  		"notinheap2.go",
   216  		"notinheap3.go",
   217  	)
   218  }
   219  
   220  func TestStdKen(t *testing.T) {
   221  	testenv.MustHaveGoBuild(t)
   222  
   223  	testTestDir(t, filepath.Join(testenv.GOROOT(t), "test", "ken"))
   224  }
   225  
   226  // Package paths of excluded packages.
   227  var excluded = map[string]bool{
   228  	"builtin": true,
   229  
   230  	// See go.dev/issue/46027: some imports are missing for this submodule.
   231  	"crypto/internal/edwards25519/field/_asm": true,
   232  	"crypto/internal/bigmod/_asm":             true,
   233  }
   234  
   235  // typecheckFiles typechecks the given package files.
   236  func typecheckFiles(t *testing.T, path string, filenames []string) {
   237  	fset := token.NewFileSet()
   238  
   239  	// parse package files
   240  	var files []*ast.File
   241  	for _, filename := range filenames {
   242  		file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
   243  		if err != nil {
   244  			// the parser error may be a list of individual errors; report them all
   245  			if list, ok := err.(scanner.ErrorList); ok {
   246  				for _, err := range list {
   247  					t.Error(err)
   248  				}
   249  				return
   250  			}
   251  			t.Error(err)
   252  			return
   253  		}
   254  
   255  		if testing.Verbose() {
   256  			if len(files) == 0 {
   257  				fmt.Println("package", file.Name.Name)
   258  			}
   259  			fmt.Println("\t", filename)
   260  		}
   261  
   262  		files = append(files, file)
   263  	}
   264  
   265  	// typecheck package files
   266  	conf := Config{
   267  		Error: func(err error) {
   268  			t.Helper()
   269  			t.Error(err)
   270  		},
   271  		Importer: stdLibImporter,
   272  	}
   273  	info := Info{Uses: make(map[*ast.Ident]Object)}
   274  	conf.Check(path, fset, files, &info)
   275  
   276  	// Perform checks of API invariants.
   277  
   278  	// All Objects have a package, except predeclared ones.
   279  	errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error
   280  	for id, obj := range info.Uses {
   281  		predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
   282  		if predeclared == (obj.Pkg() != nil) {
   283  			posn := fset.Position(id.Pos())
   284  			if predeclared {
   285  				t.Errorf("%s: predeclared object with package: %s", posn, obj)
   286  			} else {
   287  				t.Errorf("%s: user-defined object without package: %s", posn, obj)
   288  			}
   289  		}
   290  	}
   291  }
   292  
   293  // pkgFilenames returns the list of package filenames for the given directory.
   294  func pkgFilenames(dir string) ([]string, error) {
   295  	ctxt := build.Default
   296  	ctxt.CgoEnabled = false
   297  	pkg, err := ctxt.ImportDir(dir, 0)
   298  	if err != nil {
   299  		if _, nogo := err.(*build.NoGoError); nogo {
   300  			return nil, nil // no *.go files, not an error
   301  		}
   302  		return nil, err
   303  	}
   304  	if excluded[pkg.ImportPath] {
   305  		return nil, nil
   306  	}
   307  	var filenames []string
   308  	for _, name := range pkg.GoFiles {
   309  		filenames = append(filenames, filepath.Join(pkg.Dir, name))
   310  	}
   311  	for _, name := range pkg.TestGoFiles {
   312  		filenames = append(filenames, filepath.Join(pkg.Dir, name))
   313  	}
   314  	return filenames, nil
   315  }
   316  
   317  func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...any)) time.Duration {
   318  	w := walker{time.Now(), 10 * time.Millisecond, pkgh, errh}
   319  	w.walk(dir)
   320  	return time.Since(w.start)
   321  }
   322  
   323  type walker struct {
   324  	start time.Time
   325  	dmax  time.Duration
   326  	pkgh  func(dir string, filenames []string)
   327  	errh  func(args ...any)
   328  }
   329  
   330  func (w *walker) walk(dir string) {
   331  	// limit run time for short tests
   332  	if testing.Short() && time.Since(w.start) >= w.dmax {
   333  		return
   334  	}
   335  
   336  	files, err := os.ReadDir(dir)
   337  	if err != nil {
   338  		w.errh(err)
   339  		return
   340  	}
   341  
   342  	// apply pkgh to the files in directory dir
   343  	pkgFiles, err := pkgFilenames(dir)
   344  	if err != nil {
   345  		w.errh(err)
   346  		return
   347  	}
   348  	if pkgFiles != nil {
   349  		w.pkgh(dir, pkgFiles)
   350  	}
   351  
   352  	// traverse subdirectories, but don't walk into testdata
   353  	for _, f := range files {
   354  		if f.IsDir() && f.Name() != "testdata" {
   355  			w.walk(filepath.Join(dir, f.Name()))
   356  		}
   357  	}
   358  }