Browse Source

Delete duplicated `rc`

content-update
Augusto 4 years ago committed by xpetit
parent
commit
4fba39731e
  1. 143
      rc/README.md
  2. 849
      rc/rc.go
  3. 153
      rc/rc_test.go
  4. 11
      rc/tests/alphabet/main.go
  5. 214
      rc/tests/doopprog/main.go
  6. 86
      rc/tests/eightqueens.go
  7. 0
      rc/tests/empty/empty
  8. 0
      rc/tests/empty/empty.go
  9. 25
      rc/tests/example.go
  10. 16
      rc/tests/nesting/main.go
  11. 29
      rc/tests/printalphabet/printalphabet.go
  12. 19
      rc/tests/testingRepetitions/main.go
  13. 11
      rc/tests/testingSimpleFunc.go
  14. 12
      rc/tests/testingWrapping.go
  15. 23
      rc/tests/util/util.go
  16. 5
      rc/tests/utilDepth2/wrapper.go

143
rc/README.md

@ -1,143 +0,0 @@
## rc (restrictions checker)
This program analyses a go source file and displays in standard output the imports, functions, slices types and loops used without authorization.
### By default:
- Allowed
- All functions declared inside the source file are allowed
- Slices of all types are allowed
- Loops are allowed
- Relative imports are allowed
- Disallowed
- NO imports are allowed
- NO built-in functions are allowed.
- NO casting is allowed
### Flags
- Defined flags:
- `-h` for help
- `-cast` allows casting to every built-in type.
- `-no-for` prohibits the use of `for` loops in the program or function.
- `-allow-builtin` allows all builtin functions and casting to builtin types
- `-no-slices` disallows the use of all slices types
- `-no-these-slices=type1,type2`: disallows the slices of type1 and type2
- `-no-relative-imports`: disallows the use of relative imports
### Arguments:
- Flags must be passed first, before any other argument
- After the flags the first argument must be the file to be analysed
- The remaining arguments represent the allowed functions
- Allowed imports and functions from a package
- `<package>.*` for full imports (all functions from that package are allowed)
- `<package>`.`<function>` for partial imports (only the function is allowed)
- `<package>`.`<function>#amount` the function is only allowed to be used `amount` number of times
- Ex: `fmt.*` (all functions from `fmt` are allowed), `github.com/01-edu/z01.PrintRune` (only `z01.PrintRune` is allowed), `fmt.Println#2` (fmt.Println can only be used 2 times or less)
- Allowed built-in functions
- Use the name of the built-in function
- It is posible to limit the number of calls of a functions like with the imports using the '#' character
- Ex: `make`, `append`, `len`, `print#2`.
- Allowed casting
- by using the type of casting, ex: for allowing `string` casting, use `string`
- Or use the flag `-cast`, to allow every type of casting
- Import relative packages
- Use the relative path
- Ex: `../piscine`, `..`, `.`
- Disallow `for` loops
- Use the flags `-no-for`.
- Disallow all aslices types.
- Use `-no-slices`
- Unallow literals
- Use the flag `--no-lit="{PATTERN}"`
- Note: `"{PATTERN}"` must be a valid Regular Expression.
- ex:
```console
_$ rc -no-slices --no-lit=[b-yB-Y] main.go fmt.* github.com/01-edu/z01.PrintRune len
```
### Example:
- To allow the import of the whole `fmt` package, `z01.PrintRune` and the built-in functions `len` for the file `main.go`
Note: The imports must be writen exactly the way they are writen inside the source code, example:
```console
_$ rc main.go fmt.* github.com/01-edu/z01.PrintRune len
```
- Import "fmt" is allowed by executing
```console
_$ rc sourcefile.go fmt.*
```
- Import "go/parser" is allowed by executing
```console
_$ rc sourcefile.go go/parser.*
```
- Import "github.com/01-edu/z01" is allowed by executing
```console
_$ rc sourcefile.go github.com/01-edu/z01.*
```
- Allow all type of casting
```console
_$ rc -cast sourcefile.go fmt.* github.com/01-edu/z01 os.* strconv.* make len append
```
- this will allow all type of casting in the file sourcefile.go
- Disallow the use of the slices of type `string` and `int`
```console
_$ rc -no-these-slices=string,int sourcefile.go
```
- To allow just one type of casting
```console
_$ rc sourcefile.go fmt.* github.com/01-edu/z01 os.* strconv.* make len append rune
```
- this will allow the casting to `rune`, but not `int8`, ..., `string`, `float32`, ...
### How to read the error message
Let us look to an example snipped of code, let us imagine this code in a file called `main.go`:
```go
package main
import "fmt"
func main() {
for _, v := range "abcdefghijklmnopqrstuvwxyz" {
fmt.Println(v)
}
fmt.Println()
}
```
Now let us run the `rc` and understand the message
```console
_$ rc main.go github.com/01-edu/z01.PrintRune
Parsing:
Ok
Cheating:
TYPE: NAME: LOCATION:
illegal-import fmt main.go:3:8
illegal-access fmt.Println main.go:7:3
illegal-access fmt.Println main.go:10:2
illegal-definition main main.go:5:1
```
The important part is printed after the `Cheating` tag:
- The import of of the package `fmt` is not allowed
- In go the dot (.) is also known as the access operator for that reason the use of fmt.Println is shown as an illegal-access
- Finally the main function is shown as illegal-definition because the function is using unallowed functions that does not mean that the function can not be defined it just mean that the definition of the function must be changed to not use disallowed functions.
- Notice that the third column of the output with the tag "LOCATION:" show the location in the following way filepath:line:column
This mean that you have to substitute the illegal function for ones that are allowed or write your own function with allowed functions

