2017-05-04 14:35:31 +00:00
|
|
|
package proc_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"go/constant"
|
|
|
|
"go/parser"
|
|
|
|
"go/token"
|
|
|
|
"math"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2019-01-04 18:39:25 +00:00
|
|
|
"github.com/go-delve/delve/pkg/goversion"
|
|
|
|
"github.com/go-delve/delve/pkg/proc"
|
|
|
|
protest "github.com/go-delve/delve/pkg/proc/test"
|
2017-05-04 14:35:31 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestScopeWithEscapedVariable(t *testing.T) {
|
2023-08-16 16:55:02 +00:00
|
|
|
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9}) {
|
2017-05-04 14:35:31 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-22 17:26:28 +00:00
|
|
|
withTestProcess("scopeescapevareval", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
|
|
|
assertNoError(grp.Continue(), t, "Continue")
|
2017-05-04 14:35:31 +00:00
|
|
|
|
|
|
|
// On the breakpoint there are two 'a' variables in scope, the one that
|
|
|
|
// isn't shadowed is a variable that escapes to the heap and figures in
|
|
|
|
// debug_info as '&a'. Evaluating 'a' should yield the escaped variable.
|
|
|
|
|
2018-01-20 15:18:39 +00:00
|
|
|
avar := evalVariable(p, t, "a")
|
2017-05-04 14:35:31 +00:00
|
|
|
if aval, _ := constant.Int64Val(avar.Value); aval != 3 {
|
|
|
|
t.Errorf("wrong value for variable a: %d", aval)
|
|
|
|
}
|
|
|
|
|
|
|
|
if avar.Flags&proc.VariableEscaped == 0 {
|
2018-03-20 10:05:35 +00:00
|
|
|
t.Errorf("variable a isn't escaped to the heap")
|
2017-05-04 14:35:31 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestScope will:
|
2022-06-17 17:08:11 +00:00
|
|
|
// - run _fixtures/scopetest.go
|
|
|
|
// - set a breakpoint on all lines containing a comment
|
|
|
|
// - continue until the program ends
|
|
|
|
// - every time a breakpoint is hit it will check that
|
|
|
|
// scope.FunctionArguments+scope.LocalVariables and scope.EvalExpression
|
|
|
|
// return what the corresponding comment describes they should return and
|
|
|
|
// removes the breakpoint.
|
2017-05-04 14:35:31 +00:00
|
|
|
//
|
|
|
|
// Each comment is a comma separated list of variable declarations, with
|
|
|
|
// each variable declaration having the following format:
|
|
|
|
//
|
2022-06-17 17:08:11 +00:00
|
|
|
// name type = initialvalue
|
2017-05-04 14:35:31 +00:00
|
|
|
//
|
|
|
|
// the = and the initial value are optional and can only be specified if the
|
|
|
|
// type is an integer type, float32, float64 or bool.
|
|
|
|
//
|
2019-09-15 18:40:35 +00:00
|
|
|
// If multiple variables with the same name are specified:
|
2022-06-17 17:08:11 +00:00
|
|
|
// 1. LocalVariables+FunctionArguments should return them in the same order and
|
|
|
|
// every variable except the last one should be marked as shadowed
|
|
|
|
// 2. EvalExpression should return the last one.
|
2017-05-04 14:35:31 +00:00
|
|
|
func TestScope(t *testing.T) {
|
2020-03-18 16:25:32 +00:00
|
|
|
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) {
|
2017-05-04 14:35:31 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fixturesDir := protest.FindFixturesDir()
|
|
|
|
scopetestPath := filepath.Join(fixturesDir, "scopetest.go")
|
|
|
|
|
|
|
|
scopeChecks := getScopeChecks(scopetestPath, t)
|
|
|
|
|
2023-02-22 17:26:28 +00:00
|
|
|
withTestProcess("scopetest", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
2017-05-04 14:35:31 +00:00
|
|
|
for i := range scopeChecks {
|
2019-08-14 15:57:05 +00:00
|
|
|
setFileBreakpoint(p, t, fixture.Source, scopeChecks[i].line)
|
2017-05-04 14:35:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf("%d breakpoints set", len(scopeChecks))
|
|
|
|
|
|
|
|
for {
|
2023-02-22 17:26:28 +00:00
|
|
|
if err := grp.Continue(); err != nil {
|
2018-08-31 18:08:18 +00:00
|
|
|
if _, exited := err.(proc.ErrProcessExited); exited {
|
2017-05-04 14:35:31 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
assertNoError(err, t, "Continue()")
|
|
|
|
}
|
2017-09-25 06:29:13 +00:00
|
|
|
bp := p.CurrentThread().Breakpoint()
|
2017-05-04 14:35:31 +00:00
|
|
|
|
|
|
|
scopeCheck := findScopeCheck(scopeChecks, bp.Line)
|
|
|
|
if scopeCheck == nil {
|
|
|
|
t.Errorf("unknown stop position %s:%d %#x", bp.File, bp.Line, bp.Addr)
|
|
|
|
}
|
|
|
|
|
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
|
|
|
scope, _ := scopeCheck.checkLocalsAndArgs(p, t)
|
2017-05-04 14:35:31 +00:00
|
|
|
|
|
|
|
for i := range scopeCheck.varChecks {
|
|
|
|
vc := &scopeCheck.varChecks[i]
|
2019-09-15 18:40:35 +00:00
|
|
|
if vc.shdw {
|
|
|
|
continue
|
2017-05-04 14:35:31 +00:00
|
|
|
}
|
2019-09-15 18:40:35 +00:00
|
|
|
vc.checkInScope(scopeCheck.line, scope, t)
|
2017-05-04 14:35:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
scopeCheck.ok = true
|
2021-09-29 10:01:37 +00:00
|
|
|
err := p.ClearBreakpoint(bp.Addr)
|
2017-05-04 14:35:31 +00:00
|
|
|
assertNoError(err, t, "ClearBreakpoint")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
for i := range scopeChecks {
|
|
|
|
if !scopeChecks[i].ok {
|
|
|
|
t.Errorf("breakpoint at line %d not hit", scopeChecks[i].line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type scopeCheck struct {
|
|
|
|
line int
|
|
|
|
varChecks []varCheck
|
|
|
|
ok bool // this scope check was passed
|
|
|
|
}
|
|
|
|
|
|
|
|
type varCheck struct {
|
|
|
|
name string
|
|
|
|
typ string
|
|
|
|
kind reflect.Kind
|
2019-09-15 18:40:35 +00:00
|
|
|
shdw bool // this variable should be shadowed
|
2017-05-04 14:35:31 +00:00
|
|
|
hasVal bool
|
|
|
|
intVal int64
|
|
|
|
uintVal uint64
|
|
|
|
floatVal float64
|
|
|
|
boolVal bool
|
|
|
|
|
|
|
|
ok bool // this variable check was passed
|
|
|
|
}
|
|
|
|
|
|
|
|
func getScopeChecks(path string, t *testing.T) []scopeCheck {
|
|
|
|
var fset token.FileSet
|
|
|
|
root, err := parser.ParseFile(&fset, path, nil, parser.ParseComments)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not parse %s: %v", path, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
scopeChecks := []scopeCheck{}
|
|
|
|
|
|
|
|
for _, cmtg := range root.Comments {
|
|
|
|
for _, cmt := range cmtg.List {
|
|
|
|
pos := fset.Position(cmt.Slash)
|
|
|
|
|
|
|
|
scopeChecks = append(scopeChecks, scopeCheck{line: pos.Line})
|
|
|
|
scopeChecks[len(scopeChecks)-1].Parse(cmt.Text[2:], t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return scopeChecks
|
|
|
|
}
|
|
|
|
|
|
|
|
func findScopeCheck(scopeChecks []scopeCheck, line int) *scopeCheck {
|
|
|
|
for i := range scopeChecks {
|
|
|
|
if scopeChecks[i].line == line {
|
|
|
|
return &scopeChecks[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (check *scopeCheck) Parse(descr string, t *testing.T) {
|
|
|
|
decls := strings.Split(descr, ",")
|
|
|
|
check.varChecks = make([]varCheck, len(decls))
|
|
|
|
for i, decl := range decls {
|
|
|
|
varcheck := &check.varChecks[i]
|
|
|
|
value := ""
|
|
|
|
if equal := strings.Index(decl, "="); equal >= 0 {
|
|
|
|
value = strings.TrimSpace(decl[equal+1:])
|
|
|
|
decl = strings.TrimSpace(decl[:equal])
|
|
|
|
varcheck.hasVal = true
|
|
|
|
} else {
|
|
|
|
decl = strings.TrimSpace(decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
space := strings.Index(decl, " ")
|
|
|
|
if space < 0 {
|
|
|
|
t.Fatalf("could not parse scope comment %q (%q)", descr, decl)
|
|
|
|
}
|
|
|
|
varcheck.name = strings.TrimSpace(decl[:space])
|
|
|
|
varcheck.typ = strings.TrimSpace(decl[space+1:])
|
2023-11-14 09:16:39 +00:00
|
|
|
if strings.Contains(varcheck.typ, " ") {
|
2017-05-04 14:35:31 +00:00
|
|
|
t.Fatalf("could not parse scope comment %q (%q)", descr, decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !varcheck.hasVal {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch varcheck.typ {
|
|
|
|
case "int", "int8", "int16", "int32", "int64":
|
|
|
|
var err error
|
|
|
|
varcheck.kind = reflect.Int
|
|
|
|
varcheck.intVal, err = strconv.ParseInt(value, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not parse scope comment %q: %v", descr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
case "uint", "uint8", "uint16", "uint32", "uint64", "uintptr":
|
|
|
|
var err error
|
|
|
|
varcheck.kind = reflect.Uint
|
|
|
|
varcheck.uintVal, err = strconv.ParseUint(value, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not parse scope comment %q: %v", descr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
case "float32", "float64":
|
|
|
|
var err error
|
|
|
|
varcheck.kind = reflect.Float64
|
|
|
|
varcheck.floatVal, err = strconv.ParseFloat(value, 64)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not parse scope comment %q: %v", descr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
case "bool":
|
|
|
|
var err error
|
|
|
|
varcheck.kind = reflect.Bool
|
|
|
|
varcheck.boolVal, err = strconv.ParseBool(value)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not parse scope comment %q: %v", descr, err)
|
|
|
|
}
|
2019-09-15 18:40:35 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-04 14:35:31 +00:00
|
|
|
|
2019-09-15 18:40:35 +00:00
|
|
|
for i := 1; i < len(check.varChecks); i++ {
|
|
|
|
if check.varChecks[i-1].name == check.varChecks[i].name {
|
|
|
|
check.varChecks[i-1].shdw = true
|
2017-05-04 14:35:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-17 21:24:43 +00:00
|
|
|
func (check *scopeCheck) checkLocalsAndArgs(p *proc.Target, t *testing.T) (*proc.EvalScope, bool) {
|
2021-07-02 16:37:55 +00:00
|
|
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
|
|
|
assertNoError(err, t, "GoroutineScope()")
|
|
|
|
|
|
|
|
ok := true
|
|
|
|
|
|
|
|
args, err := scope.FunctionArguments(normalLoadConfig)
|
|
|
|
assertNoError(err, t, "FunctionArguments()")
|
|
|
|
locals, err := scope.LocalVariables(normalLoadConfig)
|
|
|
|
assertNoError(err, t, "LocalVariables()")
|
|
|
|
|
|
|
|
for _, arg := range args {
|
2023-08-17 21:24:43 +00:00
|
|
|
check.checkVar(arg, t)
|
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, local := range locals {
|
2023-08-17 21:24:43 +00:00
|
|
|
check.checkVar(local, t)
|
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
|
|
|
}
|
|
|
|
|
2023-08-17 21:24:43 +00:00
|
|
|
for i := range check.varChecks {
|
|
|
|
if !check.varChecks[i].ok {
|
|
|
|
t.Errorf("%d: variable %s not found", check.line, check.varChecks[i].name)
|
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
|
|
|
ok = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return scope, ok
|
|
|
|
}
|
|
|
|
|
2017-05-04 14:35:31 +00:00
|
|
|
func (check *scopeCheck) checkVar(v *proc.Variable, t *testing.T) {
|
|
|
|
var varCheck *varCheck
|
|
|
|
for i := range check.varChecks {
|
2017-11-06 17:44:38 +00:00
|
|
|
if !check.varChecks[i].ok && (check.varChecks[i].name == v.Name) {
|
2017-05-04 14:35:31 +00:00
|
|
|
varCheck = &check.varChecks[i]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if varCheck == nil {
|
|
|
|
t.Errorf("%d: unexpected variable %s", check.line, v.Name)
|
2017-11-06 17:44:38 +00:00
|
|
|
return
|
2017-05-04 14:35:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
varCheck.check(check.line, v, t, "FunctionArguments+LocalVariables")
|
|
|
|
varCheck.ok = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (varCheck *varCheck) checkInScope(line int, scope *proc.EvalScope, t *testing.T) {
|
2021-11-26 16:06:23 +00:00
|
|
|
v, err := scope.EvalExpression(varCheck.name, normalLoadConfig)
|
2017-05-04 14:35:31 +00:00
|
|
|
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", varCheck.name))
|
|
|
|
varCheck.check(line, v, t, "EvalExpression")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (varCheck *varCheck) check(line int, v *proc.Variable, t *testing.T, ctxt string) {
|
|
|
|
typ := v.DwarfType.String()
|
2023-02-14 17:36:24 +00:00
|
|
|
typ = strings.ReplaceAll(typ, " ", "")
|
2017-05-04 14:35:31 +00:00
|
|
|
if typ != varCheck.typ {
|
|
|
|
t.Errorf("%d: wrong type for %s (%s), got %s, expected %s", line, v.Name, ctxt, typ, varCheck.typ)
|
|
|
|
}
|
|
|
|
|
2019-09-15 18:40:35 +00:00
|
|
|
if varCheck.shdw && v.Flags&proc.VariableShadowed == 0 {
|
|
|
|
t.Errorf("%d: expected shadowed %s variable", line, v.Name)
|
|
|
|
}
|
|
|
|
|
2017-05-04 14:35:31 +00:00
|
|
|
if !varCheck.hasVal {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch varCheck.kind {
|
|
|
|
case reflect.Int:
|
|
|
|
if vv, _ := constant.Int64Val(v.Value); vv != varCheck.intVal {
|
|
|
|
t.Errorf("%d: wrong value for %s (%s), got %d expected %d", line, v.Name, ctxt, vv, varCheck.intVal)
|
|
|
|
}
|
|
|
|
case reflect.Uint:
|
|
|
|
if vv, _ := constant.Uint64Val(v.Value); vv != varCheck.uintVal {
|
|
|
|
t.Errorf("%d: wrong value for %s (%s), got %d expected %d", line, v.Name, ctxt, vv, varCheck.uintVal)
|
|
|
|
}
|
|
|
|
case reflect.Float64:
|
|
|
|
if vv, _ := constant.Float64Val(v.Value); math.Abs(vv-varCheck.floatVal) > 0.001 {
|
|
|
|
t.Errorf("%d: wrong value for %s (%s), got %g expected %g", line, v.Name, ctxt, vv, varCheck.floatVal)
|
|
|
|
}
|
|
|
|
case reflect.Bool:
|
|
|
|
if vv := constant.BoolVal(v.Value); vv != varCheck.boolVal {
|
|
|
|
t.Errorf("%d: wrong value for %s (%s), got %v expected %v", line, v.Name, ctxt, vv, varCheck.boolVal)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|