From 829741e2ac0fcca8aea2b00a759afec0905d8d28 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Thu, 28 Feb 2019 09:35:13 -0500 Subject: [PATCH] lang: Print a clear message on module import containing unused stmt If you run an import, you only include everything that's part of a scope. This includes, variables, classes, and functions. Anything else should cause a compile error. This cleans up the error by adding a String() method to each Stmt in our AST. --- lang/interfaces/ast.go | 2 + .../interpret_test/TestAstFunc1/unused1.graph | 1 + .../TestAstFunc1/unused1/main.mcl | 7 +++ .../TestAstFunc1/unused1/something.mcl | 11 ++++ lang/structs.go | 54 ++++++++++++++++++- 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 lang/interpret_test/TestAstFunc1/unused1.graph create mode 100644 lang/interpret_test/TestAstFunc1/unused1/main.mcl create mode 100644 lang/interpret_test/TestAstFunc1/unused1/something.mcl diff --git a/lang/interfaces/ast.go b/lang/interfaces/ast.go index 63ea6766..3053aaaa 100644 --- a/lang/interfaces/ast.go +++ b/lang/interfaces/ast.go @@ -40,6 +40,7 @@ type Node interface { // expression.) type Stmt interface { Node + fmt.Stringer // String() string Init(*Data) error // initialize the populated node and validate Interpolate() (Stmt, error) // return expanded form of AST as a new AST SetScope(*Scope) error // set the scope here and propagate it downwards @@ -54,6 +55,7 @@ type Stmt interface { // these can be stored as pointers in our graph data structure. type Expr interface { Node + //fmt.Stringer // already provided by pgraph.Vertex pgraph.Vertex // must implement this since we store these in our graphs Init(*Data) error // initialize the populated node and validate Interpolate() (Expr, error) // return expanded form of AST as a new AST diff --git a/lang/interpret_test/TestAstFunc1/unused1.graph b/lang/interpret_test/TestAstFunc1/unused1.graph new file mode 100644 index 00000000..0832494b --- /dev/null +++ b/lang/interpret_test/TestAstFunc1/unused1.graph @@ -0,0 +1 @@ +# err: import scope `something.mcl` failed: local import of `something.mcl` failed: module contains unused statements: found stmt: res(print) diff --git a/lang/interpret_test/TestAstFunc1/unused1/main.mcl b/lang/interpret_test/TestAstFunc1/unused1/main.mcl new file mode 100644 index 00000000..c8d1fd1a --- /dev/null +++ b/lang/interpret_test/TestAstFunc1/unused1/main.mcl @@ -0,0 +1,7 @@ +import "fmt" +import "something.mcl" + +include something.someclass +print "p1" { + msg => fmt.printf("someint: %d", $something.someint), +} diff --git a/lang/interpret_test/TestAstFunc1/unused1/something.mcl b/lang/interpret_test/TestAstFunc1/unused1/something.mcl new file mode 100644 index 00000000..6a9d5c64 --- /dev/null +++ b/lang/interpret_test/TestAstFunc1/unused1/something.mcl @@ -0,0 +1,11 @@ +$someint = 42 +class someclass { + print "p2" { + msg => "i'm inside of someclass", + } +} + +# this should generate a compile error +print "unused" { + msg => "i'm unused because i'm inside an imported module", +} diff --git a/lang/structs.go b/lang/structs.go index b85fec22..f0425650 100644 --- a/lang/structs.go +++ b/lang/structs.go @@ -90,6 +90,11 @@ func (obj *StmtBind) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtBind) String() string { + return fmt.Sprintf("bind(%s)", obj.Ident) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtBind) Init(data *interfaces.Data) error { @@ -181,6 +186,11 @@ func (obj *StmtRes) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtRes) String() string { + return fmt.Sprintf("res(%s)", obj.Kind) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtRes) Init(data *interfaces.Data) error { @@ -1406,6 +1416,11 @@ func (obj *StmtEdge) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtEdge) String() string { + return "edge" // TODO: improve this +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtEdge) Init(data *interfaces.Data) error { @@ -1731,6 +1746,11 @@ func (obj *StmtIf) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtIf) String() string { + return "if" // TODO: improve this +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtIf) Init(data *interfaces.Data) error { @@ -1948,6 +1968,11 @@ func (obj *StmtProg) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtProg) String() string { + return "prog" // TODO: improve this +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtProg) Init(data *interfaces.Data) error { @@ -2678,8 +2703,8 @@ func (obj *StmtProg) IsModuleUnsafe() error { // TODO: rename this function? case *StmtComment: // possibly not even parsed // all of these are safe default: - // something else unsafe - return fmt.Errorf("found unsafe stmt: %v", x) + // something else unsafe (unused) + return fmt.Errorf("found stmt: %s", x.String()) } } return nil @@ -2706,6 +2731,11 @@ func (obj *StmtFunc) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtFunc) String() string { + return fmt.Sprintf("func(%s)", obj.Name) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtFunc) Init(data *interfaces.Data) error { @@ -2787,6 +2817,11 @@ func (obj *StmtClass) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtClass) String() string { + return fmt.Sprintf("class(%s)", obj.Name) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtClass) Init(data *interfaces.Data) error { @@ -2879,6 +2914,11 @@ func (obj *StmtInclude) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtInclude) String() string { + return fmt.Sprintf("include(%s)", obj.Name) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtInclude) Init(data *interfaces.Data) error { @@ -3089,6 +3129,11 @@ type StmtImport struct { // a select number of node types, since they won't need extra noop iterators... func (obj *StmtImport) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtImport) String() string { + return fmt.Sprintf("import(%s)", obj.Name) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtImport) Init(*interfaces.Data) error { return nil } @@ -3155,6 +3200,11 @@ type StmtComment struct { // a select number of node types, since they won't need extra noop iterators... func (obj *StmtComment) Apply(fn func(interfaces.Node) error) error { return fn(obj) } +// String returns a short representation of this statement. +func (obj *StmtComment) String() string { + return fmt.Sprintf("comment(%s)", obj.Value) +} + // Init initializes this branch of the AST, and returns an error if it fails to // validate. func (obj *StmtComment) Init(*interfaces.Data) error {