github.com/wiselike/revel-cmd@v1.2.1/parser/appends.go (about) 1 package parser 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/wiselike/revel-cmd/model" 8 "github.com/wiselike/revel-cmd/utils" 9 ) 10 11 // If this Decl is a struct type definition, it is summarized and added to specs. 12 // Else, specs is returned unchanged. 13 func appendStruct(fileName string, specs []*model.TypeInfo, pkgImportPath string, pkg *ast.Package, decl ast.Decl, imports map[string]string, fset *token.FileSet) []*model.TypeInfo { 14 // Filter out non-Struct type declarations. 15 spec, found := getStructTypeDecl(decl, fset) 16 if !found { 17 return specs 18 } 19 20 structType := spec.Type.(*ast.StructType) 21 22 // At this point we know it's a type declaration for a struct. 23 // Fill in the rest of the info by diving into the fields. 24 // Add it provisionally to the Controller list -- it's later filtered using field info. 25 controllerSpec := &model.TypeInfo{ 26 StructName: spec.Name.Name, 27 ImportPath: pkgImportPath, 28 PackageName: pkg.Name, 29 } 30 31 for _, field := range structType.Fields.List { 32 // If field.Names is set, it's not an embedded type. 33 if field.Names != nil { 34 continue 35 } 36 37 // A direct "sub-type" has an ast.Field as either: 38 // Ident { "AppController" } 39 // SelectorExpr { "rev", "Controller" } 40 // Additionally, that can be wrapped by StarExprs. 41 fieldType := field.Type 42 pkgName, typeName := func() (string, string) { 43 // Drill through any StarExprs. 44 for { 45 if starExpr, ok := fieldType.(*ast.StarExpr); ok { 46 fieldType = starExpr.X 47 continue 48 } 49 break 50 } 51 52 // If the embedded type is in the same package, it's an Ident. 53 if ident, ok := fieldType.(*ast.Ident); ok { 54 return "", ident.Name 55 } 56 57 if selectorExpr, ok := fieldType.(*ast.SelectorExpr); ok { 58 if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok { 59 return pkgIdent.Name, selectorExpr.Sel.Name 60 } 61 } 62 return "", "" 63 }() 64 65 // If a typename wasn't found, skip it. 66 if typeName == "" { 67 continue 68 } 69 70 // Find the import path for this type. 71 // If it was referenced without a package name, use the current package import path. 72 // Else, look up the package's import path by name. 73 var importPath string 74 if pkgName == "" { 75 importPath = pkgImportPath 76 } else { 77 var ok bool 78 if importPath, ok = imports[pkgName]; !ok { 79 utils.Logger.Error("Error: Failed to find import path for ", "package", pkgName, "type", typeName) 80 continue 81 } 82 } 83 84 controllerSpec.EmbeddedTypes = append(controllerSpec.EmbeddedTypes, &model.EmbeddedTypeName{ 85 ImportPath: importPath, 86 StructName: typeName, 87 }) 88 } 89 90 return append(specs, controllerSpec) 91 } 92 93 // If decl is a Method declaration, it is summarized and added to the array 94 // underneath its receiver type. 95 // e.g. "Login" => {MethodSpec, MethodSpec, ..}. 96 func appendAction(fset *token.FileSet, mm methodMap, decl ast.Decl, pkgImportPath, pkgName string, imports map[string]string) { 97 // Func declaration? 98 funcDecl, ok := decl.(*ast.FuncDecl) 99 if !ok { 100 return 101 } 102 103 // Have a receiver? 104 if funcDecl.Recv == nil { 105 return 106 } 107 108 // Is it public? 109 if !funcDecl.Name.IsExported() { 110 return 111 } 112 113 // Does it return a Result? 114 if funcDecl.Type.Results == nil || len(funcDecl.Type.Results.List) != 1 { 115 return 116 } 117 selExpr, ok := funcDecl.Type.Results.List[0].Type.(*ast.SelectorExpr) 118 if !ok { 119 return 120 } 121 if selExpr.Sel.Name != "Result" { 122 return 123 } 124 if pkgIdent, ok := selExpr.X.(*ast.Ident); !ok || imports[pkgIdent.Name] != model.RevelImportPath { 125 return 126 } 127 128 method := &model.MethodSpec{ 129 Name: funcDecl.Name.Name, 130 } 131 132 // Add a description of the arguments to the method. 133 for _, field := range funcDecl.Type.Params.List { 134 for _, name := range field.Names { 135 var importPath string 136 typeExpr := model.NewTypeExprFromAst(pkgName, field.Type) 137 if !typeExpr.Valid { 138 utils.Logger.Warnf("Warn: Didn't understand argument '%s' of action %s. Ignoring.", name, getFuncName(funcDecl)) 139 return // We didn't understand one of the args. Ignore this action. 140 } 141 // Local object 142 if typeExpr.PkgName == pkgName { 143 importPath = pkgImportPath 144 } else if typeExpr.PkgName != "" { 145 var ok bool 146 if importPath, ok = imports[typeExpr.PkgName]; !ok { 147 utils.Logger.Fatalf("Failed to find import for arg of type: %s , %s", typeExpr.PkgName, typeExpr.TypeName("")) 148 } 149 } 150 method.Args = append(method.Args, &model.MethodArg{ 151 Name: name.Name, 152 TypeExpr: typeExpr, 153 ImportPath: importPath, 154 }) 155 } 156 } 157 158 // Add a description of the calls to Render from the method. 159 // Inspect every node (e.g. always return true). 160 method.RenderCalls = []*model.MethodCall{} 161 ast.Inspect(funcDecl.Body, func(node ast.Node) bool { 162 // Is it a function call? 163 callExpr, ok := node.(*ast.CallExpr) 164 if !ok { 165 return true 166 } 167 168 // Is it calling (*Controller).Render? 169 selExpr, ok := callExpr.Fun.(*ast.SelectorExpr) 170 if !ok { 171 return true 172 } 173 174 // The type of the receiver is not easily available, so just store every 175 // call to any method called Render. 176 if selExpr.Sel.Name != "Render" { 177 return true 178 } 179 180 // Add this call's args to the renderArgs. 181 pos := fset.Position(callExpr.Lparen) 182 methodCall := &model.MethodCall{ 183 Line: pos.Line, 184 Names: []string{}, 185 } 186 for _, arg := range callExpr.Args { 187 argIdent, ok := arg.(*ast.Ident) 188 if !ok { 189 continue 190 } 191 methodCall.Names = append(methodCall.Names, argIdent.Name) 192 } 193 method.RenderCalls = append(method.RenderCalls, methodCall) 194 return true 195 }) 196 197 var recvTypeName string 198 recvType := funcDecl.Recv.List[0].Type 199 if recvStarType, ok := recvType.(*ast.StarExpr); ok { 200 recvTypeName = recvStarType.X.(*ast.Ident).Name 201 } else { 202 recvTypeName = recvType.(*ast.Ident).Name 203 } 204 205 mm[recvTypeName] = append(mm[recvTypeName], method) 206 } 207 208 // Combine the 2 source info models into one. 209 func appendSourceInfo(srcInfo1, srcInfo2 *model.SourceInfo) *model.SourceInfo { 210 if srcInfo1 == nil { 211 return srcInfo2 212 } 213 214 srcInfo1.StructSpecs = append(srcInfo1.StructSpecs, srcInfo2.StructSpecs...) 215 srcInfo1.InitImportPaths = append(srcInfo1.InitImportPaths, srcInfo2.InitImportPaths...) 216 for k, v := range srcInfo2.ValidationKeys { 217 if _, ok := srcInfo1.ValidationKeys[k]; ok { 218 utils.Logger.Warn("Warn: Key conflict when scanning validation calls:", "key", k) 219 continue 220 } 221 srcInfo1.ValidationKeys[k] = v 222 } 223 return srcInfo1 224 }