849
rc/rc.go

@ -1,849 +0,0 @@
package main
import (
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
table "github.com/tatsushid/go-prettytable"
)
type strBoolMap map[string]bool
// Implementation of the flag.Value interface
func (a *strBoolMap) String() (res string) {
for k, _ := range *a {
res += k
}
return res
}
// Implementation of the flag.Value interface
func (a *strBoolMap) Set(str string) error {
if *a == nil {
*a = make(map[string]bool)
}
s := strings.Split(str, ",")
for _, v := range s {
(*a)[v] = true
}
return nil
}
// Flag that groups a boolean value and a regular expression
type regexpFlag struct {
active bool
reg *regexp.Regexp
}
// Implementation of the flag.Value interface
func (r *regexpFlag) String() string {
if r.reg != nil {
return r.reg.String()
}
return ""
}
// Implementation of the flag.Value interface
func (r *regexpFlag) Set(s string) error {
r.active = true
r.reg = regexp.MustCompile(s)
return nil
}
var (
allowedFun = make(map[string]map[string]bool)
allowedRep = make(map[string]int)
// Flags
noArrays bool
noSlices bool
noRelativeImports bool
noTheseSlices strBoolMap
casting bool
noFor bool
noLit regexpFlag
allowBuiltin bool
)
type illegal struct {
T string
Name string
Pos string
}
func (i *illegal) String() string {
return i.T + " " + i.Name + " " + i.Pos
}
func init() {
flag.Var(&noTheseSlices, "no-these-slices", "Disallowes the slice types passed in the flag as a comma-separated list without spaces\nLike so: -no-these-slices=int,string,bool")
flag.Var(&noLit, "no-lit",
`The use of basic literals (strings or characters) matching the pattern -no-lit="{PATTERN}"
passed to the program would not be allowed`,
)
flag.BoolVar(&noRelativeImports, "no-relative-imports", false, `Disallowes the use of relative imports`)
flag.BoolVar(&noFor, "no-for", false, `The "for" instruction is not allowed`)
flag.BoolVar(&casting, "cast", false, "Allowes casting")
flag.BoolVar(&noArrays, "no-arrays", false, "Deprecated: use -no-slices")
flag.BoolVar(&noSlices, "no-slices", false, "Disallowes all slice types")
flag.BoolVar(&allowBuiltin, "allow-builtin", false, "Allowes all builtin functions and casting")
sort.Sort(sort.StringSlice(os.Args[1:]))
}
func main() {
flag.Parse()
filename := goFile(flag.Args())
if filename == "" {
fmt.Println("\tNo file to analyze")
os.Exit(1)
}
err := parseArgs(flag.Args(), allowBuiltin, casting)
if err != nil {
fmt.Printf("\t%s\n", err)
os.Exit(1)
}
load := make(loadedSource)
currentPath := filepath.Dir(filename)
err = loadProgram(currentPath, load)
if err != nil {
fmt.Printf("\t%s\n", err)
os.Exit(1)
}
info := analyzeProgram(filename, currentPath, load)
if info.illegals != nil {
fmt.Println("Cheating:")
printIllegals(info.illegals)
os.Exit(1)
}
}
func goFile(args []string) string {
for _, v := range args {
if strings.HasSuffix(v, ".go") {
return v
}
}
return ""
}
// Returns the smallest block containing the position pos. It can
// return nil if `pos` is not inside any ast.BlockStmt
func smallestBlock(pos token.Pos, blocks []*ast.BlockStmt) (minBlock *ast.BlockStmt) {
var minSize token.Pos
for _, v := range blocks {
if pos > v.Pos() && pos < v.End() {
size := v.End() - v.Pos()
if minBlock == nil || size < minSize {
minBlock = v
minSize = size
}
}
}
return minBlock
}
// Used to mark an ast.Object as a function parameter
type data struct {
parameter bool
}
func fillScope(funcDefs []*function, scope *ast.Scope, scopes map[*ast.BlockStmt]*ast.Scope) {
for _, fun := range funcDefs {
scope.Insert(fun.obj)
for _, name := range fun.params {
obj := ast.NewObj(ast.Fun, name)
obj.Data = data{
parameter: true,
}
scopes[fun.body].Insert(obj)
}
}
}
// Create the scopes for a BlockStmt contained inside another BlockStmt
func createChildScope(
block *ast.BlockStmt,
l *loadVisitor, scopes map[*ast.BlockStmt]*ast.Scope) {
blocks := l.blocks
// The smalles block containing the beggining of the block
parentBlock := smallestBlock(block.Pos(), blocks)
if scopes[parentBlock] == nil {
createChildScope(parentBlock, l, scopes)
}
scopes[block] = ast.NewScope(scopes[parentBlock])
}
// Returns true if `block` is contained inside another ast.BlockStmt
func isContained(block *ast.BlockStmt, blocks []*ast.BlockStmt) bool {
for _, v := range blocks {
if block == v {
continue
}
if block.Pos() > v.Pos() && block.End() < v.End() {
return true
}
}
return false
}
// Creates all the scopes in the package
func createScopes(l *loadVisitor, pkgScope *ast.Scope) map[*ast.BlockStmt]*ast.Scope {
scopes := make(map[*ast.BlockStmt]*ast.Scope)
if l.blocks == nil {
return nil
}
for _, b := range l.blocks {
if !isContained(b, l.blocks) {
scopes[b] = ast.NewScope(pkgScope)
}
}
for _, b := range l.blocks {
if scopes[b] == nil {
createChildScope(b, l, scopes)
}
}
return scopes
}
type blockVisitor struct {
funct []*function
// All functions defined in the scope in any
// way: as a funcDecl, GenDecl or AssigmentStmt
oneBlock bool
// Indicates if the visitor already encounter a
// blockStmt
}
func (b *blockVisitor) Visit(n ast.Node) ast.Visitor {
switch t := n.(type) {
case *ast.BlockStmt:
if b.oneBlock {
return nil
}
return b
case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt:
def := extractFunction(t)
if def == nil || def.obj == nil {
return b
}
b.funct = append(b.funct, def)
return nil
default:
return b
}
}
type loadedSource map[string]*loadVisitor
// Returns information about the function defined in the block node
func functionsInfo(block ast.Node) []*function {
b := &blockVisitor{}
ast.Walk(b, block)
return b.funct
}
func (l *loadVisitor) init() {
l.functions = make(map[string]ast.Node)
l.absImports = make(map[string]*element)
l.relImports = make(map[string]*element)
l.objFunc = make(map[*ast.Object]ast.Node)
l.fset = token.NewFileSet()
l.scopes = make(map[*ast.BlockStmt]*ast.Scope)
}
func loadProgram(path string, load loadedSource) error {
l := &loadVisitor{}
l.init()
pkgs, err := parser.ParseDir(l.fset, path, nil, parser.AllErrors)
if err != nil {
return err
}
for _, pkg := range pkgs {
ast.Walk(l, pkg)
l.pkgScope = ast.NewScope(nil)
functions := functionsInfo(pkg)
for _, f := range functions {
l.pkgScope.Insert(f.obj)
}
l.scopes = createScopes(l, l.pkgScope)
fillScope(functions, l.pkgScope, l.scopes)
for block, scope := range l.scopes {
functions := functionsInfo(block)
fillScope(functions, scope, l.scopes)
}
load[path] = l
l.files = pkg.Files
}
for _, relativePath := range l.relImports {
if load[relativePath.name] == nil {
newPath := filepath.Clean(path + "/" + relativePath.name)
err = loadProgram(newPath, load)
if err != nil {
return err
}
}
}
return nil
}
func smallestScopeContaining(pos token.Pos, path string, load loadedSource) *ast.Scope {
pack := load[path]
sm := smallestBlock(pos, pack.blocks)
if sm == nil {
return pack.pkgScope
}
return pack.scopes[sm]
}
func lookupDefinitionObj(el *element, path string, load loadedSource) *ast.Object {
scope := smallestScopeContaining(el.pos, path, load)
for scope != nil {
obj := scope.Lookup(el.name)
if obj != nil {
return obj
}
scope = scope.Outer
}
return nil
}
type visitor struct {
fset *token.FileSet
uses []*element
selections map[string][]*element
arrays []*occurrence
lits []*occurrence
fors []*occurrence
callRepetition map[string]int
oneTime bool
}
func (v *visitor) getPos(n ast.Node) string {
return v.fset.Position(n.Pos()).String()
}
func (v *visitor) Visit(n ast.Node) ast.Visitor {
switch t := n.(type) {
case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt:
//Avoids analyzing a declaration inside a declaration
//Since this is handle by the functions `isAllowed`
fdef := extractFunction(t)
if fdef == nil || fdef.obj == nil {
return v
}
if v.oneTime {
return nil
}
v.oneTime = true
return v
case *ast.BasicLit:
if t.Kind != token.CHAR && t.Kind != token.STRING {
return nil
}
v.lits = append(v.lits, &occurrence{pos: v.getPos(n), name: t.Value})
case *ast.ArrayType:
if op, ok := t.Elt.(*ast.Ident); ok {
v.arrays = append(v.arrays, &occurrence{
name: op.Name,
pos: v.getPos(n),
})
}
case *ast.ForStmt:
v.fors = append(v.fors, &occurrence{
name: "for",
pos: v.getPos(n),
})
case *ast.CallExpr:
if fun, ok := t.Fun.(*ast.Ident); ok {
v.uses = append(v.uses, &element{
name: fun.Name,
pos: fun.Pos(),
})
v.callRepetition[fun.Name]++
}
case *ast.SelectorExpr:
if x, ok := t.X.(*ast.Ident); ok {
v.selections[x.Name] = append(v.selections[x.Name], &element{
name: t.Sel.Name,
pos: n.Pos(),
})
v.callRepetition[x.Name+"."+t.Sel.Name]++
}
}
return v
}
func (v *visitor) init(fset *token.FileSet) {
v.selections = make(map[string][]*element)
v.callRepetition = make(map[string]int)
v.fset = fset
}
func (info *info) add(v *visitor) {
info.fors = append(info.fors, v.fors...)
info.lits = append(info.lits, v.lits...)
info.arrays = append(info.arrays, v.arrays...)
for name, v := range v.callRepetition {
info.callRepetition[name] += v
}
}
// Returns the info structure with all the ocurrences of the element
// of the analised in the project
func isAllowed(function *element, path string, load loadedSource, walked map[ast.Node]bool, info *info) bool {
functionObj := lookupDefinitionObj(function, path, load)
definedLocally := functionObj != nil
explicitlyAllowed := allowedFun["builtin"]["*"] || allowedFun["builtin"][function.name]
isFunctionParameter := func(function *ast.Object) bool {
arg, ok := function.Data.(data)
return ok && arg.parameter
}
DoesntCallMoreFunctions := func(functionDefinition ast.Node, v *visitor) bool {
if !walked[functionDefinition] {
ast.Walk(v, functionDefinition)
info.add(v)
walked[functionDefinition] = true
}
return v.uses == nil && v.selections == nil
}
appendIllegalCall := func(function *element) {
info.illegals = append(info.illegals, &illegal{
T: "illegal-call",
Name: function.name,
Pos: load[path].fset.Position(function.pos).String(),
})
}
if !definedLocally && !explicitlyAllowed {
appendIllegalCall(function)
return false
}
functionDefinition := load[path].objFunc[functionObj]
v := &visitor{}
v.init(load[path].fset)
if explicitlyAllowed || isFunctionParameter(functionObj) ||
DoesntCallMoreFunctions(functionDefinition, v) {
return true
}
allowed := true
for _, functionCall := range v.uses {
if !isAllowed(functionCall, path, load, walked, info) {
appendIllegalCall(functionCall)
allowed = false
}
}
for pck, funcNames := range v.selections {
pathToFunction := func() string { return load[path].relImports[pck].name }
isRelativeImport := load[path].relImports[pck] != nil
for _, fun := range funcNames {
appendIllegalAccess := func() {
info.illegals = append(info.illegals, &illegal{
T: "illegal-access",
Name: pck + "." + fun.name,
Pos: load[path].fset.Position(fun.pos).String(),
})
allowed = false
}
absoluteImport := load[path].absImports[pck]
importExplicitlyAllowed := absoluteImport == nil ||
allowedFun[absoluteImport.name][fun.name] ||
allowedFun[absoluteImport.name]["*"]
if !isRelativeImport && !importExplicitlyAllowed {
appendIllegalAccess()
} else if isRelativeImport &&
!isAllowed(newElement(fun.name), filepath.Clean(path+"/"+pathToFunction()), load, walked, info) {
appendIllegalAccess()
}
}
}
if !allowed {
info.illegals = append(info.illegals, &illegal{
T: "illegal-definition",
Name: functionObj.Name,
Pos: load[path].fset.Position(functionDefinition.Pos()).String(),
})
}
return allowed
}
func removeRepetitions(slc []*illegal) (result []*illegal) {
in := make(map[string]bool)
for _, v := range slc {
if in[v.Pos] {
continue
}
result = append(result, v)
in[v.Pos] = true
}
return result
}
type occurrence struct {
name string
pos string
}
type info struct {
arrays []*occurrence
lits []*occurrence
fors []*occurrence
callRepetition map[string]int
illegals []*illegal // functions, selections that are not allowed
}
func newElement(name string) *element {
return &element{
name: name,
pos: token.Pos(0),
}
}
func analyzeProgram(filename, path string, load loadedSource) *info {
fset := load[path].fset
file := load[path].files[filename]
functions := functionsInfo(file)
info := &info{
callRepetition: make(map[string]int),
}
info.illegals = append(info.illegals, analyzeImports(file, fset, noRelativeImports)...)
walked := make(map[ast.Node]bool)
for _, fun := range functions {
function := newElement(fun.obj.Name)
isAllowed(function, path, load, walked, info)
}
info.illegals = append(info.illegals, analyzeLoops(info.fors, noFor)...)
info.illegals = append(info.illegals, analyzeArrayTypes(info.arrays, noArrays || noSlices, noTheseSlices)...)
info.illegals = append(info.illegals, analyzeLits(info.lits, noLit)...)
info.illegals = append(info.illegals, analyzeRepetition(info.callRepetition, allowedRep)...)
info.illegals = removeRepetitions(info.illegals)
return info
}
func parseArgs(toAllow []string, builtins bool, casting bool) error {
allowedFun["builtin"] = make(map[string]bool)
predeclaredTypes := []string{"bool", "byte", "complex64", "complex128",
"error", "float32", "float64", "int", "int8",
"int16", "int32", "int64", "rune", "string",
"uint", "uint8", "uint16", "uint32", "uint64",
"uintptr",
}
if builtins {
allowedFun["builtin"]["*"] = true
}
if casting {
for _, v := range predeclaredTypes {
allowedFun["builtin"][v] = true
}
}
for _, v := range toAllow {
var path, funcName string
if strings.ContainsRune(v, '/') {
path = filepath.Dir(v)
funcName = filepath.Base(v)
spl := strings.Split(funcName, ".")
path = path + "/" + spl[0]
if len(spl) > 1 {
funcName = spl[1]
}
} else if strings.ContainsRune(v, '.') {
spl := strings.Split(v, ".")
path = spl[0]
if len(spl) > 1 {
funcName = spl[1]
}
} else {
path = "builtin"
funcName = v
}
if strings.ContainsRune(funcName, '#') {
spl := strings.Split(funcName, "#")
funcName = spl[0]
n, err := strconv.Atoi(spl[1])
if err != nil {
return fmt.Errorf("After the '#' there should be an integer" +
" representing the maximum number of allowed occurrences")
}
var prefix string
if path != "" {
prefix = filepath.Base(path)
}
allowedRep[prefix+"."+funcName] = n
}
if allowedFun[path] == nil {
allowedFun[path] = make(map[string]bool)
}
allowedFun[path][funcName] = true
}
return nil
}
func printIllegals(illegals []*illegal) {
tbl, err := table.NewTable([]table.Column{
{Header: "\tTYPE:"},
{Header: "NAME:", MinWidth: 7},
{Header: "LOCATION:"},
}...)
if err != nil {
panic(err)
}
tbl.Separator = "\t"
for _, v := range illegals {
tbl.AddRow("\t"+v.T, v.Name, v.Pos)
}
tbl.Print()
}
func analyzeRepetition(callRepetition map[string]int, allowRep map[string]int) (illegals []*illegal) {
for name, rep := range allowedRep {
if callRepetition[name] > rep {
diff := callRepetition[name] - rep
illegals = append(illegals, &illegal{
T: "illegal-amount",
Name: name + " exeding max repetitions by " + strconv.Itoa(diff),
Pos: "all the project",
})
}
}
return illegals
}
func analyzeLits(litOccu []*occurrence, noLit regexpFlag) (illegals []*illegal) {
if noLit.active {
for _, v := range litOccu {
if noLit.reg.Match([]byte(v.name)) {
illegals = append(illegals, &illegal{
T: "illegal-lit",
Name: v.name,
Pos: v.pos,
})
}
}
}
return illegals
}
func analyzeArrayTypes(arrays []*occurrence, noArrays bool, noTheseSlices map[string]bool) (illegals []*illegal) {
for _, v := range arrays {
if noArrays || noTheseSlices[v.name] {
illegals = append(illegals, &illegal{
T: "illegal-slice",
Name: v.name,
Pos: v.pos,
})
}
}
return illegals
}
func analyzeLoops(fors []*occurrence, noFor bool) (illegals []*illegal) {
if noFor {
for _, v := range fors {
illegals = append(illegals, &illegal{
T: "illegal-loop",
Name: v.name,
Pos: v.pos,
})
}
}
return illegals
}
type importVisitor struct {
imports map[string]*element
}
func (i *importVisitor) Visit(n ast.Node) ast.Visitor {
if imp, ok := n.(*ast.ImportSpec); ok {
path, _ := strconv.Unquote(imp.Path.Value)
var name string
if imp.Name != nil {
name = imp.Name.Name
} else {
name = filepath.Base(path)
}
el := &element{
name: path,
pos: n.Pos(),
}
i.imports[name] = el
}
return i
}
func analyzeImports(file ast.Node, fset *token.FileSet, noRelImp bool) (illegals []*illegal) {
i := &importVisitor{
imports: make(map[string]*element),
}
ast.Walk(i, file)
for _, path := range i.imports {
isRelativeImport := isRelativeImport(path.name)
if (noRelativeImports && isRelativeImport) || (allowedFun[path.name] == nil && !isRelativeImport) {
illegals = append(illegals, &illegal{
T: "illegal-import",
Name: path.name,
Pos: fset.Position(path.pos).String(),
})
}
}
return illegals
}
type element struct {
name string
pos token.Pos
}
type loadVisitor struct {
relImports map[string]*element
absImports map[string]*element
functions map[string]ast.Node
fset *token.FileSet
objFunc map[*ast.Object]ast.Node
blocks []*ast.BlockStmt
scopes map[*ast.BlockStmt]*ast.Scope
// nil after the visit
// used to keep the result of the createScope function
pkgScope *ast.Scope
files map[string]*ast.File
}
// Returns all the parameter of a function that identify a function
func functionsInTheParameters(params *ast.FieldList) []string {
var funcs []string
for _, param := range params.List {
if _, ok := param.Type.(*ast.FuncType); ok {
for _, name := range param.Names {
funcs = append(funcs, name.Name)
}
}
}
return funcs
}
type function struct {
obj *ast.Object // the ast.Object that represents a function
params []string
// the name of the parameter that represent
// functions
body *ast.BlockStmt
}
// Returns information about a node representing a function declaration
func extractFunction(n ast.Node) *function {
function := &function{}
switch t := n.(type) {
case *ast.FuncDecl:
function.obj = t.Name.Obj
function.params = functionsInTheParameters(t.Type.Params)
function.body = t.Body
return function
case *ast.GenDecl:
for _, v := range t.Specs {
if val, ok := v.(*ast.ValueSpec); ok {
for i, value := range val.Values {
if funcLit, ok := value.(*ast.FuncLit); ok {
function.obj = val.Names[i].Obj
function.params = functionsInTheParameters(funcLit.Type.Params)
function.body = funcLit.Body
}
}
}
}
return function
case *ast.AssignStmt:
for i, right := range t.Rhs {
if funcLit, ok := right.(*ast.FuncLit); ok {
if ident, ok := t.Lhs[i].(*ast.Ident); ok {
function.obj = ident.Obj
function.params = functionsInTheParameters(funcLit.Type.Params)
}
}
return function
}
default:
return function
}
return function
}
func (l *loadVisitor) Visit(n ast.Node) ast.Visitor {
switch t := n.(type) {
case *ast.ImportSpec:
path, _ := strconv.Unquote(t.Path.Value)
var name string
if t.Name != nil {
name = t.Name.Name
} else {
name = filepath.Base(path)
}
el := &element{
name: path,
pos: n.Pos(),
}
if isRelativeImport(path) {
l.relImports[name] = el
} else {
l.absImports[name] = el
}
case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt:
fdef := extractFunction(t)
if fdef == nil || fdef.obj == nil {
return l
}
l.objFunc[fdef.obj] = n
case *ast.BlockStmt:
l.blocks = append(l.blocks, t)
}
return l
}
// Returns true if the string matches the format of a relative import
func isRelativeImport(s string) bool {
return strings.HasPrefix(s, ".")
}

