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.
This commit is contained in:
James Shubin
2019-02-28 09:35:13 -05:00
parent 94c40909cc
commit 829741e2ac
5 changed files with 73 additions and 2 deletions

View File

@@ -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

View File

@@ -0,0 +1 @@
# err: import scope `something.mcl` failed: local import of `something.mcl` failed: module contains unused statements: found stmt: res(print)

View File

@@ -0,0 +1,7 @@
import "fmt"
import "something.mcl"
include something.someclass
print "p1" {
msg => fmt.printf("someint: %d", $something.someint),
}

View File

@@ -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",
}

View File

@@ -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 {