Home | History | Annotate | Download | only in 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