153
rc/rc_test.go

@ -1,153 +0,0 @@
package main
import (
"sort"
"strings"
"testing"
"github.com/01-edu/z01"
)
func TestFlags(t *testing.T) {
argsAndSolution := map[string]string{
"tests/testingSimpleFunc.go": `Cheating:
TYPE: NAME: LOCATION:
illegal-import regexp tests/testingSimpleFunc.go:4:2
illegal-call len tests/testingSimpleFunc.go:10:9
illegal-access regexp.MustCompile tests/testingSimpleFunc.go:8:8
illegal-definition SimpleFunc tests/testingSimpleFunc.go:7:1
`,
"-no-for -no-lit=[a-z] tests/printalphabet/printalphabet.go": `Cheating:
TYPE: NAME: LOCATION:
illegal-import fmt tests/printalphabet/printalphabet.go:4:2
illegal-import github.com/01-edu/z01 tests/printalphabet/printalphabet.go:6:2
illegal-call append tests/printalphabet/printalphabet.go:11:7
illegal-definition fillArray tests/printalphabet/printalphabet.go:9:1
illegal-call int tests/printalphabet/printalphabet.go:17:7
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:19:3
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:21:2
illegal-definition main tests/printalphabet/printalphabet.go:15:1
illegal-access fmt.Println tests/printalphabet/printalphabet.go:26:3
illegal-definition defFun tests/printalphabet/printalphabet.go:25:2
illegal-call defFun tests/printalphabet/printalphabet.go:28:2
illegal-definition testingScope tests/printalphabet/printalphabet.go:24:1
illegal-loop for tests/printalphabet/printalphabet.go:10:2
illegal-lit 'a' tests/printalphabet/printalphabet.go:10:11
illegal-lit 'z' tests/printalphabet/printalphabet.go:10:21
illegal-lit 'a' tests/printalphabet/printalphabet.go:16:14
illegal-lit 'b' tests/printalphabet/printalphabet.go:16:19
illegal-lit 'c' tests/printalphabet/printalphabet.go:16:24
illegal-lit 'd' tests/printalphabet/printalphabet.go:16:29
illegal-lit 'e' tests/printalphabet/printalphabet.go:16:34
illegal-lit 'f' tests/printalphabet/printalphabet.go:16:39
illegal-lit 'a' tests/printalphabet/printalphabet.go:17:11
illegal-lit '\n' tests/printalphabet/printalphabet.go:21:16
illegal-lit "Hello" tests/printalphabet/printalphabet.go:28:9
`,
"-cast tests/eightqueens.go": `Cheating:
TYPE: NAME: LOCATION:
illegal-import github.com/01-edu/z01 tests/eightqueens.go:4:2
illegal-access z01.PrintRune tests/eightqueens.go:49:5
illegal-access z01.PrintRune tests/eightqueens.go:55:2
illegal-definition printQueens tests/eightqueens.go:42:1
`,
"-no-arrays tests/printalphabet/printalphabet.go": `Cheating:
TYPE: NAME: LOCATION:
illegal-import fmt tests/printalphabet/printalphabet.go:4:2
illegal-import github.com/01-edu/z01 tests/printalphabet/printalphabet.go:6:2
illegal-call append tests/printalphabet/printalphabet.go:11:7
illegal-definition fillArray tests/printalphabet/printalphabet.go:9:1
illegal-call int tests/printalphabet/printalphabet.go:17:7
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:19:3
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:21:2
illegal-definition main tests/printalphabet/printalphabet.go:15:1
illegal-access fmt.Println tests/printalphabet/printalphabet.go:26:3
illegal-definition defFun tests/printalphabet/printalphabet.go:25:2
illegal-call defFun tests/printalphabet/printalphabet.go:28:2
illegal-definition testingScope tests/printalphabet/printalphabet.go:24:1
illegal-slice rune tests/printalphabet/printalphabet.go:9:18
illegal-slice rune tests/printalphabet/printalphabet.go:16:7
`,
"-no-slices tests/printalphabet/printalphabet.go": `Cheating:
TYPE: NAME: LOCATION:
illegal-import fmt tests/printalphabet/printalphabet.go:4:2
illegal-import github.com/01-edu/z01 tests/printalphabet/printalphabet.go:6:2
illegal-call append tests/printalphabet/printalphabet.go:11:7
illegal-definition fillArray tests/printalphabet/printalphabet.go:9:1
illegal-call int tests/printalphabet/printalphabet.go:17:7
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:19:3
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:21:2
illegal-definition main tests/printalphabet/printalphabet.go:15:1
illegal-access fmt.Println tests/printalphabet/printalphabet.go:26:3
illegal-definition defFun tests/printalphabet/printalphabet.go:25:2
illegal-call defFun tests/printalphabet/printalphabet.go:28:2
illegal-definition testingScope tests/printalphabet/printalphabet.go:24:1
illegal-slice rune tests/printalphabet/printalphabet.go:9:18
illegal-slice rune tests/printalphabet/printalphabet.go:16:7
`,
"-no-these-slices=int,rune tests/printalphabet/printalphabet.go": `Cheating:
TYPE: NAME: LOCATION:
illegal-import fmt tests/printalphabet/printalphabet.go:4:2
illegal-import github.com/01-edu/z01 tests/printalphabet/printalphabet.go:6:2
illegal-call append tests/printalphabet/printalphabet.go:11:7
illegal-definition fillArray tests/printalphabet/printalphabet.go:9:1
illegal-call int tests/printalphabet/printalphabet.go:17:7
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:19:3
illegal-access z01.PrintRune tests/printalphabet/printalphabet.go:21:2
illegal-definition main tests/printalphabet/printalphabet.go:15:1
illegal-access fmt.Println tests/printalphabet/printalphabet.go:26:3
illegal-definition defFun tests/printalphabet/printalphabet.go:25:2
illegal-call defFun tests/printalphabet/printalphabet.go:28:2
illegal-definition testingScope tests/printalphabet/printalphabet.go:24:1
illegal-slice rune tests/printalphabet/printalphabet.go:9:18
illegal-slice rune tests/printalphabet/printalphabet.go:16:7
`,
`-allow-builtin tests/doopprog/main.go fmt.Println strconv.Atoi os.Args`: ``,
`-cast tests/doopprog/main.go fmt.Println strconv.Atoi os.Args len`: ``,
`tests/testingWrapping.go`: `Cheating:
TYPE: NAME: LOCATION:
illegal-call len tests/utilDepth2/wrapper.go:4:9
illegal-definition LenWrapper tests/utilDepth2/wrapper.go:3:1
illegal-access util2.LenWrapper tests/util/util.go:10:9
illegal-definition LenWrapperU tests/util/util.go:9:1
illegal-access util.LenWrapperU tests/testingWrapping.go:8:9
illegal-definition Length tests/testingWrapping.go:7:1
`,
`tests/testingWrapping.go len`: ``,
`tests/empty/empty len`: ` No file to analyze
`,
`tests/empty/empty.go tests/empty/empty`: ` tests/empty/empty.go:1:1: expected ';', found 'EOF' (and 2 more errors)
`,
}
Compare(t, argsAndSolution)
}
func Compare(t *testing.T, argsAndSol map[string]string) {
for args, sol := range argsAndSol {
a := strings.Split(args, " ")
out, err := z01.MainOut("../rc", a...)
if !EqualResult(sol, out) {
if err == nil {
t.Errorf("./rc %s prints %q\n instead of %q\n", args, out, sol)
}
if err != nil && !EqualResult(sol, err.Error()) {
t.Errorf("./rc %s prints %q\n instead of %q\n", args, err.Error(), sol)
}
}
}
}
func EqualResult(sol, out string) bool {
// split
solSli := strings.Split(sol, "\n")
outSli := strings.Split(out, "\n")
// sort
sort.Sort(sort.StringSlice(solSli))
sort.Sort(sort.StringSlice(outSli))
// join
sol = strings.Join(solSli, "\n")
out = strings.Join(outSli, "\n")
// compare
return sol == out
}

