mirror of https://github.com/01-edu/public.git
xpetit
4 years ago
23 changed files with 3 additions and 1679 deletions
@ -1,139 +0,0 @@ |
|||||||
### RC (Restrictions Checker) |
|
||||||
|
|
||||||
This program analyzes a go source file and displays in standard output the imports, functions, slice 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 absolute imports are allowed |
|
||||||
- NO built-in functions are allowed. |
|
||||||
- NO casting is allowed |
|
||||||
|
|
||||||
### 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 |
|
||||||
- `--no-lit="{PATTERN}"`: disallows character and string literals that match the pattern `PATTERN` which represent a Regular Expression |
|
||||||
|
|
||||||
### Arguments: |
|
||||||
|
|
||||||
- Flags can be passed at any point (in the beginning, middle or end) of the argument list |
|
||||||
|
|
||||||
- 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 possible to limit the number of calls of a functions like with the imports using the '#' character |
|
||||||
- Ex: `make`, `append`, `len`, `print#2`. |
|
||||||
|
|
||||||
### 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 written exactly the way they are written 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.* |
|
||||||
``` |
|
||||||
|
|
||||||
- Disallow literals |
|
||||||
|
|
||||||
- Use the flag `--no-lit="{PATTERN}"` |
|
||||||
```console |
|
||||||
_$ rc -no-slices --no-lit=[b-yB-Y] main.go fmt.* github.com/01-edu/z01.PrintRune len |
|
||||||
``` |
|
||||||
|
|
||||||
- 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 disallowed 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 |
|
@ -1,892 +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-array", 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 _, err := os.Stat(filename); err != nil { |
|
||||||
fmt.Printf("\t%s\n", err) |
|
||||||
os.Exit(1) |
|
||||||
} |
|
||||||
|
|
||||||
err := parseArgs(flag.Args()) |
|
||||||
|
|
||||||
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) set() { |
|
||||||
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.set() |
|
||||||
|
|
||||||
pkgs, err := parser.ParseDir(l.fset, path, nil, parser.AllErrors) |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
if len(pkgs) > 1 { |
|
||||||
packages := []string{} |
|
||||||
for pkgName := range pkgs { |
|
||||||
packages = append(packages, pkgName) |
|
||||||
} |
|
||||||
return fmt.Errorf("There should be only one package in this directory: Found packages: %v", packages) |
|
||||||
} |
|
||||||
|
|
||||||
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 nested declarations
|
|
||||||
//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) set(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
|
|
||||||
// TODO: Refactor so this function has only one responsibility
|
|
||||||
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.set(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) error { |
|
||||||
allowBuiltins() |
|
||||||
allowCasting() |
|
||||||
for _, v := range toAllow { |
|
||||||
err := allowFunction(v) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func allowFunction(functionPath string) error { |
|
||||||
functionName := functionName(functionPath) |
|
||||||
packageName := packageName(functionPath) |
|
||||||
// for github.com/01-edu/z01 shortName = z01
|
|
||||||
packageShortName := filepath.Base(packageName) |
|
||||||
restrictsRepetitions := strings.ContainsRune(functionPath, '#') |
|
||||||
if restrictsRepetitions { |
|
||||||
allowedReps, err := repetitionsAllowed(functionPath) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
allowedRep[packageShortName+"."+functionName] = allowedReps |
|
||||||
} |
|
||||||
if allowedFun[packageName] == nil { |
|
||||||
allowedFun[packageName] = make(map[string]bool) |
|
||||||
} |
|
||||||
allowedFun[packageName][functionName] = true |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func functionName(functionPath string) string { |
|
||||||
segmentedPath := strings.Split(functionPath, ".") |
|
||||||
return strings.Split(segmentedPath[len(segmentedPath)-1], "#")[0] |
|
||||||
} |
|
||||||
|
|
||||||
func packageName(functionPath string) string { |
|
||||||
segmentedPath := strings.Split(functionPath, ".") |
|
||||||
hasNoPackage := len(segmentedPath) < 2 |
|
||||||
if hasNoPackage { |
|
||||||
return "builtin" |
|
||||||
} |
|
||||||
return strings.Join(segmentedPath[:len(segmentedPath)-1], ".") |
|
||||||
} |
|
||||||
|
|
||||||
// Assumes that `functionPath` contains `#`
|
|
||||||
func repetitionsAllowed(functionPath string) (int, error) { |
|
||||||
segmentedPath := strings.Split(functionPath, "#") |
|
||||||
repetitions := segmentedPath[len(segmentedPath)-1] |
|
||||||
allowedReps, err := strconv.Atoi(repetitions) |
|
||||||
if err != nil { |
|
||||||
return allowedReps, fmt.Errorf("After the '#' there should be an integer" + |
|
||||||
" representing the maximum number of allowed occurrences") |
|
||||||
} |
|
||||||
return allowedReps, nil |
|
||||||
} |
|
||||||
|
|
||||||
func allowBuiltins() { |
|
||||||
if allowedFun["builtin"] == nil { |
|
||||||
allowedFun["builtin"] = make(map[string]bool) |
|
||||||
} |
|
||||||
if allowBuiltin { |
|
||||||
allowedFun["builtin"]["*"] = true |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func allowCasting() { |
|
||||||
if allowedFun["builtin"] == nil { |
|
||||||
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 casting { |
|
||||||
for _, v := range predeclaredTypes { |
|
||||||
allowedFun["builtin"][v] = true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
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 |
|
||||||
} |
|
||||||
|
|
||||||
func (l *loadVisitor) String() (res string) { |
|
||||||
res = "files" |
|
||||||
for f, _ := range l.files { |
|
||||||
res += f + "," |
|
||||||
} |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// 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, ".") |
|
||||||
} |
|
@ -1,151 +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-array 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`: ` stat : no such file or directory |
|
||||||
`, |
|
||||||
`tests/empty/empty.go tests/empty/empty`: ` tests/empty/empty.go:1:1: expected ';', found 'EOF' (and 2 more errors) |
|
||||||
`, |
|
||||||
`tests/abc/main.go --cast github.com/01-edu/z01.PrintRune#2 --no-lit=[b-mB-Y]`: ``, |
|
||||||
`itoa.go len --cast`: ` stat itoa.go: no such file or directory |
|
||||||
`, |
|
||||||
} |
|
||||||
Compare(t, argsAndSolution) |
|
||||||
} |
|
||||||
|
|
||||||
func Compare(t *testing.T, argsAndSol map[string]string) { |
|
||||||
for args, expected := range argsAndSol { |
|
||||||
a := strings.Split(args, " ") |
|
||||||
_, err := z01.MainOut("../rc", a...) |
|
||||||
if err != nil && !EqualResult(expected, err.Error()) { |
|
||||||
t.Errorf("./rc %s prints %q\n instead of %q\n", args, err.Error(), expected) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
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, " ") |
|
||||||
out = strings.Join(outSli, " ") |
|
||||||
// compare
|
|
||||||
return sol == out |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import "github.com/01-edu/z01" |
|
||||||
|
|
||||||
func main() { |
|
||||||
for r := 'a'; r <= 'z'; r++ { |
|
||||||
z01.PrintRune(r) |
|
||||||
} |
|
||||||
z01.PrintRune('\n') |
|
||||||
} |
|
@ -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)
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -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) |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
@ -1,11 +0,0 @@ |
|||||||
package piscine |
|
||||||
|
|
||||||
import "github.com/01-edu/z01" |
|
||||||
|
|
||||||
func IsNegative(nb int) { |
|
||||||
if nb < 0 { |
|
||||||
z01.PrintRune('T') |
|
||||||
} else { |
|
||||||
z01.PrintRune('F') |
|
||||||
} |
|
||||||
} |
|
@ -1,5 +0,0 @@ |
|||||||
package piscine |
|
||||||
|
|
||||||
func hello() { |
|
||||||
println("Nothing here") |
|
||||||
} |
|
@ -1,7 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import "fmt" |
|
||||||
|
|
||||||
func main() { |
|
||||||
fmt.Println("Hello world") |
|
||||||
} |
|
@ -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")) |
|
||||||
} |
|
@ -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") |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import "github.com/01-edu/z01" |
|
||||||
|
|
||||||
func main() { |
|
||||||
alphabet := "abcdefghijklmnopqrstuvwxyz\n" |
|
||||||
for _, letter := range alphabet { |
|
||||||
z01.PrintRune(letter) |
|
||||||
} |
|
||||||
} |
|
@ -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') |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
@ -1,12 +0,0 @@ |
|||||||
package solutions |
|
||||||
|
|
||||||
import ( |
|
||||||
util "./util" |
|
||||||
) |
|
||||||
|
|
||||||
func Length(ss []string) int { |
|
||||||
return util.LenWrapperU(ss) |
|
||||||
} |
|
||||||
|
|
||||||
// func NotUsed() {
|
|
||||||
// }
|
|
@ -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) |
|
||||||
} |
|
||||||
} |
|
@ -1,5 +0,0 @@ |
|||||||
package util |
|
||||||
|
|
||||||
func LenWrapper(ss []string) int { |
|
||||||
return len(ss) |
|
||||||
} |
|
Loading…
Reference in new issue