github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/analysis/passes/shadow/shadow.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  // Package shadow defines an Analyzer that checks for shadowed variables.
     6  package shadow
     7  
     8  import (
     9  	"go/ast"
    10  	"go/token"
    11  	"go/types"
    12  
    13  	"github.com/powerman/golang-tools/go/analysis"
    14  	"github.com/powerman/golang-tools/go/analysis/passes/inspect"
    15  	"github.com/powerman/golang-tools/go/ast/inspector"
    16  )
    17  
    18  // NOTE: Experimental. Not part of the vet suite.
    19  
    20  const Doc = `check for possible unintended shadowing of variables
    21  
    22  This analyzer check for shadowed variables.
    23  A shadowed variable is a variable declared in an inner scope
    24  with the same name and type as a variable in an outer scope,
    25  and where the outer variable is mentioned after the inner one
    26  is declared.
    27  
    28  (This definition can be refined; the module generates too many
    29  false positives and is not yet enabled by default.)
    30  
    31  For example:
    32  
    33  	func BadRead(f *os.File, buf []byte) error {
    34  		var err error
    35  		for {
    36  			n, err := f.Read(buf) // shadows the function variable 'err'
    37  			if err != nil {
    38  				break // causes return of wrong value
    39  			}
    40  			foo(buf)
    41  		}
    42  		return err
    43  	}
    44  `
    45  
    46  var Analyzer = &analysis.Analyzer{
    47  	Name:     "shadow",
    48  	Doc:      Doc,
    49  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    50  	Run:      run,
    51  }
    52  
    53  // flags
    54  var strict = false
    55  
    56  func init() {
    57  	Analyzer.Flags.BoolVar(&strict, "strict", strict, "whether to be strict about shadowing; can be noisy")
    58  }
    59  
    60  func run(pass *analysis.Pass) (interface{}, error) {
    61  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    62  
    63  	spans := make(map[types.Object]span)
    64  	for id, obj := range pass.TypesInfo.Defs {
    65  		// Ignore identifiers that don't denote objects
    66  		// (package names, symbolic variables such as t
    67  		// in t := x.(type) of type switch headers).
    68  		if obj != nil {
    69  			growSpan(spans, obj, id.Pos(), id.End())
    70  		}
    71  	}
    72  	for id, obj := range pass.TypesInfo.Uses {
    73  		growSpan(spans, obj, id.Pos(), id.End())
    74  	}
    75  	for node, obj := range pass.TypesInfo.Implicits {
    76  		// A type switch with a short variable declaration
    77  		// such as t := x.(type) doesn't declare the symbolic
    78  		// variable (t in the example) at the switch header;
    79  		// instead a new variable t (with specific type) is
    80  		// declared implicitly for each case. Such variables
    81  		// are found in the types.Info.Implicits (not Defs)
    82  		// map. Add them here, assuming they are declared at
    83  		// the type cases' colon ":".
    84  		if cc, ok := node.(*ast.CaseClause); ok {
    85  			growSpan(spans, obj, cc.Colon, cc.Colon)
    86  		}
    87  	}
    88  
    89  	nodeFilter := []ast.Node{
    90  		(*ast.AssignStmt)(nil),
    91  		(*ast.GenDecl)(nil),
    92  	}
    93  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    94  		switch n := n.(type) {
    95  		case *ast.AssignStmt:
    96  			checkShadowAssignment(pass, spans, n)
    97  		case *ast.GenDecl:
    98  			checkShadowDecl(pass, spans, n)
    99  		}
   100  	})
   101  	return nil, nil
   102  }
   103  
   104  // A span stores the minimum range of byte positions in the file in which a
   105  // given variable (types.Object) is mentioned. It is lexically defined: it spans
   106  // from the beginning of its first mention to the end of its last mention.
   107  // A variable is considered shadowed (if strict is off) only if the
   108  // shadowing variable is declared within the span of the shadowed variable.
   109  // In other words, if a variable is shadowed but not used after the shadowed
   110  // variable is declared, it is inconsequential and not worth complaining about.
   111  // This simple check dramatically reduces the nuisance rate for the shadowing
   112  // check, at least until something cleverer comes along.
   113  //
   114  // One wrinkle: A "naked return" is a silent use of a variable that the Span
   115  // will not capture, but the compilers catch naked returns of shadowed
   116  // variables so we don't need to.
   117  //
   118  // Cases this gets wrong (TODO):
   119  // - If a for loop's continuation statement mentions a variable redeclared in
   120  // the block, we should complain about it but don't.
   121  // - A variable declared inside a function literal can falsely be identified
   122  // as shadowing a variable in the outer function.
   123  //
   124  type span struct {
   125  	min token.Pos
   126  	max token.Pos
   127  }
   128  
   129  // contains reports whether the position is inside the span.
   130  func (s span) contains(pos token.Pos) bool {
   131  	return s.min <= pos && pos < s.max
   132  }
   133  
   134  // growSpan expands the span for the object to contain the source range [pos, end).
   135  func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) {
   136  	if strict {
   137  		return // No need
   138  	}
   139  	s, ok := spans[obj]
   140  	if ok {
   141  		if s.min > pos {
   142  			s.min = pos
   143  		}
   144  		if s.max < end {
   145  			s.max = end
   146  		}
   147  	} else {
   148  		s = span{pos, end}
   149  	}
   150  	spans[obj] = s
   151  }
   152  
   153  // checkShadowAssignment checks for shadowing in a short variable declaration.
   154  func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *ast.AssignStmt) {
   155  	if a.Tok != token.DEFINE {
   156  		return
   157  	}
   158  	if idiomaticShortRedecl(pass, a) {
   159  		return
   160  	}
   161  	for _, expr := range a.Lhs {
   162  		ident, ok := expr.(*ast.Ident)
   163  		if !ok {
   164  			pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier")
   165  			return
   166  		}
   167  		checkShadowing(pass, spans, ident)
   168  	}
   169  }
   170  
   171  // idiomaticShortRedecl reports whether this short declaration can be ignored for
   172  // the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
   173  func idiomaticShortRedecl(pass *analysis.Pass, a *ast.AssignStmt) bool {
   174  	// Don't complain about deliberate redeclarations of the form
   175  	//	i := i
   176  	// Such constructs are idiomatic in range loops to create a new variable
   177  	// for each iteration. Another example is
   178  	//	switch n := n.(type)
   179  	if len(a.Rhs) != len(a.Lhs) {
   180  		return false
   181  	}
   182  	// We know it's an assignment, so the LHS must be all identifiers. (We check anyway.)
   183  	for i, expr := range a.Lhs {
   184  		lhs, ok := expr.(*ast.Ident)
   185  		if !ok {
   186  			pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier")
   187  			return true // Don't do any more processing.
   188  		}
   189  		switch rhs := a.Rhs[i].(type) {
   190  		case *ast.Ident:
   191  			if lhs.Name != rhs.Name {
   192  				return false
   193  			}
   194  		case *ast.TypeAssertExpr:
   195  			if id, ok := rhs.X.(*ast.Ident); ok {
   196  				if lhs.Name != id.Name {
   197  					return false
   198  				}
   199  			}
   200  		default:
   201  			return false
   202  		}
   203  	}
   204  	return true
   205  }
   206  
   207  // idiomaticRedecl reports whether this declaration spec can be ignored for
   208  // the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
   209  func idiomaticRedecl(d *ast.ValueSpec) bool {
   210  	// Don't complain about deliberate redeclarations of the form
   211  	//	var i, j = i, j
   212  	// Don't ignore redeclarations of the form
   213  	//	var i = 3
   214  	if len(d.Names) != len(d.Values) {
   215  		return false
   216  	}
   217  	for i, lhs := range d.Names {
   218  		rhs, ok := d.Values[i].(*ast.Ident)
   219  		if !ok || lhs.Name != rhs.Name {
   220  			return false
   221  		}
   222  	}
   223  	return true
   224  }
   225  
   226  // checkShadowDecl checks for shadowing in a general variable declaration.
   227  func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.GenDecl) {
   228  	if d.Tok != token.VAR {
   229  		return
   230  	}
   231  	for _, spec := range d.Specs {
   232  		valueSpec, ok := spec.(*ast.ValueSpec)
   233  		if !ok {
   234  			pass.ReportRangef(spec, "invalid AST: var GenDecl not ValueSpec")
   235  			return
   236  		}
   237  		// Don't complain about deliberate redeclarations of the form
   238  		//	var i = i
   239  		if idiomaticRedecl(valueSpec) {
   240  			return
   241  		}
   242  		for _, ident := range valueSpec.Names {
   243  			checkShadowing(pass, spans, ident)
   244  		}
   245  	}
   246  }
   247  
   248  // checkShadowing checks whether the identifier shadows an identifier in an outer scope.
   249  func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast.Ident) {
   250  	if ident.Name == "_" {
   251  		// Can't shadow the blank identifier.
   252  		return
   253  	}
   254  	obj := pass.TypesInfo.Defs[ident]
   255  	if obj == nil {
   256  		return
   257  	}
   258  	// obj.Parent.Parent is the surrounding scope. If we can find another declaration
   259  	// starting from there, we have a shadowed identifier.
   260  	_, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos())
   261  	if shadowed == nil {
   262  		return
   263  	}
   264  	// Don't complain if it's shadowing a universe-declared identifier; that's fine.
   265  	if shadowed.Parent() == types.Universe {
   266  		return
   267  	}
   268  	if strict {
   269  		// The shadowed identifier must appear before this one to be an instance of shadowing.
   270  		if shadowed.Pos() > ident.Pos() {
   271  			return
   272  		}
   273  	} else {
   274  		// Don't complain if the span of validity of the shadowed identifier doesn't include
   275  		// the shadowing identifier.
   276  		span, ok := spans[shadowed]
   277  		if !ok {
   278  			pass.ReportRangef(ident, "internal error: no range for %q", ident.Name)
   279  			return
   280  		}
   281  		if !span.contains(ident.Pos()) {
   282  			return
   283  		}
   284  	}
   285  	// Don't complain if the types differ: that implies the programmer really wants two different things.
   286  	if types.Identical(obj.Type(), shadowed.Type()) {
   287  		line := pass.Fset.Position(shadowed.Pos()).Line
   288  		pass.ReportRangef(ident, "declaration of %q shadows declaration at line %d", obj.Name(), line)
   289  	}
   290  }