 bf27481efd
			
		
	
	
		bf27481efd
		
			
		
	
	
	
	
		
			
			full diff: https://github.com/dominikh/go-tools/compare/2019.2.3...2020.1.3 Also updates tests to accomodate updated rules: --- FAIL: TestSourcesFromTestdataWithIssuesDir/staticcheck.go (0.43s) linters_test.go:137: [run --disable-all --print-issued-lines=false --print-linter-name=false --out-format=line-number --max-same-issues=10 -Estaticcheck --no-config testdata/staticcheck.go] linters_test.go:33: Error Trace: linters_test.go:33 linters_test.go:138 linters_test.go:53 Error: Received unexpected error: staticcheck.go:11: no match for `self-assignment of x to x` vs ["SA4006: this value of `x` is never used"] in: staticcheck.go:11:2: SA4006: this value of `x` is never used unmatched errors staticcheck.go:11:2: SA4006: this value of `x` is never used Test: TestSourcesFromTestdataWithIssuesDir/staticcheck.go Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			451 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			451 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2013 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.
 | |
| 
 | |
| package ir
 | |
| 
 | |
| // Helpers for emitting IR instructions.
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/constant"
 | |
| 	"go/token"
 | |
| 	"go/types"
 | |
| )
 | |
| 
 | |
| // emitNew emits to f a new (heap Alloc) instruction allocating an
 | |
| // object of type typ.  pos is the optional source location.
 | |
| //
 | |