11
rc/tests/alphabet/main.go

@ -1,11 +0,0 @@
package main
import "fmt"
func main() {
for _, v := range "abcdefghijklmnopqrstuvwxyz" {
fmt.Println(v)
}
fmt.Println()
}

214
rc/tests/doopprog/main.go

@ -1,214 +0,0 @@
// package main
// import (
// "fmt"
// "os"
// "strconv"
// )
// level 3: doopprog
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
args := os.Args
if len(args) != 4 {
return
}
if args[2] != "+" && args[2] != "-" && args[2] != "/" && args[2] != "*" && args[2] != "%" {
fmt.Println(0)
return
}
// arg1 := getArg(args[1])
arg1, err := strconv.Atoi(args[1])
if err != nil {
fmt.Println(0)
return
}
//fmt.Println("Arg1: ", arg1)
arg2, err := strconv.Atoi(args[3])
if err != nil {
fmt.Println(0)
return
}
//fmt.Println("Arg2: ", arg2)
if args[2] == "+" {
Add(int64(arg1), int64(arg2))
} else if args[2] == "-" {
Subs(int64(arg1), int64(arg2))
} else if args[2] == "/" {
Div(int64(arg1), int64(arg2))
} else if args[2] == "*" {
Multip(int64(arg1), int64(arg2))
} else if args[2] == "%" {
Modulo(int64(arg1), int64(arg2))
}
}
var max int64 = 9223372036854775807
var min int64 = -9223372036854775808
func Add(x, y int64) {
if x >= 0 && y < 0 {
Subs(x, -y)
return
} else if x < 0 && y >= 0 {
Subs(y, -x)
return
} else if x < 0 && y < 0 {
if x >= min-y {
fmt.Println(x + y)
return
} else {
fmt.Println(0)
return
}
}
if x <= max-y {
fmt.Println(x + y)
} else {
fmt.Println(0)
return
}
}
func Subs(x, y int64) {
if x >= 0 && y < 0 {
Add(x, -y)
return
} else if x < 0 && y >= 0 {
Add(-y, x)
return
} else if x < 0 && y < 0 {
Subs(-y, -x)
return
}
if x >= min+y {
fmt.Println(x - y)
return
} else {
fmt.Println(0)
return
}
}
func Multip(x, y int64) {
if x >= 0 && y >= 0 {
if x <= max/y {
fmt.Println(x * y)
return
} else {
fmt.Println(0)
return
}
} else if x < 0 && y < 0 {
if x > max/y {
fmt.Println(x * y)
return
} else {
fmt.Println(0)
return
}
} else {
if x >= min/y {
fmt.Println(x * y)
return
} else {
fmt.Println(0)
return
}
}
}
func Div(x, y int64) {
if y == 0 {
fmt.Println("No division by 0")
return
}
if x == min && y == -1 {
fmt.Println(0)
return
}
fmt.Println(x / y)
return
}
func Modulo(x, y int64) {
if y == 0 {
fmt.Println("No modulo by 0")
return
}
if y == min && x == -1 {
fmt.Println(0)
return
}
fmt.Println(x % y)
return
}
//9223372036854775807
// func main() {
// if len(os.Args) == 4 {
// var result int
// firstArg, err := strconv.Atoi(os.Args[1])
// if err != nil {
// fmt.Println(0)
// return
// }
// operator := os.Args[2]
// secondArg, err1 := strconv.Atoi(os.Args[3])
// if err1 != nil {
// fmt.Println(0)
// return
// }
// if secondArg == 0 && operator == "/" {
// fmt.Println("No division by 0")
// return
// } else if secondArg == 0 && operator == "%" {
// fmt.Println("No modulo by 0")
// return
// } else if operator == "+" {
// result = firstArg + secondArg
// if !((result > firstArg) == (secondArg > 0)) {
// fmt.Println(0)
// return
// }
// } else if operator == "-" {
// result = firstArg - secondArg
// if !((result < firstArg) == (secondArg > 0)) {
// fmt.Println(0)
// return
// }
// } else if operator == "/" {
// result = firstArg / secondArg
// } else if operator == "*" {
// result = firstArg * secondArg
// if firstArg != 0 && (result/firstArg != secondArg) {
// fmt.Println(0)
// return
// }
// } else if operator == "%" {
// result = firstArg % secondArg
// }
// fmt.Println(result)
// }
// }

