...
Run Format

Source file src/cmd/vet/shadow.go

Documentation: cmd/vet

     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  /*
     6  This file contains the code to check for shadowed variables.
     7  A shadowed variable is a variable declared in an inner scope
     8  with the same name and type as a variable in an outer scope,
     9  and where the outer variable is mentioned after the inner one
    10  is declared.
    11  
    12  (This definition can be refined; the module generates too many
    13  false positives and is not yet enabled by default.)
    14  
    15  For example:
    16  
    17  	func BadRead(f *os.File, buf []byte) error {
    18  		var err error
    19  		for {
    20  			n, err := f.Read(buf) // shadows the function variable 'err'
    21  			if err != nil {
    22  				break // causes return of wrong value
    23  			}
    24  			foo(buf)
    25  		}
    26  		return err
    27  	}
    28  
    29  */
    30  
    31  package main
    32  
    33  import (
    34  	"flag"
    35  	"go/ast"
    36  	"go/token"
    37  	"go/types"
    38  )
    39  
    40  var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy")
    41  
    42  func init() {
    43  	register("shadow",
    44  		"check for shadowed variables (experimental; must be set explicitly)",
    45  		checkShadow,
    46  		assignStmt, genDecl)
    47  	experimental["shadow"] = true
    48  }
    49  
    50  func checkShadow(f *File, node ast.Node) {
    51  	switch n := node.(type) {
    52  	case *ast.AssignStmt:
    53  		checkShadowAssignment(f, n)
    54  	case *ast.GenDecl:
    55  		checkShadowDecl(f, n)
    56  	}
    57  }
    58  
    59  // Span stores the minimum range of byte positions in the file in which a
    60  // given variable (types.Object) is mentioned. It is lexically defined: it spans
    61  // from the beginning of its first mention to the end of its last mention.
    62  // A variable is considered shadowed (if *strictShadowing is off) only if the
    63  // shadowing variable is declared within the span of the shadowed variable.
    64  // In other words, if a variable is shadowed but not used after the shadowed
    65  // variable is declared, it is inconsequential and not worth complaining about.
    66  // This simple check dramatically reduces the nuisance rate for the shadowing
    67  // check, at least until something cleverer comes along.
    68  //
    69  // One wrinkle: A "naked return" is a silent use of a variable that the Span
    70  // will not capture, but the compilers catch naked returns of shadowed
    71  // variables so we don't need to.
    72  //
    73  // Cases this gets wrong (TODO):
    74  // - If a for loop's continuation statement mentions a variable redeclared in
    75  // the block, we should complain about it but don't.
    76  // - A variable declared inside a function literal can falsely be identified
    77  // as shadowing a variable in the outer function.
    78  //
    79  type Span struct {
    80  	min token.Pos
    81  	max token.Pos
    82  }
    83  
    84  // contains reports whether the position is inside the span.
    85  func (s Span) contains(pos token.Pos) bool {
    86  	return s.min <= pos && pos < s.max
    87  }
    88  
    89  // growSpan expands the span for the object to contain the instance represented
    90  // by the identifier.
    91  func (pkg *Package) growSpan(ident *ast.Ident, obj types.Object) {
    92  	if *strictShadowing {
    93  		return // No need
    94  	}
    95  	pos := ident.Pos()
    96  	end := ident.End()
    97  	span, ok := pkg.spans[obj]
    98  	if ok {
    99  		if span.min > pos {
   100  			span.min = pos
   101  		}
   102  		if span.max < end {
   103  			span.max = end
   104  		}
   105  	} else {
   106  		span = Span{pos, end}
   107  	}
   108  	pkg.spans[obj] = span
   109  }
   110  
   111  // checkShadowAssignment checks for shadowing in a short variable declaration.
   112  func checkShadowAssignment(f *File, a *ast.AssignStmt) {
   113  	if a.Tok != token.DEFINE {
   114  		return
   115  	}
   116  	if f.idiomaticShortRedecl(a) {
   117  		return
   118  	}
   119  	for _, expr := range a.Lhs {
   120  		ident, ok := expr.(*ast.Ident)
   121  		if !ok {
   122  			f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
   123  			return
   124  		}
   125  		checkShadowing(f, ident)
   126  	}
   127  }
   128  
   129  // idiomaticShortRedecl reports whether this short declaration can be ignored for
   130  // the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
   131  func (f *File) idiomaticShortRedecl(a *ast.AssignStmt) bool {
   132  	// Don't complain about deliberate redeclarations of the form
   133  	//	i := i
   134  	// Such constructs are idiomatic in range loops to create a new variable
   135  	// for each iteration. Another example is
   136  	//	switch n := n.(type)
   137  	if len(a.Rhs) != len(a.Lhs) {
   138  		return false
   139  	}
   140  	// We know it's an assignment, so the LHS must be all identifiers. (We check anyway.)
   141  	for i, expr := range a.Lhs {
   142  		lhs, ok := expr.(*ast.Ident)
   143  		if !ok {
   144  			f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
   145  			return true // Don't do any more processing.
   146  		}
   147  		switch rhs := a.Rhs[i].(type) {
   148  		case *ast.Ident:
   149  			if lhs.Name != rhs.Name {
   150  				return false
   151  			}
   152  		case *ast.TypeAssertExpr:
   153  			if id, ok := rhs.X.(*ast.Ident); ok {
   154  				if lhs.Name != id.Name {
   155  					return false
   156  				}
   157  			}
   158  		default:
   159  			return false
   160  		}
   161  	}
   162  	return true
   163  }
   164  
   165  // idiomaticRedecl reports whether this declaration spec can be ignored for
   166  // the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
   167  func (f *File) idiomaticRedecl(d *ast.ValueSpec) bool {
   168  	// Don't complain about deliberate redeclarations of the form
   169  	//	var i, j = i, j
   170  	if len(d.Names) != len(d.Values) {
   171  		return false
   172  	}
   173  	for i, lhs := range d.Names {
   174  		if rhs, ok := d.Values[i].(*ast.Ident); ok {
   175  			if lhs.Name != rhs.Name {
   176  				return false
   177  			}
   178  		}
   179  	}
   180  	return true
   181  }
   182  
   183  // checkShadowDecl checks for shadowing in a general variable declaration.
   184  func checkShadowDecl(f *File, d *ast.GenDecl) {
   185  	if d.Tok != token.VAR {
   186  		return
   187  	}
   188  	for _, spec := range d.Specs {
   189  		valueSpec, ok := spec.(*ast.ValueSpec)
   190  		if !ok {
   191  			f.Badf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec")
   192  			return
   193  		}
   194  		// Don't complain about deliberate redeclarations of the form
   195  		//	var i = i
   196  		if f.idiomaticRedecl(valueSpec) {
   197  			return
   198  		}
   199  		for _, ident := range valueSpec.Names {
   200  			checkShadowing(f, ident)
   201  		}
   202  	}
   203  }
   204  
   205  // checkShadowing checks whether the identifier shadows an identifier in an outer scope.
   206  func checkShadowing(f *File, ident *ast.Ident) {
   207  	if ident.Name == "_" {
   208  		// Can't shadow the blank identifier.
   209  		return
   210  	}
   211  	obj := f.pkg.defs[ident]
   212  	if obj == nil {
   213  		return
   214  	}
   215  	// obj.Parent.Parent is the surrounding scope. If we can find another declaration
   216  	// starting from there, we have a shadowed identifier.
   217  	_, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos())
   218  	if shadowed == nil {
   219  		return
   220  	}
   221  	// Don't complain if it's shadowing a universe-declared identifier; that's fine.
   222  	if shadowed.Parent() == types.Universe {
   223  		return
   224  	}
   225  	if *strictShadowing {
   226  		// The shadowed identifier must appear before this one to be an instance of shadowing.
   227  		if shadowed.Pos() > ident.Pos() {
   228  			return
   229  		}
   230  	} else {
   231  		// Don't complain if the span of validity of the shadowed identifier doesn't include
   232  		// the shadowing identifier.
   233  		span, ok := f.pkg.spans[shadowed]
   234  		if !ok {
   235  			f.Badf(ident.Pos(), "internal error: no range for %q", ident.Name)
   236  			return
   237  		}
   238  		if !span.contains(ident.Pos()) {
   239  			return
   240  		}
   241  	}
   242  	// Don't complain if the types differ: that implies the programmer really wants two different things.
   243  	if types.Identical(obj.Type(), shadowed.Type()) {
   244  		f.Badf(ident.Pos(), "declaration of %q shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos()))
   245  	}
   246  }
   247  

View as plain text