...

Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go

     1	// Copyright 2014 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 shift defines an Analyzer that checks for shifts that exceed
     6	// the width of an integer.
     7	package shift
     8	
     9	// TODO(adonovan): integrate with ctrflow (CFG-based) dead code analysis. May
    10	// have impedance mismatch due to its (non-)treatment of constant
    11	// expressions (such as runtime.GOARCH=="386").
    12	
    13	import (
    14		"go/ast"
    15		"go/constant"
    16		"go/token"
    17	
    18		"golang.org/x/tools/go/analysis"
    19		"golang.org/x/tools/go/analysis/passes/inspect"
    20		"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    21		"golang.org/x/tools/go/ast/inspector"
    22	)
    23	
    24	var Analyzer = &analysis.Analyzer{
    25		Name:     "shift",
    26		Doc:      "check for shifts that equal or exceed the width of the integer",
    27		Requires: []*analysis.Analyzer{inspect.Analyzer},
    28		Run:      run,
    29	}
    30	
    31	func run(pass *analysis.Pass) (interface{}, error) {
    32		inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    33	
    34		// Do a complete pass to compute dead nodes.
    35		dead := make(map[ast.Node]bool)
    36		nodeFilter := []ast.Node{
    37			(*ast.IfStmt)(nil),
    38			(*ast.SwitchStmt)(nil),
    39		}
    40		inspect.Preorder(nodeFilter, func(n ast.Node) {
    41			// TODO(adonovan): move updateDead into this file.
    42			updateDead(pass.TypesInfo, dead, n)
    43		})
    44	
    45		nodeFilter = []ast.Node{
    46			(*ast.AssignStmt)(nil),
    47			(*ast.BinaryExpr)(nil),
    48		}
    49		inspect.Preorder(nodeFilter, func(node ast.Node) {
    50			if dead[node] {
    51				// Skip shift checks on unreachable nodes.
    52				return
    53			}
    54	
    55			switch node := node.(type) {
    56			case *ast.BinaryExpr:
    57				if node.Op == token.SHL || node.Op == token.SHR {
    58					checkLongShift(pass, node, node.X, node.Y)
    59				}
    60			case *ast.AssignStmt:
    61				if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
    62					return
    63				}
    64				if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
    65					checkLongShift(pass, node, node.Lhs[0], node.Rhs[0])
    66				}
    67			}
    68		})
    69		return nil, nil
    70	}
    71	
    72	// checkLongShift checks if shift or shift-assign operations shift by more than
    73	// the length of the underlying variable.
    74	func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
    75		if pass.TypesInfo.Types[x].Value != nil {
    76			// Ignore shifts of constants.
    77			// These are frequently used for bit-twiddling tricks
    78			// like ^uint(0) >> 63 for 32/64 bit detection and compatibility.
    79			return
    80		}
    81	
    82		v := pass.TypesInfo.Types[y].Value
    83		if v == nil {
    84			return
    85		}
    86		amt, ok := constant.Int64Val(v)
    87		if !ok {
    88			return
    89		}
    90		t := pass.TypesInfo.Types[x].Type
    91		if t == nil {
    92			return
    93		}
    94		size := 8 * pass.TypesSizes.Sizeof(t)
    95		if amt >= size {
    96			ident := analysisutil.Format(pass.Fset, x)
    97			pass.Reportf(node.Pos(), "%s (%d bits) too small for shift of %d", ident, size, amt)
    98		}
    99	}
   100	

View as plain text