rpcbinding: check duplicating struct fields before binding generation
RPC binding config may be malformed or the source .go contract may contain structures like this: ``` type Str struct { Field int field int } ``` We need to recognise these cases and return error. otherwise the resulting binding can't be compiled. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
b4c0fcfaad
commit
16d1d1e5eb
8 changed files with 139 additions and 0 deletions
|
@ -562,3 +562,51 @@ func TestCompile_GuessEventTypes(t *testing.T) {
|
|||
check(t, filepath.Join("testdata", "rpcbindings", "invalid5"), "configured declared named type intersects with the contract's one: `invalid5.NamedStruct`")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateRPCBindings_Errors(t *testing.T) {
|
||||
app := cli.NewApp()
|
||||
app.Commands = NewCommands()
|
||||
app.ExitErrHandler = func(*cli.Context, error) {}
|
||||
|
||||
t.Run("duplicating resulting fields", func(t *testing.T) {
|
||||
check := func(t *testing.T, packageName string, autogen bool, expectedError string) {
|
||||
tmpDir := t.TempDir()
|
||||
source := filepath.Join("testdata", "rpcbindings", packageName)
|
||||
configFile := filepath.Join(source, "invalid.yml")
|
||||
out := filepath.Join(tmpDir, "rpcbindings.out")
|
||||
manifestF := filepath.Join(tmpDir, "manifest.json")
|
||||
bindingF := filepath.Join(tmpDir, "binding.yml")
|
||||
nefF := filepath.Join(tmpDir, "out.nef")
|
||||
cmd := []string{"", "contract", "compile",
|
||||
"--in", source,
|
||||
"--config", configFile,
|
||||
"--manifest", manifestF,
|
||||
"--bindings", bindingF,
|
||||
"--out", nefF,
|
||||
}
|
||||
if autogen {
|
||||
cmd = append(cmd, "--guess-eventtypes")
|
||||
}
|
||||
require.NoError(t, app.Run(cmd))
|
||||
|
||||
cmds := []string{"", "contract", "generate-rpcwrapper",
|
||||
"--config", bindingF,
|
||||
"--manifest", manifestF,
|
||||
"--out", out,
|
||||
}
|
||||
err := app.Run(cmds)
|
||||
require.Error(t, err)
|
||||
require.True(t, strings.Contains(err.Error(), expectedError), err.Error())
|
||||
}
|
||||
|
||||
t.Run("event", func(t *testing.T) {
|
||||
check(t, "invalid6", false, "error during generation: named type `SomeStruct` has two fields with identical resulting binding name `Field`")
|
||||
})
|
||||
t.Run("autogen event", func(t *testing.T) {
|
||||
check(t, "invalid7", true, "error during generation: named type `invalid7.SomeStruct` has two fields with identical resulting binding name `Field`")
|
||||
})
|
||||
t.Run("struct", func(t *testing.T) {
|
||||
check(t, "invalid8", false, "error during generation: named type `invalid8.SomeStruct` has two fields with identical resulting binding name `Field`")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
14
cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go
vendored
Normal file
14
cli/smartcontract/testdata/rpcbindings/invalid6/invalid.go
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
package invalid6
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
type SomeStruct struct {
|
||||
Field int
|
||||
// RPC binding generator will convert this field into exported, which matches
|
||||
// exactly the existing Field.
|
||||
field int
|
||||
}
|
||||
|
||||
func Main() {
|
||||
runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123})
|
||||
}
|
18
cli/smartcontract/testdata/rpcbindings/invalid6/invalid.yml
vendored
Normal file
18
cli/smartcontract/testdata/rpcbindings/invalid6/invalid.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
name: Test duplicating event fields
|
||||
events:
|
||||
- name: SomeEvent
|
||||
parameters:
|
||||
- name: p1
|
||||
type: Struct
|
||||
extendedtype:
|
||||
base: Struct
|
||||
name: SomeStruct
|
||||
namedtypes:
|
||||
SomeStruct:
|
||||
base: Struct
|
||||
name: SomeStruct
|
||||
fields:
|
||||
- field: Field
|
||||
base: Integer
|
||||
- field: field
|
||||
base: Integer
|
14
cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go
vendored
Normal file
14
cli/smartcontract/testdata/rpcbindings/invalid7/invalid.go
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
package invalid7
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
type SomeStruct struct {
|
||||
Field int
|
||||
// RPC binding generator will convert this field into exported, which matches
|
||||
// exactly the existing Field.
|
||||
field int
|
||||
}
|
||||
|
||||
func Main() {
|
||||
runtime.Notify("SomeEvent", SomeStruct{Field: 123, field: 123})
|
||||
}
|
6
cli/smartcontract/testdata/rpcbindings/invalid7/invalid.yml
vendored
Normal file
6
cli/smartcontract/testdata/rpcbindings/invalid7/invalid.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
name: Test duplicating autogenerated event fields
|
||||
events:
|
||||
- name: SomeEvent
|
||||
parameters:
|
||||
- name: p1
|
||||
type: Struct
|
16
cli/smartcontract/testdata/rpcbindings/invalid8/invalid.go
vendored
Normal file
16
cli/smartcontract/testdata/rpcbindings/invalid8/invalid.go
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package invalid8
|
||||
|
||||
type SomeStruct struct {
|
||||
Field int
|
||||
// RPC binding generator will convert this field into exported, which matches
|
||||
// exactly the existing Field.
|
||||
field int
|
||||
}
|
||||
|
||||
func Main() SomeStruct {
|
||||
s := SomeStruct{
|
||||
Field: 1,
|
||||
field: 2,
|
||||
}
|
||||
return s
|
||||
}
|
1
cli/smartcontract/testdata/rpcbindings/invalid8/invalid.yml
vendored
Normal file
1
cli/smartcontract/testdata/rpcbindings/invalid8/invalid.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
name: Test duplicating struct fields
|
|
@ -430,6 +430,28 @@ func Generate(cfg binding.Config) error {
|
|||
ctr = scTemplateToRPC(cfg, ctr, imports, scTypeToGo)
|
||||
ctr.NamedTypes = cfg.NamedTypes
|
||||
|
||||
// Check resulting named types and events don't have duplicating field names.
|
||||
for _, t := range ctr.NamedTypes {
|
||||
fDict := make(map[string]struct{})
|
||||
for _, n := range t.Fields {
|
||||
name := upperFirst(n.Field)
|
||||
if _, ok := fDict[name]; ok {
|
||||
return fmt.Errorf("named type `%s` has two fields with identical resulting binding name `%s`", t.Name, name)
|
||||
}
|
||||
fDict[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, e := range ctr.CustomEvents {
|
||||
fDict := make(map[string]struct{})
|
||||
for _, n := range e.Parameters {
|
||||
name := upperFirst(n.Name)
|
||||
if _, ok := fDict[name]; ok {
|
||||
return fmt.Errorf("event `%s` has two fields with identical resulting binding name `%s`", e.Name, name)
|
||||
}
|
||||
fDict[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{
|
||||
"addIndent": addIndent,
|
||||
"etTypeConverter": etTypeConverter,
|
||||
|
|
Loading…
Reference in a new issue