| func emitNew(f *Function, typ types.Type, source ast.Node) *Alloc {
 | |
| 	v := &Alloc{Heap: true}
 | |
| 	v.setType(types.NewPointer(typ))
 | |
| 	f.emit(v, source)
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // emitLoad emits to f an instruction to load the address addr into a
 | |
| // new temporary, and returns the value so defined.
 | |
| //
 | |
| func emitLoad(f *Function, addr Value, source ast.Node) *Load {
 | |
| 	v := &Load{X: addr}
 | |
| 	v.setType(deref(addr.Type()))
 | |
| 	f.emit(v, source)
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func emitRecv(f *Function, ch Value, commaOk bool, typ types.Type, source ast.Node) Value {
 | |
| 	recv := &Recv{
 | |
| 		Chan:    ch,
 | |
| 		CommaOk: commaOk,
 | |
| 	}
 | |
| 	recv.setType(typ)
 | |
| 	return f.emit(recv, source)
 | |
| }
 | |
| 
 | |
| // emitDebugRef emits to f a DebugRef pseudo-instruction associating
 | |
| // expression e with value v.
 | |
| //
 | |
| func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
 | |
| 	if !f.debugInfo() {
 | |
| 		return // debugging not enabled
 | |
| 	}
 | |
| 	if v == nil || e == nil {
 | |
| 		panic("nil")
 | |
| 	}
 | |
| 	var obj types.Object
 | |
| 	e = unparen(e)
 | |
| 	if id, ok := e.(*ast.Ident); ok {
 | |
| 		if isBlankIdent(id) {
 | |
| 			return
 | |
| 		}
 | |
| 		obj = f.Pkg.objectOf(id)
 | |
| 		switch obj.(type) {
 | |
| 		case *types.Nil, *types.Const, *types.Builtin:
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	f.emit(&DebugRef{
 | |
| 		X:      v,
 | |
| 		Expr:   e,
 | |
| 		IsAddr: isAddr,
 | |
| 		object: obj,
 | |
| 	}, nil)
 | |
| }
 | |
| 
 | |
| // emitArith emits to f code to compute the binary operation op(x, y)
 | |
| // where op is an eager shift, logical or arithmetic operation.
 | |
| // (Use emitCompare() for comparisons and Builder.logicalBinop() for
 | |
| // non-eager operations.)
 | |
| //
 | |
| func emitArith(f *Function, op token.Token, x, y Value, t types.Type, source ast.Node) Value {
 | |
| 	switch op {
 | |
| 	case token.SHL, token.SHR:
 | |
| 		x = emitConv(f, x, t, source)
 | |
| 		// y may be signed or an 'untyped' constant.
 | |
| 		// TODO(adonovan): whence signed values?
 | |
| 		if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
 | |
| 			y = emitConv(f, y, types.Typ[types.Uint64], source)
 | |
| 		}
 | |
| 
 | |
| 	case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
 | |
| 		x = emitConv(f, x, t, source)
 | |
| 		y = emitConv(f, y, t, source)
 | |
| 
 | |
| 	default:
 | |
| 		panic("illegal op in emitArith: " + op.String())
 | |
| 
 | |
| 	}
 | |
| 	v := &BinOp{
 | |
| 		Op: op,
 | |
| 		X:  x,
 | |
| 		Y:  y,
 | |
| 	}
 | |
| 	v.setType(t)
 | |
| 	return f.emit(v, source)
 | |
| }
 | |
| 
 | |
| // emitCompare emits to f code compute the boolean result of
 | |
| // comparison comparison 'x op y'.
 | |
| //
 | |
| func emitCompare(f *Function, op token.Token, x, y Value, source ast.Node) Value {
 | |
| 	xt := x.Type().Underlying()
 | |
| 	yt := y.Type().Underlying()
 | |
| 
 | |
| 	// Special case to optimise a tagless SwitchStmt so that
 | |
| 	// these are equivalent
 | |
| 	//   switch { case e: ...}
 | |
| 	//   switch true { case e: ... }
 | |
| 	//   if e==true { ... }
 | |
| 	// even in the case when e's type is an interface.
 | |
| 	// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
 | |
| 	if x, ok := x.(*Const); ok && op == token.EQL && x.Value != nil && x.Value.Kind() == constant.Bool && constant.BoolVal(x.Value) {
 | |
| 		if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
 | |
| 			return y
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if types.Identical(xt, yt) {
 | |
| 		// no conversion necessary
 | |
| 	} else if _, ok := xt.(*types.Interface); ok {
 | |
| 		y = emitConv(f, y, x.Type(), source)
 | |
| 	} else if _, ok := yt.(*types.Interface); ok {
 | |
| 		x = emitConv(f, x, y.Type(), source)
 | |
| 	} else if _, ok := x.(*Const); ok {
 | |
| 		x = emitConv(f, x, y.Type(), source)
 | |
| 	} else if _, ok := y.(*Const); ok {
 | |
| 		y = emitConv(f, y, x.Type(), source)
 | |
| 		//lint:ignore SA9003 no-op
 | |
| 	} else {
 | |
| 		// other cases, e.g. channels.  No-op.
 | |
| 	}
 | |
| 
 | |
| 	v := &BinOp{
 | |
| 		Op: op,
 | |
| 		X:  x,
 | |
| 		Y:  y,
 | |
| 	}
 | |
| 	v.setType(tBool)
 | |
| 	return f.emit(v, source)
 | |
| }
 | |
| 
 | |
| // isValuePreserving returns true if a conversion from ut_src to
 | |
| // ut_dst is value-preserving, i.e. just a change of type.
 | |
| // Precondition: neither argument is a named type.
 | |
| //
 | |
| func isValuePreserving(ut_src, ut_dst types.Type) bool {
 | |
| 	// Identical underlying types?
 | |
| 	if structTypesIdentical(ut_dst, ut_src) {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	switch ut_dst.(type) {
 | |
| 	case *types.Chan:
 | |
| 		// Conversion between channel types?
 | |
| 		_, ok := ut_src.(*types.Chan)
 | |
| 		return ok
 | |
| 
 | |
| 	case *types.Pointer:
 | |
| 		// Conversion between pointers with identical base types?
 | |
| 		_, ok := ut_src.(*types.Pointer)
 | |
| 		return ok
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // emitConv emits to f code to convert Value val to exactly type typ,
 | |
| // and returns the converted value.  Implicit conversions are required
 | |
| // by language assignability rules in assignments, parameter passing,
 | |
| // etc.  Conversions cannot fail dynamically.
 | |
| //
 | |
| func emitConv(f *Function, val Value, typ types.Type, source ast.Node) Value {
 | |
| 	t_src := val.Type()
 | |
| 
 | |
| 	// Identical types?  Conversion is a no-op.
 | |
| 	if types.Identical(t_src, typ) {
 | |
| 		return val
 | |
| 	}
 | |
| 
 | |
| 	ut_dst := typ.Underlying()
 | |
| 	ut_src := t_src.Underlying()
 | |
| 
 | |
| 	// Just a change of type, but not value or representation?
 | |
| 	if isValuePreserving(ut_src, ut_dst) {
 | |
| 		c := &ChangeType{X: val}
 | |
| 		c.setType(typ)
 | |
| 		return f.emit(c, source)
 | |
| 	}
 | |
| 
 | |
| 	// Conversion to, or construction of a value of, an interface type?
 | |
| 	if _, ok := ut_dst.(*types.Interface); ok {
 | |
| 		// Assignment from one interface type to another?
 | |
| 		if _, ok := ut_src.(*types.Interface); ok {
 | |
| 			c := &ChangeInterface{X: val}
 | |
| 			c.setType(typ)
 | |
| 			return f.emit(c, source)
 | |
| 		}
 | |
| 
 | |
| 		// Untyped nil constant?  Return interface-typed nil constant.
 | |
| 		if ut_src == tUntypedNil {
 | |
| 			return emitConst(f, nilConst(typ))
 | |
| 		}
 | |
| 
 | |
| 		// Convert (non-nil) "untyped" literals to their default type.
 | |
| 		if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
 | |
| 			val = emitConv(f, val, types.Default(ut_src), source)
 | |
| 		}
 | |
| 
 | |
| 		f.Pkg.Prog.needMethodsOf(val.Type())
 | |
| 		mi := &MakeInterface{X: val}
 | |
| 		mi.setType(typ)
 | |
| 		return f.emit(mi, source)
 | |
| 	}
 | |
| 
 | |
| 	// Conversion of a compile-time constant value?
 | |
| 	if c, ok := val.(*Const); ok {
 | |
| 		if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() {
 | |
| 			// Conversion of a compile-time constant to
 | |
| 			// another constant type results in a new
 | |
| 			// constant of the destination type and
 | |
| 			// (initially) the same abstract value.
 | |
| 			// We don't truncate the value yet.
 | |
| 			return emitConst(f, NewConst(c.Value, typ))
 | |
| 		}
 | |
| 
 | |
| 		// We're converting from constant to non-constant type,
 | |
| 		// e.g. string -> []byte/[]rune.
 | |
| 	}
 | |
| 
 | |
| 	// A representation-changing conversion?
 | |
| 	// At least one of {ut_src,ut_dst} must be *Basic.
 | |
| 	// (The other may be []byte or []rune.)
 | |
| 	_, ok1 := ut_src.(*types.Basic)
 | |
| 	_, ok2 := ut_dst.(*types.Basic)
 | |
| 	if ok1 || ok2 {
 | |
| 		c := &Convert{X: val}
 | |
| 		c.setType(typ)
 | |
| 		return f.emit(c, source)
 | |
| 	}
 | |
| 
 | |
| 	panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
 | |
| }
 | |
| 
 | |
| // emitStore emits to f an instruction to store value val at location
 | |
| // addr, applying implicit conversions as required by assignability rules.
 | |
| //
 | |
| func emitStore(f *Function, addr, val Value, source ast.Node) *Store {
 | |
| 	s := &Store{
 | |
| 		Addr: addr,
 | |
| 		Val:  emitConv(f, val, deref(addr.Type()), source),
 | |
| 	}
 | |
| 	// make sure we call getMem after the call to emitConv, which may
 | |
| 	// itself update the memory state
 | |
| 	f.emit(s, source)
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| // emitJump emits to f a jump to target, and updates the control-flow graph.
 | |
| // Postcondition: f.currentBlock is nil.
 | |
| //
 | |
| func emitJump(f *Function, target *BasicBlock, source ast.Node) *Jump {
 | |
| 	b := f.currentBlock
 | |
| 	j := new(Jump)
 | |
| 	b.emit(j, source)
 | |
| 	addEdge(b, target)
 | |
| 	f.currentBlock = nil
 | |
| 	return j
 | |
| }
 | |
| 
 | |
| // emitIf emits to f a conditional jump to tblock or fblock based on
 | |
| // cond, and updates the control-flow graph.
 | |
| // Postcondition: f.currentBlock is nil.
 | |
| //
 | |
| func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock, source ast.Node) *If {
 | |
| 	b := f.currentBlock
 | |
| 	stmt := &If{Cond: cond}
 | |
| 	b.emit(stmt, source)
 | |
| 	addEdge(b, tblock)
 | |
| 	addEdge(b, fblock)
 | |
| 	f.currentBlock = nil
 | |
| 	return stmt
 | |
| }
 | |
| 
 | |
| // emitExtract emits to f an instruction to extract the index'th
 | |
| // component of tuple.  It returns the extracted value.
 | |
| //
 | |
| func emitExtract(f *Function, tuple Value, index int, source ast.Node) Value {
 | |
| 	e := &Extract{Tuple: tuple, Index: index}
 | |
| 	e.setType(tuple.Type().(*types.Tuple).At(index).Type())
 | |
| 	return f.emit(e, source)
 | |
| }
 | |
| 
 | |
| // emitTypeAssert emits to f a type assertion value := x.(t) and
 | |
| // returns the value.  x.Type() must be an interface.
 | |
| //
 | |
| func emitTypeAssert(f *Function, x Value, t types.Type, source ast.Node) Value {
 | |
| 	a := &TypeAssert{X: x, AssertedType: t}
 | |
| 	a.setType(t)
 | |
| 	return f.emit(a, source)
 | |
| }
 | |
| 
 | |
| // emitTypeTest emits to f a type test value,ok := x.(t) and returns
 | |
| // a (value, ok) tuple.  x.Type() must be an interface.
 | |
| //
 | |
| func emitTypeTest(f *Function, x Value, t types.Type, source ast.Node) Value {
 | |
| 	a := &TypeAssert{
 | |
| 		X:            x,
 | |
| 		AssertedType: t,
 | |
| 		CommaOk:      true,
 | |
| 	}
 | |
| 	a.setType(types.NewTuple(
 | |
| 		newVar("value", t),
 | |
| 		varOk,
 | |
| 	))
 | |
| 	return f.emit(a, source)
 | |
| }
 | |
| 
 | |
| // emitTailCall emits to f a function call in tail position.  The
 | |
| // caller is responsible for all fields of 'call' except its type.
 | |
| // Intended for wrapper methods.
 | |
| // Precondition: f does/will not use deferred procedure calls.
 | |
| // Postcondition: f.currentBlock is nil.
 | |
| //
 | |
| func emitTailCall(f *Function, call *Call, source ast.Node) {
 | |
| 	tresults := f.Signature.Results()
 | |
| 	nr := tresults.Len()
 | |
| 	if nr == 1 {
 | |
| 		call.typ = tresults.At(0).Type()
 | |
| 	} else {
 | |
| 		call.typ = tresults
 | |
| 	}
 | |
| 	tuple := f.emit(call, source)
 | |
| 	var ret Return
 | |
| 	switch nr {
 | |
| 	case 0:
 | |
| 		// no-op
 | |
| 	case 1:
 | |
| 		ret.Results = []Value{tuple}
 | |
| 	default:
 | |
| 		for i := 0; i < nr; i++ {
 | |
| 			v := emitExtract(f, tuple, i, source)
 | |
| 			// TODO(adonovan): in principle, this is required:
 | |
| 			//   v = emitConv(f, o.Type, f.Signature.Results[i].Type)
 | |
| 			// but in practice emitTailCall is only used when
 | |
| 			// the types exactly match.
 | |
| 			ret.Results = append(ret.Results, v)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	f.Exit = f.newBasicBlock("exit")
 | |
| 	emitJump(f, f.Exit, source)
 | |
| 	f.currentBlock = f.Exit
 | |
| 	f.emit(&ret, source)
 | |
| 	f.currentBlock = nil
 | |
| }
 | |
| 
 | |
| // emitImplicitSelections emits to f code to apply the sequence of
 | |
| // implicit field selections specified by indices to base value v, and
 | |
| // returns the selected value.
 | |
| //
 | |
| // If v is the address of a struct, the result will be the address of
 | |
| // a field; if it is the value of a struct, the result will be the
 | |
| // value of a field.
 | |
| //
 | |
| func emitImplicitSelections(f *Function, v Value, indices []int, source ast.Node) Value {
 | |
| 	for _, index := range indices {
 | |
| 		fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
 | |
| 
 | |
| 		if isPointer(v.Type()) {
 | |
| 			instr := &FieldAddr{
 | |
| 				X:     v,
 | |
| 				Field: index,
 | |
| 			}
 | |
| 			instr.setType(types.NewPointer(fld.Type()))
 | |
| 			v = f.emit(instr, source)
 | |
| 			// Load the field's value iff indirectly embedded.
 | |
| 			if isPointer(fld.Type()) {
 | |
| 				v = emitLoad(f, v, source)
 | |
| 			}
 | |
| 		} else {
 | |
| 			instr := &Field{
 | |
| 				X:     v,
 | |
| 				Field: index,
 | |
| 			}
 | |
| 			instr.setType(fld.Type())
 | |
| 			v = f.emit(instr, source)
 | |
| 		}
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // emitFieldSelection emits to f code to select the index'th field of v.
 | |
| //
 | |
| // If wantAddr, the input must be a pointer-to-struct and the result
 | |
| // will be the field's address; otherwise the result will be the
 | |
| // field's value.
 | |
| // Ident id is used for position and debug info.
 | |
| //
 | |
| func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
 | |
| 	fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
 | |
| 	if isPointer(v.Type()) {
 | |
| 		instr := &FieldAddr{
 | |
| 			X:     v,
 | |
| 			Field: index,
 | |
| 		}
 | |
| 		instr.setSource(id)
 | |
| 		instr.setType(types.NewPointer(fld.Type()))
 | |
| 		v = f.emit(instr, id)
 | |
| 		// Load the field's value iff we don't want its address.
 | |
| 		if !wantAddr {
 | |
| 			v = emitLoad(f, v, id)
 | |
| 		}
 | |
| 	} else {
 | |
| 		instr := &Field{
 | |
| 			X:     v,
 | |
| 			Field: index,
 | |
| 		}
 | |
| 		instr.setSource(id)
 | |
| 		instr.setType(fld.Type())
 | |
| 		v = f.emit(instr, id)
 | |
| 	}
 | |
| 	emitDebugRef(f, id, v, wantAddr)
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // zeroValue emits to f code to produce a zero value of type t,
 | |
| // and returns it.
 | |
| //
 | |
| func zeroValue(f *Function, t types.Type, source ast.Node) Value {
 | |
| 	switch t.Underlying().(type) {
 | |
| 	case *types.Struct, *types.Array:
 | |
| 		return emitLoad(f, f.addLocal(t, source), source)
 | |
| 	default:
 | |
| 		return emitConst(f, zeroConst(t))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func emitConst(f *Function, c *Const) *Const {
 | |
| 	f.consts = append(f.consts, c)
 | |
| 	return c
 | |
| }
 |