reporter.mx raw

   1  package main
   2  
   3  import (
   4  	"fmt"
   5  	"io"
   6  )
   7  
   8  func report(w io.Writer, path string, f *File) {
   9  	for _, d := range f.DeclList {
  10  		reportDecl(w, path, d)
  11  	}
  12  }
  13  
  14  func reportDecl(w io.Writer, path string, d Decl) {
  15  	switch d := d.(type) {
  16  	case *ImportDecl:
  17  		if d.Path == nil {
  18  			return
  19  		}
  20  		v := unquote(d.Path.Value)
  21  		switch v {
  22  		case "reflect":
  23  			flag(w, path, d.Pos(), "reflect", "reflect import - requires manual elimination")
  24  		case "sync":
  25  			flag(w, path, d.Pos(), "sync", "sync import - no shared memory in moxie")
  26  		case "context":
  27  			flag(w, path, d.Pos(), "context", "context import - restructure to channels")
  28  		case "unsafe":
  29  			flag(w, path, d.Pos(), "unsafe", "unsafe import - review for necessity")
  30  		}
  31  
  32  	case *VarDecl:
  33  		flag(w, path, d.Pos(), "pkg-var", "package-level var: " | d.NameList[0].Value)
  34  
  35  	case *FuncDecl:
  36  		if d.Body != nil {
  37  			reportBlock(w, path, d.Body)
  38  		}
  39  	}
  40  }
  41  
  42  func reportBlock(w io.Writer, path string, b *BlockStmt) {
  43  	for _, s := range b.List {
  44  		reportStmt(w, path, s)
  45  	}
  46  }
  47  
  48  func reportStmt(w io.Writer, path string, s Stmt) {
  49  	if s == nil {
  50  		return
  51  	}
  52  	switch s := s.(type) {
  53  	case *CallStmt:
  54  		if s.Tok == Go {
  55  			flag(w, path, s.Pos(), "goroutine", "go statement - restructure to spawn/channels")
  56  		}
  57  		reportExpr(w, path, s.Call)
  58  
  59  	case *ExprStmt:
  60  		reportExpr(w, path, s.X)
  61  
  62  	case *SendStmt:
  63  		reportExpr(w, path, s.Chan)
  64  		reportExpr(w, path, s.Value)
  65  
  66  	case *AssignStmt:
  67  		reportExpr(w, path, s.Lhs)
  68  		reportExpr(w, path, s.Rhs)
  69  
  70  	case *ReturnStmt:
  71  		reportExpr(w, path, s.Results)
  72  
  73  	case *IfStmt:
  74  		reportSimpleStmt(w, path, s.Init)
  75  		reportExpr(w, path, s.Cond)
  76  		reportBlock(w, path, s.Then)
  77  		if s.Else != nil {
  78  			reportStmt(w, path, s.Else)
  79  		}
  80  
  81  	case *ForStmt:
  82  		reportSimpleStmt(w, path, s.Init)
  83  		reportExpr(w, path, s.Cond)
  84  		reportSimpleStmt(w, path, s.Post)
  85  		reportBlock(w, path, s.Body)
  86  
  87  	case *SwitchStmt:
  88  		reportSimpleStmt(w, path, s.Init)
  89  		reportExpr(w, path, s.Tag)
  90  		for _, cc := range s.Body {
  91  			reportExpr(w, path, cc.Cases)
  92  			for _, st := range cc.Body {
  93  				reportStmt(w, path, st)
  94  			}
  95  		}
  96  
  97  	case *SelectStmt:
  98  		for _, cc := range s.Body {
  99  			reportSimpleStmt(w, path, cc.Comm)
 100  			for _, st := range cc.Body {
 101  				reportStmt(w, path, st)
 102  			}
 103  		}
 104  
 105  	case *BlockStmt:
 106  		reportBlock(w, path, s)
 107  
 108  	case *DeclStmt:
 109  		for _, d := range s.DeclList {
 110  			reportDecl(w, path, d)
 111  		}
 112  
 113  	case *LabeledStmt:
 114  		reportStmt(w, path, s.Stmt)
 115  
 116  	case *BranchStmt:
 117  		if s.Tok == Fallthrough {
 118  			flag(w, path, s.Pos(), "fallthrough", "fallthrough with preceding code - needs manual restructure")
 119  		}
 120  	}
 121  }
 122  
 123  func reportSimpleStmt(w io.Writer, path string, s SimpleStmt) {
 124  	if s == nil {
 125  		return
 126  	}
 127  	switch s := s.(type) {
 128  	case *ExprStmt:
 129  		reportExpr(w, path, s.X)
 130  	case *SendStmt:
 131  		reportExpr(w, path, s.Chan)
 132  		reportExpr(w, path, s.Value)
 133  	case *AssignStmt:
 134  		reportExpr(w, path, s.Lhs)
 135  		reportExpr(w, path, s.Rhs)
 136  	case *RangeClause:
 137  		reportExpr(w, path, s.Lhs)
 138  		reportExpr(w, path, s.X)
 139  	}
 140  }
 141  
 142  func reportExpr(w io.Writer, path string, e Expr) {
 143  	if e == nil {
 144  		return
 145  	}
 146  	switch e := e.(type) {
 147  	case *Name:
 148  		switch e.Value {
 149  		case "any":
 150  			flag(w, path, e.Pos(), "any", "any type - needs concrete type or interface")
 151  		case "uintptr":
 152  			flag(w, path, e.Pos(), "uintptr", "uintptr - removed in moxie")
 153  		case "complex64":
 154  			flag(w, path, e.Pos(), "complex", "complex64 - removed in moxie")
 155  		case "complex128":
 156  			flag(w, path, e.Pos(), "complex", "complex128 - removed in moxie")
 157  		}
 158  
 159  	case *InterfaceType:
 160  		if int32(len(e.MethodList)) == 0 {
 161  			flag(w, path, e.Pos(), "empty-iface", "interface{} - needs concrete type")
 162  		}
 163  
 164  	case *Operation:
 165  		if e.Op == Add && e.Y != nil && !isStringLit(e.X) && !isStringLit(e.Y) {
 166  			flag(w, path, e.Pos(), "maybe-string+", "+ operator on non-literal - check if string concat")
 167  		}
 168  		reportExpr(w, path, e.X)
 169  		reportExpr(w, path, e.Y)
 170  
 171  	case *CallExpr:
 172  		reportExpr(w, path, e.Fun)
 173  		for _, arg := range e.ArgList {
 174  			reportExpr(w, path, arg)
 175  		}
 176  
 177  	case *CompositeLit:
 178  		reportExpr(w, path, e.Type)
 179  		for _, elem := range e.ElemList {
 180  			reportExpr(w, path, elem)
 181  		}
 182  
 183  	case *KeyValueExpr:
 184  		reportExpr(w, path, e.Key)
 185  		reportExpr(w, path, e.Value)
 186  
 187  	case *FuncLit:
 188  		if e.Body != nil {
 189  			reportBlock(w, path, e.Body)
 190  		}
 191  
 192  	case *ParenExpr:
 193  		reportExpr(w, path, e.X)
 194  
 195  	case *SelectorExpr:
 196  		reportExpr(w, path, e.X)
 197  
 198  	case *IndexExpr:
 199  		reportExpr(w, path, e.X)
 200  		reportExpr(w, path, e.Index)
 201  
 202  	case *SliceExpr:
 203  		reportExpr(w, path, e.X)
 204  		for i := 0; i < 3; i++ {
 205  			reportExpr(w, path, e.Index[i])
 206  		}
 207  
 208  	case *AssertExpr:
 209  		reportExpr(w, path, e.X)
 210  		reportExpr(w, path, e.Type)
 211  
 212  	case *TypeSwitchGuard:
 213  		reportExpr(w, path, e.X)
 214  
 215  	case *ListExpr:
 216  		for _, elem := range e.ElemList {
 217  			reportExpr(w, path, elem)
 218  		}
 219  	}
 220  }
 221  
 222  func flag(w io.Writer, path string, pos Pos, kind string, msg string) {
 223  	if pos.IsKnown() {
 224  		fmt.Fprintf(w, "%s:%d:%d: [%s] %s\n", path, pos.Line(), pos.Col(), kind, msg)
 225  	} else {
 226  		fmt.Fprintf(w, "%s: [%s] %s\n", path, kind, msg)
 227  	}
 228  }
 229  
 230  func unquote(s string) string {
 231  	if int32(len(s)) >= 2 && s[0] == '"' && s[int32(len(s))-1] == '"' {
 232  		return s[1 : int32(len(s))-1]
 233  	}
 234  	return s
 235  }
 236