86
rc/tests/eightqueens.go

@ -1,86 +0,0 @@
package solutions
import (
"github.com/01-edu/z01"
)
const size = 8
// board is a chessboard composed of boolean squares, a true square means a queen is on it
// a false square means it is a free square
var board [size][size]bool
// goodDirection check that there is no queen on the segment that starts at (x, y)
// coordinates, points into the direction vector (vx, vy) and ends at the edge of the board
func goodDirection(x, y, vx, vy int) bool {
// x and y are still on board
for 0 <= x && x < size &&
0 <= y && y < size {
if board[x][y] {
// Not a good line : the square is already occupied
return false
}
x = x + vx // Move x in the right direction
y = y + vy // Move y in the right direction
}
// All clear
return true
}
// goodSquare makes all the necessary line checks for the queens movements
func goodSquare(x, y int) bool {
return goodDirection(x, y, +0, -1) &&
goodDirection(x, y, +1, -1) &&
goodDirection(x, y, +1, +0) &&
goodDirection(x, y, +1, +1) &&
goodDirection(x, y, +0, +1) &&
goodDirection(x, y, -1, +1) &&
goodDirection(x, y, -1, +0) &&
goodDirection(x, y, -1, -1)
}
func printQueens() {
x := 0
for x < size {
y := 0
for y < size {
if board[x][y] {
// We have found a queen, let's print her y
z01.PrintRune(rune(y) + '1')
}
y++
}
x++
}
z01.PrintRune('\n')
}
// tryX tries, for a given x (column) to find a y (row) so that the queen on (x, y) is a part
// of the solution to the problem
func tryX(x int) {
y := 0
for y < size {
if goodSquare(x, y) {
// Since the square is good for the queen, let's put one on it:
board[x][y] = true
if x == size-1 {
// x is the biggest possible x, it means that we just placed the last
// queen on the board, so the solution is complete and we can print it
printQueens()
} else {
// let's try to put another queen on the next empty x (column)
tryX(x + 1)
}
// remove the queen of the board, to try other y values
board[x][y] = false
}
y++
}
}
func EightQueens() {
// try the first column
tryX(0)
}

