/src/pkg/go/typechecker/typechecker_test.go
Go | 168 lines | 112 code | 24 blank | 32 comment | 33 complexity | ece4a702e2e8ec645b5fdd821fe51fba MD5 | raw file
Possible License(s): BSD-3-Clause
- // Copyright 2010 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // This file implements a simple typechecker test harness. Packages found
- // in the testDir directory are typechecked. Error messages reported by
- // the typechecker are compared against the error messages expected for
- // the test files.
- //
- // Expected errors are indicated in the test files by putting a comment
- // of the form /* ERROR "rx" */ immediately following an offending token.
- // The harness will verify that an error matching the regular expression
- // rx is reported at that source position. Consecutive comments may be
- // used to indicate multiple errors for the same token position.
- //
- // For instance, the following test file indicates that a "not declared"
- // error should be reported for the undeclared variable x:
- //
- // package P0
- // func f() {
- // _ = x /* ERROR "not declared" */ + 1
- // }
- //
- // If the -pkg flag is set, only packages with package names matching
- // the regular expression provided via the flag value are tested.
- package typechecker
- import (
- "flag"
- "fmt"
- "go/ast"
- "go/parser"
- "go/scanner"
- "go/token"
- "io/ioutil"
- "os"
- "regexp"
- "sort"
- "strings"
- "testing"
- )
- const testDir = "./testdata" // location of test packages
- var fset = token.NewFileSet()
- var (
- pkgPat = flag.String("pkg", ".*", "regular expression to select test packages by package name")
- trace = flag.Bool("trace", false, "print package names")
- )
- // ERROR comments must be of the form /* ERROR "rx" */ and rx is
- // a regular expression that matches the expected error message.
- var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
- // expectedErrors collects the regular expressions of ERROR comments
- // found in the package files of pkg and returns them in sorted order
- // (by filename and position).
- func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
- // scan all package files
- for filename := range pkg.Files {
- src, err := ioutil.ReadFile(filename)
- if err != nil {
- t.Fatalf("expectedErrors(%s): %v", pkg.Name, err)
- }
- var s scanner.Scanner
- file := fset.AddFile(filename, fset.Base(), len(src))
- s.Init(file, src, nil, scanner.ScanComments)
- var prev token.Pos // position of last non-comment token
- loop:
- for {
- pos, tok, lit := s.Scan()
- switch tok {
- case token.EOF:
- break loop
- case token.COMMENT:
- s := errRx.FindStringSubmatch(lit)
- if len(s) == 2 {
- list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
- }
- default:
- prev = pos
- }
- }
- }
- sort.Sort(list) // multiple files may not be sorted
- return
- }
- func testFilter(f *os.FileInfo) bool {
- return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
- }
- func checkError(t *testing.T, expected, found *scanner.Error) {
- rx, err := regexp.Compile(expected.Msg)
- if err != nil {
- t.Errorf("%s: %v", expected.Pos, err)
- return
- }
- match := rx.MatchString(found.Msg)
- if expected.Pos.Offset != found.Pos.Offset {
- if match {
- t.Errorf("%s: expected error should have been at %s", expected.Pos, found.Pos)
- } else {
- t.Errorf("%s: error matching %q expected", expected.Pos, expected.Msg)
- return
- }
- }
- if !match {
- t.Errorf("%s: %q does not match %q", expected.Pos, expected.Msg, found.Msg)
- }
- }
- func TestTypeCheck(t *testing.T) {
- flag.Parse()
- pkgRx, err := regexp.Compile(*pkgPat)
- if err != nil {
- t.Fatalf("illegal flag value %q: %s", *pkgPat, err)
- }
- pkgs, err := parser.ParseDir(fset, testDir, testFilter, 0)
- if err != nil {
- scanner.PrintError(os.Stderr, err)
- t.Fatalf("packages in %s contain syntax errors", testDir)
- }
- for _, pkg := range pkgs {
- if !pkgRx.MatchString(pkg.Name) {
- continue // only test selected packages
- }
- if *trace {
- fmt.Println(pkg.Name)
- }
- xlist := expectedErrors(t, pkg)
- err := CheckPackage(fset, pkg, nil)
- if err != nil {
- if elist, ok := err.(scanner.ErrorList); ok {
- // verify that errors match
- for i := 0; i < len(xlist) && i < len(elist); i++ {
- checkError(t, xlist[i], elist[i])
- }
- // the correct number or errors must have been found
- if len(xlist) != len(elist) {
- fmt.Fprintf(os.Stderr, "%s\n", pkg.Name)
- scanner.PrintError(os.Stderr, elist)
- fmt.Fprintln(os.Stderr)
- t.Errorf("TypeCheck(%s): %d errors expected but %d reported", pkg.Name, len(xlist), len(elist))
- }
- } else {
- t.Errorf("TypeCheck(%s): %v", pkg.Name, err)
- }
- } else if len(xlist) > 0 {
- t.Errorf("TypeCheck(%s): %d errors expected but 0 reported", pkg.Name, len(xlist))
- }
- }
- }