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  }