25
rc/tests/example.go

@ -1,25 +0,0 @@
package solutions
import (
"fmt"
)
func thisIsAFunc() {
fmt.Println("This is a function")
}
var ThisToo = func(s string) {
fmt.Printf("ThisToo prints %s\n", s)
}
func aux(s string) int {
return 1
}
func youCanAlso(f func(string), s string) {
aux := func(s string) int {
return len(s)
}
aux(s)
f(s)
}

16
rc/tests/nesting/main.go

@ -1,16 +0,0 @@
package main
import "fmt"
func myFunction(s string) bool {
return s > "m"
}
func main() {
// myFunction := func(s string) bool {
// return s < "m"
// }
fmt.Printf("Does %s comes before \"m\" %v\n", "name", myFunction("name"))
fmt.Printf("Does %s comes before \"m\" %v\n", "change", myFunction("change"))
}

29
rc/tests/printalphabet/printalphabet.go

@ -1,29 +0,0 @@
package main
import (
"fmt"
"github.com/01-edu/z01"
)
func fillArray(a []rune) {
for i := 'a'; i <= 'z'; i++ {
a = append(a, i)
}
}
func main() {
a := []rune{'a', 'b', 'c', 'd', 'e', 'f'}
b := int('a')
for _, v := range a {
z01.PrintRune(v)
}
z01.PrintRune('\n')
}
func testingScope() {
defFun := func(s string) {
fmt.Println(s)
}
defFun("Hello")
}

