From 4fba39731efc3f61a6c24d5584f5f41334fa4418 Mon Sep 17 00:00:00 2001 From: Augusto Date: Tue, 16 Jun 2020 11:05:11 +0100 Subject: [PATCH] Delete duplicated `rc` --- rc/README.md | 143 ---- rc/rc.go | 849 ------------------------ rc/rc_test.go | 153 ----- rc/tests/alphabet/main.go | 11 - rc/tests/doopprog/main.go | 214 ------ rc/tests/eightqueens.go | 86 --- rc/tests/empty/empty | 0 rc/tests/empty/empty.go | 0 rc/tests/example.go | 25 - rc/tests/nesting/main.go | 16 - rc/tests/printalphabet/printalphabet.go | 29 - rc/tests/testingRepetitions/main.go | 19 - rc/tests/testingSimpleFunc.go | 11 - rc/tests/testingWrapping.go | 12 - rc/tests/util/util.go | 23 - rc/tests/utilDepth2/wrapper.go | 5 - 16 files changed, 1596 deletions(-) delete mode 100644 rc/README.md delete mode 100644 rc/rc.go delete mode 100644 rc/rc_test.go delete mode 100644 rc/tests/alphabet/main.go delete mode 100644 rc/tests/doopprog/main.go delete mode 100644 rc/tests/eightqueens.go delete mode 100644 rc/tests/empty/empty delete mode 100644 rc/tests/empty/empty.go delete mode 100644 rc/tests/example.go delete mode 100644 rc/tests/nesting/main.go delete mode 100644 rc/tests/printalphabet/printalphabet.go delete mode 100644 rc/tests/testingRepetitions/main.go delete mode 100644 rc/tests/testingSimpleFunc.go delete mode 100644 rc/tests/testingWrapping.go delete mode 100644 rc/tests/util/util.go delete mode 100644 rc/tests/utilDepth2/wrapper.go diff --git a/rc/README.md b/rc/README.md deleted file mode 100644 index 3a660bbdb..000000000 --- a/rc/README.md +++ /dev/null @@ -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 - - `.*` for full imports (all functions from that package are allowed) - - ``.`` for partial imports (only the function is allowed) - - ``.`#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 diff --git a/rc/rc.go b/rc/rc.go deleted file mode 100644 index 5aa4ccf3f..000000000 --- a/rc/rc.go +++ /dev/null @@ -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, ".") -} diff --git a/rc/rc_test.go b/rc/rc_test.go deleted file mode 100644 index ff31d387c..000000000 --- a/rc/rc_test.go +++ /dev/null @@ -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 -} diff --git a/rc/tests/alphabet/main.go b/rc/tests/alphabet/main.go deleted file mode 100644 index f75b1327a..000000000 --- a/rc/tests/alphabet/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import "fmt" - -func main() { - for _, v := range "abcdefghijklmnopqrstuvwxyz" { - fmt.Println(v) - } - - fmt.Println() -} diff --git a/rc/tests/doopprog/main.go b/rc/tests/doopprog/main.go deleted file mode 100644 index 011902685..000000000 --- a/rc/tests/doopprog/main.go +++ /dev/null @@ -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) -// } -// } diff --git a/rc/tests/eightqueens.go b/rc/tests/eightqueens.go deleted file mode 100644 index 833faab79..000000000 --- a/rc/tests/eightqueens.go +++ /dev/null @@ -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) -} diff --git a/rc/tests/empty/empty b/rc/tests/empty/empty deleted file mode 100644 index e69de29bb..000000000 diff --git a/rc/tests/empty/empty.go b/rc/tests/empty/empty.go deleted file mode 100644 index e69de29bb..000000000 diff --git a/rc/tests/example.go b/rc/tests/example.go deleted file mode 100644 index fa7e4f35c..000000000 --- a/rc/tests/example.go +++ /dev/null @@ -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) -} diff --git a/rc/tests/nesting/main.go b/rc/tests/nesting/main.go deleted file mode 100644 index b84133ec8..000000000 --- a/rc/tests/nesting/main.go +++ /dev/null @@ -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")) -} diff --git a/rc/tests/printalphabet/printalphabet.go b/rc/tests/printalphabet/printalphabet.go deleted file mode 100644 index 10a5cd828..000000000 --- a/rc/tests/printalphabet/printalphabet.go +++ /dev/null @@ -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") -} diff --git a/rc/tests/testingRepetitions/main.go b/rc/tests/testingRepetitions/main.go deleted file mode 100644 index ebed01412..000000000 --- a/rc/tests/testingRepetitions/main.go +++ /dev/null @@ -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') -} diff --git a/rc/tests/testingSimpleFunc.go b/rc/tests/testingSimpleFunc.go deleted file mode 100644 index fadd048e9..000000000 --- a/rc/tests/testingSimpleFunc.go +++ /dev/null @@ -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) -} diff --git a/rc/tests/testingWrapping.go b/rc/tests/testingWrapping.go deleted file mode 100644 index 23228adb3..000000000 --- a/rc/tests/testingWrapping.go +++ /dev/null @@ -1,12 +0,0 @@ -package solutions - -import ( - util "./util" -) - -func Length(ss []string) int { - return util.LenWrapperU(ss) -} - -// func NotUsed() { -// } diff --git a/rc/tests/util/util.go b/rc/tests/util/util.go deleted file mode 100644 index e9094c252..000000000 --- a/rc/tests/util/util.go +++ /dev/null @@ -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) - } -} diff --git a/rc/tests/utilDepth2/wrapper.go b/rc/tests/utilDepth2/wrapper.go deleted file mode 100644 index 7ab08d523..000000000 --- a/rc/tests/utilDepth2/wrapper.go +++ /dev/null @@ -1,5 +0,0 @@ -package util - -func LenWrapper(ss []string) int { - return len(ss) -}