Documentation
¶
Overview ¶
Package brenda is a boolean expression solver for Go AST
Index ¶
Examples ¶
- NewSolver (And)
- NewSolver (Bool_lit_1)
- NewSolver (Bool_lit_2)
- NewSolver (Bool_lit_3)
- NewSolver (Brackets1)
- NewSolver (Brackets2)
- NewSolver (Brackets3)
- NewSolver (Brackets4)
- NewSolver (Else)
- NewSolver (Else_if)
- NewSolver (Errors)
- NewSolver (Impossible)
- NewSolver (Invert)
- NewSolver (Invert2)
- NewSolver (Invert_gt)
- NewSolver (Invert_lt)
- NewSolver (Mixed)
- NewSolver (Or)
- NewSolver (Simple)
- NewSolver (Unary)
- NewSolver (Unknown)
- NewSolver (Usage)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Result ¶
type Result struct {
Match bool // Match is true if this component must be true.
Inverse bool // Inverse is true if this component must be false.
}
Result contains information about each result.
type Solver ¶
type Solver struct {
Components map[ast.Expr]*Result // Components is a map of all the individual components of the expression, and the results
Impossible bool // Impossible is true if this expression is impossible
// contains filtered or unexported fields
}
Solver solves boolean expressions given the ast.Expr
func NewSolver ¶
func NewSolver(fset *token.FileSet, uses, defs map[*ast.Ident]types.Object, expression ast.Expr, falseExpressions ...ast.Expr) *Solver
NewSolver returns a new *Solver. fset should be the AST FileSet. uses should be the Uses from go/types.Info. expression is the expression to solve. falseExpressions is a slice of expressions we know to be false - e.g. all previous conditions that came before an else-if statement.
Example (And) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b bool
if a && b {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a && b { // a TRUE // b TRUE } else { // a UNKNOWN // b UNKNOWN }
Example (Bool_lit_1) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
if false {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if false { // IMPOSSIBLE } else { }
Example (Bool_lit_2) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a bool
if a || true {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a || true { // a UNKNOWN } else { // IMPOSSIBLE }
Example (Bool_lit_3) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a bool
if a && false {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a && false { // IMPOSSIBLE } else { // a UNKNOWN }
Example (Brackets1) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b, c bool
if a || (b && c) {} else if a {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a || (b && c) { // a UNKNOWN // b UNKNOWN // c UNKNOWN } else if a { // IMPOSSIBLE } else { // a FALSE // b UNKNOWN // c UNKNOWN }
Example (Brackets2) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b, c bool
if a || (b && c) {} else if b {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a || (b && c) { // a UNKNOWN // b UNKNOWN // c UNKNOWN } else if b { // a FALSE // b TRUE // c FALSE } else { // a FALSE // b FALSE // c UNKNOWN }
Example (Brackets3) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b, c bool
if a && (b || c) {} else if a {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a && (b || c) { // a TRUE // b UNKNOWN // c UNKNOWN } else if a { // a TRUE // b FALSE // c FALSE } else { // a FALSE // b UNKNOWN // c UNKNOWN }
Example (Brackets4) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b, c bool
if a && (b || c) {} else if b {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a && (b || c) { // a TRUE // b UNKNOWN // c UNKNOWN } else if b { // a FALSE // b TRUE // c UNKNOWN } else { // a UNKNOWN // b FALSE // c UNKNOWN }
Example (Else) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a bool
if a {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a { // a TRUE } else { // a FALSE }
Example (Else_if) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b bool
if a {} else if b {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a { // a TRUE } else if b { // a FALSE // b TRUE } else { // a FALSE // b FALSE }
Example (Errors) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a error
if a == nil {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a == nil { // a == nil TRUE } else { // a != nil TRUE }
Example (Impossible) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a bool
if a {} else if !a {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a { // a TRUE } else if !a { // a FALSE } else { // IMPOSSIBLE }
Example (Invert) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
// should correctly detect that b == nil is the inverse of b != nil
var a, b error
if a == nil && b == nil {} else if b != nil {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a == nil && b == nil { // a == nil TRUE // b == nil TRUE } else if b != nil { // a == nil UNKNOWN // b != nil TRUE } else { // a == nil FALSE // b == nil TRUE }
Example (Invert2) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
// should correctly detect that a == nil is the inverse of a != nil
var a error
var b bool
if a == nil && b || a != nil {} else if b {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a == nil && b || a != nil { // a == nil UNKNOWN // b UNKNOWN } else if b { // IMPOSSIBLE } else { // a == nil TRUE // b FALSE }
Example (Invert_gt) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a int
if a > 0 {} else if a <= 0 {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a > 0 { // a > 0 TRUE } else if a <= 0 { // a <= 0 TRUE } else { // IMPOSSIBLE }
Example (Invert_lt) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a int
if a < 0 {} else if a >= 0 {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a < 0 { // a < 0 TRUE } else if a >= 0 { // a >= 0 TRUE } else { // IMPOSSIBLE }
Example (Mixed) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a error
var b, c bool
var d int
if a == nil && (b && d > 0) || c {} else if d <= 0 || c {} else if b {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a == nil && (b && d > 0) || c { // a == nil UNKNOWN // b UNKNOWN // c UNKNOWN // d > 0 UNKNOWN } else if d <= 0 || c { // a == nil UNKNOWN // b UNKNOWN // c FALSE // d <= 0 TRUE } else if b { // a == nil FALSE // b TRUE // c FALSE // d > 0 TRUE }
Example (Or) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b bool
if a || b {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a || b { // a UNKNOWN // b UNKNOWN } else { // a FALSE // b FALSE }
Example (Simple) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a bool
if a { }
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a { // a TRUE }
Example (Unary) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b bool
if !(a || b) {} else {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if !(a || b) { // a FALSE // b FALSE } else { // a UNKNOWN // b UNKNOWN }
Example (Unknown) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
"github.com/pkg/errors"
"os"
)
func main() {
printExample(`
var a, b, c bool
if a && (b || c) {} else if b {}
`)
}
func printExample(src string) {
err := printOutput(os.Stdout, src)
if err != nil {
fmt.Printf("%+v", err)
}
}
func printOutput(writer io.Writer, src string) error {
fpath := "/foo.go"
ppath := "a.b/c"
src = fmt.Sprintf("package a\n\nfunc a() {\n%s\n}", src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fpath, src, parser.ParseComments)
if err != nil {
return errors.Wrap(err, "Error parsing file")
}
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{
Importer: importer.Default(),
}
if _, err = conf.Check(ppath, fset, []*ast.File{f}, &info); err != nil {
return errors.Wrap(err, "Error checking conf")
}
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
return errors.New("No *ast.IfStmt found")
}
err = printIf(writer, fset, info, ifs)
if err != nil {
return err
}
return nil
}
func printIf(writer io.Writer, fset *token.FileSet, info types.Info, expr *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Fprintln(writer, "if", printNode(fset, expr.Cond), "{")
printMatches(writer, fset, s)
switch e := expr.Else.(type) {
case nil:
fmt.Fprintln(writer, "}")
case *ast.BlockStmt:
s := brenda.NewSolver(fset, info.Uses, info.Defs, expr.Cond, falseExpr...)
s.SolveFalse()
fmt.Fprintln(writer, "} else {")
printMatches(writer, fset, s)
fmt.Fprintln(writer, "}")
case *ast.IfStmt:
fmt.Fprint(writer, "} else ")
falseExpr = append(falseExpr, expr.Cond)
printIf(writer, fset, info, e, falseExpr...)
}
return nil
}
func printMatches(writer io.Writer, fset *token.FileSet, s *brenda.Solver) {
if s.Impossible {
fmt.Fprintln(writer, "\t// IMPOSSIBLE")
return
}
var lines []string
for ex, m := range s.Components {
switch {
case m.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " TRUE"))
case m.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, ex), " UNKNOWN"))
}
}
sort.Strings(lines)
fmt.Fprintln(writer, strings.Join(lines, "\n"))
}
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
Output: if a && (b || c) { // a TRUE // b UNKNOWN // c UNKNOWN } else if b { // a FALSE // b TRUE // c UNKNOWN }
Example (Usage) ¶
package main
import (
"bytes"
"go/ast"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
"github.com/dave/brenda"
"fmt"
"sort"
"strings"
)
func printNode(fset *token.FileSet, node ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, node)
if err != nil {
return err.Error()
}
return buf.String()
}
func main() {
// A simple source file
src := `package foo
func foo(a, b bool) {
if a { } else if b { } else { }
}`
// We parse the AST
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "foo.go", src, 0)
if err != nil {
fmt.Println(err)
return
}
// We extract type info
info := types.Info{
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
conf := types.Config{Importer: importer.Default()}
if _, err = conf.Check("foo", fset, []*ast.File{f}, &info); err != nil {
fmt.Println(err)
return
}
// Walk the AST until we find the first *ast.IfStmt
var ifs *ast.IfStmt
ast.Inspect(f, func(node ast.Node) bool {
if ifs != nil {
return false
}
if n, ok := node.(*ast.IfStmt); ok && n != nil {
ifs = n
return false
}
return true
})
if ifs == nil {
fmt.Println("No *ast.IfStmt found")
return
}
var printIf func(*ast.IfStmt, ...ast.Expr) error
var sprintResults func(*brenda.Solver) string
var sprintNode func(ast.Node) string
// This is called recursively for the if and all else-if statements. falseExpr
// is a slice of all the conditions that came before an else-if statement,
// which must all be false for the else-if to be reached.
printIf = func(ifStmt *ast.IfStmt, falseExpr ...ast.Expr) error {
s := brenda.NewSolver(fset, info.Uses, info.Defs, ifStmt.Cond, falseExpr...)
err := s.SolveTrue()
if err != nil {
return err
}
fmt.Printf("if %s {\n%s\n}", sprintNode(ifStmt.Cond), sprintResults(s))
switch e := ifStmt.Else.(type) {
case *ast.BlockStmt:
// Else block
s := brenda.NewSolver(fset, info.Uses, info.Defs, ifStmt.Cond, falseExpr...)
s.SolveFalse()
fmt.Printf(" else {\n%s\n}", sprintResults(s))
case *ast.IfStmt:
// Else if statement
fmt.Print(" else ")
// Add the condition from the current if statement to the list of
// false expressions for the else-if solver
falseExpr = append(falseExpr, ifStmt.Cond)
printIf(e, falseExpr...)
}
return nil
}
// Helper function to print results
sprintResults = func(s *brenda.Solver) string {
if s.Impossible {
// If the expression is impossible
return "\t// IMPOSSIBLE"
}
// The results must be sorted to ensure repeatable output
var lines []string
for expr, result := range s.Components {
switch {
case result.Match:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, expr), " TRUE"))
case result.Inverse:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, expr), " FALSE"))
default:
lines = append(lines, fmt.Sprint("\t// ", printNode(fset, expr), " UNKNOWN"))
}
}
sort.Strings(lines)
return strings.Join(lines, "\n")
}
// Helper function to print AST nodes
sprintNode = func(n ast.Node) string {
buf := &bytes.Buffer{}
err := format.Node(buf, fset, n)
if err != nil {
return err.Error()
}
return buf.String()
}
if err := printIf(ifs); err != nil {
fmt.Println(err)
return
}
}
Output: if a { // a TRUE } else if b { // a FALSE // b TRUE } else { // a FALSE // b FALSE }
func (*Solver) SolveFalse ¶
SolveFalse solves the expression as false - e.g. for the else block of an if statement
Click to show internal directories.
Click to hide internal directories.