19
rc/tests/testingRepetitions/main.go

@ -1,19 +0,0 @@
package main
import "github.com/01-edu/z01"
func main() {
z01.PrintRune('a')
z01.PrintRune('b')
z01.PrintRune('c')
z01.PrintRune('d')
z01.PrintRune('e')
z01.PrintRune('f')
z01.PrintRune('g')
z01.PrintRune('h')
z01.PrintRune('i')
z01.PrintRune('j')
z01.PrintRune('k')
z01.PrintRune('l')
z01.PrintRune('m')
}

11
rc/tests/testingSimpleFunc.go

@ -1,11 +0,0 @@
package solutions
import (
"regexp"
)
func SimpleFunc(str string) int {
re := regexp.MustCompile(`[a-zA-Z]`)
found := re.FindAll([]byte(str), -1)
return len(found)
}

12
rc/tests/testingWrapping.go

@ -1,12 +0,0 @@
package solutions
import (
util "./util"
)
func Length(ss []string) int {
return util.LenWrapperU(ss)
}
// func NotUsed() {
// }

23
rc/tests/util/util.go

@ -1,23 +0,0 @@
package util
import (
"fmt"
util2 "../utilDepth2"
)
func LenWrapperU(ss []string) int {
return util2.LenWrapper(ss)
}
func NotUsed() {
b := []string{"just", "something"}
a := len(b)
for i, v := range b {
if i == a-1 {
fmt.Println("Last element", v)
continue
}
fmt.Println("Element", v)
}
}

5
rc/tests/utilDepth2/wrapper.go

@ -1,5 +0,0 @@
package util
func LenWrapper(ss []string) int {
return len(ss)
}
Loading…
Cancel
Save