未验证 提交 2afec010 编写于 作者: P Phodal Huang

feat: remove bad smell from repo

上级 3e67cbf5
package bs
import (
"encoding/json"
"fmt"
"github.com/antlr/antlr4/runtime/Go/antlr"
"os"
"path/filepath"
"strconv"
"strings"
. "github.com/phodal/coca/bs/models"
. "github.com/phodal/coca/language/java"
. "github.com/phodal/coca/utils"
)
var nodeInfos []JFullClassNode
type BadSmellModel struct {
File string
Line string
Bs string
}
type BadSmellApp struct {
}
func (j *BadSmellApp) AnalysisPath(codeDir string) []BadSmellModel {
nodeInfos = nil
files := (*BadSmellApp)(nil).javaFiles(codeDir)
for index := range files {
nodeInfo := NewJFullClassNode()
file := files[index]
displayName := filepath.Base(file)
fmt.Println("Start parse java call: " + displayName)
parser := (*BadSmellApp)(nil).processFile(file)
context := parser.CompilationUnit()
listener := NewBadSmellListener()
antlr.NewParseTreeWalker().Walk(listener, context)
nodeInfo = listener.getNodeInfo()
nodeInfo.Path = file
nodeInfos = append(nodeInfos, *nodeInfo)
}
bsModel, _ := json.MarshalIndent(nodeInfos, "", "\t")
WriteToFile("nodeInfos.json", string(bsModel))
bsList := analysisBadSmell(nodeInfos)
return bsList
}
func analysisBadSmell(nodes []JFullClassNode) []BadSmellModel {
var badSmellList []BadSmellModel
for _, node := range nodes {
// To be Defined number
if node.Type == "Class" && len(node.Methods) < 1 {
badSmellList = append(badSmellList, *&BadSmellModel{node.Path, "", "lazyElement"})
}
onlyHaveGetterAndSetter := true
// Long Method
for _, method := range node.Methods {
if method.StopLine-method.StartLine > 50 {
longMethod := &BadSmellModel{node.Path, strconv.Itoa(method.StartLine), "longMethod"}
badSmellList = append(badSmellList, *longMethod)
}
if strings.Contains(method.Name, "get") && strings.Contains(method.Name, "set") {
onlyHaveGetterAndSetter = false
}
// longParameterList
if len(method.Parameters) > 6 {
longParams := &BadSmellModel{node.Path, strconv.Itoa(method.StartLine), "longParameterList"}
badSmellList = append(badSmellList, *longParams)
}
// longParameterList
if method.MethodBs.IfSize > 8 || method.MethodBs.SwitchSize > 8 {
longParams := &BadSmellModel{node.Path, strconv.Itoa(method.StartLine), "repeatedSwitches"}
badSmellList = append(badSmellList, *longParams)
}
}
fmt.Println(onlyHaveGetterAndSetter, node.Type, len(node.Methods))
if onlyHaveGetterAndSetter && node.Type == "Class" && len(node.Methods) > 0 {
dataClass := &BadSmellModel{node.Path, "", "dataClass"}
badSmellList = append(badSmellList, *dataClass)
}
//Refused Bequest
if node.Extends != "" {
hasCallParentMethod := false
for _, methodCall := range node.MethodCalls {
if methodCall.Class == node.Extends {
hasCallParentMethod = true
}
}
if !hasCallParentMethod {
badSmellList = append(badSmellList, *&BadSmellModel{node.Path, "", "refusedBequest"})
}
}
// LargeClass
if node.Type == "Class" && len(node.Methods) > 30 {
badSmellList = append(badSmellList, *&BadSmellModel{node.Path, "", "largeClass"})
}
}
return badSmellList
}
func (j *BadSmellApp) javaFiles(codeDir string) []string {
files := make([]string, 0)
_ = filepath.Walk(codeDir, func(path string, fi os.FileInfo, err error) error {
if strings.HasSuffix(path, ".java") && !strings.Contains(path, "Test.java") {
files = append(files, path)
}
return nil
})
return files
}
func (j *BadSmellApp) processFile(path string) *JavaParser {
is, _ := antlr.NewFileStream(path)
lexer := NewJavaLexer(is)
stream := antlr.NewCommonTokenStream(lexer, 0);
parser := NewJavaParser(stream)
return parser
}
package bs
import (
"github.com/antlr/antlr4/runtime/Go/antlr"
. "github.com/phodal/coca/bs/models"
. "github.com/phodal/coca/language/java"
"reflect"
"strings"
)
var imports []string
var clzs []string
var currentPkg string
var currentClz string
var currentClzType string
var currentClzExtends string
var currentClzImplements []string
var methods []JFullMethod
var methodCalls []JFullMethodCall
var fields = make(map[string]string)
var localVars = make(map[string]string)
var formalParameters = make(map[string]string)
var currentClassBs ClassBadSmellInfo
func NewBadSmellListener() *BadSmellListener {
currentClz = ""
currentPkg = ""
methods = nil
methodCalls = nil
currentClzImplements = nil
currentClzExtends = ""
return &BadSmellListener{}
}
type BadSmellListener struct {
BaseJavaParserListener
}
func (s *BadSmellListener) getNodeInfo() *JFullClassNode {
return &JFullClassNode{
currentPkg,
currentClz,
currentClzType,
"",
currentClzExtends,
currentClzImplements,
methods,
methodCalls,
currentClassBs,
}
}
func (s *BadSmellListener) EnterPackageDeclaration(ctx *PackageDeclarationContext) {
currentPkg = ctx.QualifiedName().GetText()
}
func (s *BadSmellListener) EnterImportDeclaration(ctx *ImportDeclarationContext) {
importText := ctx.QualifiedName().GetText()
imports = append(imports, importText)
}
func (s *BadSmellListener) EnterClassDeclaration(ctx *ClassDeclarationContext) {
currentClzType = "Class"
currentClz = ctx.IDENTIFIER().GetText()
if ctx.EXTENDS() != nil {
currentClzExtends = ctx.TypeType().GetText()
}
if ctx.IMPLEMENTS() != nil {
typeList := ctx.TypeList().(*TypeListContext)
for _, typ := range typeList.AllTypeType() {
typeData := getTypeDATA(typ.(*TypeTypeContext))
currentClzImplements = append(currentClzImplements, typeData)
}
}
}
func getTypeDATA(typ *TypeTypeContext) string {
var typeData string
classOrInterface := typ.ClassOrInterfaceType().(*ClassOrInterfaceTypeContext)
if classOrInterface != nil {
identifiers := classOrInterface.AllIDENTIFIER()
typeData = identifiers[len(identifiers)-1].GetText()
}
return typeData
}
func (s *BadSmellListener) EnterInterfaceDeclaration(ctx *InterfaceDeclarationContext) {
currentClzType = "Interface"
currentClz = ctx.IDENTIFIER().GetText()
}
func (s *BadSmellListener) EnterInterfaceMethodDeclaration(ctx *InterfaceMethodDeclarationContext) {
startLine := ctx.GetStart().GetLine()
startLinePosition := ctx.IDENTIFIER().GetSymbol().GetColumn()
stopLine := ctx.GetStop().GetLine()
name := ctx.IDENTIFIER().GetText()
stopLinePosition := startLinePosition + len(name)
methodBody := ctx.MethodBody().GetText()
typeType := ctx.TypeTypeOrVoid().GetText()
var methodParams []JFullParameter = nil
parameters := ctx.FormalParameters()
if parameters != nil {
if reflect.TypeOf(parameters.GetChild(1)).String() == "*parser.FormalParameterListContext" {
allFormal := parameters.GetChild(1).(*FormalParameterListContext)
formalParameter := allFormal.AllFormalParameter()
for _, param := range formalParameter {
paramContext := param.(*FormalParameterContext)
paramType := paramContext.TypeType().GetText()
paramValue := paramContext.VariableDeclaratorId().(*VariableDeclaratorIdContext).IDENTIFIER().GetText()
methodParams = append(methodParams, *&JFullParameter{paramType, paramValue})
}
}
}
methodBSInfo := *&MethodBadSmellInfo{0, 0}
method := &JFullMethod{
name,
typeType,
startLine,
startLinePosition,
stopLine,
stopLinePosition,
methodBody,
methodParams,
methodBSInfo,
}
methods = append(methods, *method)
}
func (s *BadSmellListener) EnterFormalParameter(ctx *FormalParameterContext) {
formalParameters[ctx.VariableDeclaratorId().GetText()] = ctx.TypeType().GetText()
}
func (s *BadSmellListener) EnterFieldDeclaration(ctx *FieldDeclarationContext) {
declarators := ctx.VariableDeclarators()
variableName := declarators.GetParent().GetChild(0).(antlr.ParseTree).GetText()
fields[variableName] = ctx.TypeType().GetText()
}
func (s *BadSmellListener) EnterLocalVariableDeclaration(ctx *LocalVariableDeclarationContext) {
typ := ctx.GetChild(0).(antlr.ParseTree).GetText()
variableName := ctx.GetChild(1).GetChild(0).GetChild(0).(antlr.ParseTree).GetText()
localVars[variableName] = typ
}
func (s *BadSmellListener) EnterMethodDeclaration(ctx *MethodDeclarationContext) {
startLine := ctx.GetStart().GetLine()
startLinePosition := ctx.IDENTIFIER().GetSymbol().GetColumn()
stopLine := ctx.GetStop().GetLine()
name := ctx.IDENTIFIER().GetText()
stopLinePosition := startLinePosition + len(name)
//XXX: find the start position of {, not public
typeType := ctx.TypeTypeOrVoid().GetText()
methodBody := ctx.MethodBody().GetText()
var methodParams []JFullParameter = nil
parameters := ctx.FormalParameters()
if parameters != nil {
if reflect.TypeOf(parameters.GetChild(1)).String() == "*parser.FormalParameterListContext" {
allFormal := parameters.GetChild(1).(*FormalParameterListContext)
formalParameter := allFormal.AllFormalParameter()
for _, param := range formalParameter {
paramContext := param.(*FormalParameterContext)
paramType := paramContext.TypeType().GetText()
paramValue := paramContext.VariableDeclaratorId().(*VariableDeclaratorIdContext).IDENTIFIER().GetText()
methodParams = append(methodParams, *&JFullParameter{paramType, paramValue})
}
}
}
methodBSInfo := *&MethodBadSmellInfo{0, 0}
methodBadSmellInfo := buildMethodBSInfo(ctx, methodBSInfo)
method := &JFullMethod{
name,
typeType,
startLine,
startLinePosition,
stopLine,
stopLinePosition,
methodBody,
methodParams,
methodBadSmellInfo,
}
methods = append(methods, *method)
}
func buildMethodBSInfo(context *MethodDeclarationContext, bsInfo MethodBadSmellInfo) MethodBadSmellInfo {
methodBody := context.MethodBody()
blockContext := methodBody.GetChild(0)
if reflect.TypeOf(blockContext).String() == "*parser.BlockContext" {
blcStatement := blockContext.(*BlockContext).AllBlockStatement()
for _, statement := range blcStatement {
if reflect.TypeOf(statement.GetChild(0)).String() == "*parser.StatementContext" {
if len(statement.GetChild(0).(*StatementContext).GetChildren()) < 3 {
continue
}
statementCtx := statement.GetChild(0).(*StatementContext)
if (reflect.TypeOf(statementCtx.GetChild(1)).String()) == "*parser.ParExpressionContext" {
if statementCtx.GetChild(0).(antlr.ParseTree).GetText() == "if" {
bsInfo.IfSize = bsInfo.IfSize + 1
}
if statementCtx.GetChild(0).(antlr.ParseTree).GetText() == "switch" {
bsInfo.SwitchSize = bsInfo.SwitchSize + 1
}
}
}
}
}
return bsInfo
}
func (s *BadSmellListener) EnterFormalParameterList(ctx *FormalParameterListContext) {
//fmt.Println(ctx.GetParent().GetParent().(antlr.RuleNode).get)
//fmt.Println(ctx.AllFormalParameter()
}
func (s *BadSmellListener) EnterAnnotation(ctx *AnnotationContext) {
if currentClzType == "Class" && ctx.QualifiedName().GetText() == "Override" {
currentClassBs.OverrideSize++
}
}
func (s *BadSmellListener) EnterMethodCall(ctx *MethodCallContext) {
var targetCtx = ctx.GetParent().GetChild(0).(antlr.ParseTree).GetText()
var targetType = parseTargetType(targetCtx)
callee := ctx.GetChild(0).(antlr.ParseTree).GetText()
startLine := ctx.GetStart().GetLine()
startLinePosition := ctx.GetStart().GetColumn()
stopLine := ctx.GetStop().GetLine()
stopLinePosition := startLinePosition + len(callee)
//typeType := ctx.GetChild(0).(antlr.ParseTree).TypeTypeOrVoid().GetText()
fullType := warpTargetFullType(targetType)
if fullType != "" {
jMethodCall := &JFullMethodCall{removeTarget(fullType), "", targetType, callee, startLine, startLinePosition, stopLine, stopLinePosition}
methodCalls = append(methodCalls, *jMethodCall)
} else {
if ctx.GetText() == targetType {
methodName := ctx.IDENTIFIER().GetText()
jMethodCall := &JFullMethodCall{currentPkg, "", currentClz, methodName, startLine, startLinePosition, stopLine, stopLinePosition}
methodCalls = append(methodCalls, *jMethodCall)
}
}
}
func (s *BadSmellListener) EnterExpression(ctx *ExpressionContext) {
// lambda BlogPO::of
if ctx.COLONCOLON() != nil {
text := ctx.Expression(0).GetText()
methodName := ctx.IDENTIFIER().GetText()
targetType := parseTargetType(text)
fullType := warpTargetFullType(targetType)
startLine := ctx.GetStart().GetLine()
startLinePosition := ctx.GetStart().GetColumn()
stopLine := ctx.GetStop().GetLine()
stopLinePosition := startLinePosition + len(text)
jMethodCall := &JFullMethodCall{removeTarget(fullType), "", targetType, methodName, startLine, startLinePosition, stopLine, stopLinePosition}
methodCalls = append(methodCalls, *jMethodCall)
}
}
func (s *BadSmellListener) appendClasses(classes []string) {
clzs = classes
}
func removeTarget(fullType string) string {
split := strings.Split(fullType, ".")
return strings.Join(split[:len(split)-1], ".")
}
func parseTargetType(targetCtx string) string {
targetVar := targetCtx
targetType := targetVar
//TODO: update this reflect
typeOf := reflect.TypeOf(targetCtx).String()
if strings.HasSuffix(typeOf, "MethodCallContext") {
targetType = currentClz;
} else {
fieldType := fields[targetVar]
formalType := formalParameters[targetVar]
localVarType := localVars[targetVar]
if fieldType != "" {
targetType = fieldType
} else if formalType != "" {
targetType = formalType;
} else if localVarType != "" {
targetType = localVarType;
}
}
return targetType
}
func warpTargetFullType(targetType string) string {
if strings.EqualFold(currentClz, targetType) {
return currentPkg + "." + targetType
}
for index := range imports {
imp := imports[index]
if strings.HasSuffix(imp, targetType) {
return imp
}
}
//maybe the same package
for _, clz := range clzs {
if strings.HasSuffix(clz, "."+targetType) {
return clz
}
}
//1. current package, 2. import by *
return ""
}
package models
type JFullClassNode struct {
Package string
Class string
Type string
Path string
Extends string
Implements []string
Methods []JFullMethod
MethodCalls []JFullMethodCall
ClassBS ClassBadSmellInfo
}
type JFullMethodCall struct {
Package string
Type string
Class string
MethodName string
StartLine int
StartLinePosition int
StopLine int
StopLinePosition int
}
type JFullMethod struct {
Name string
Type string
StartLine int
StartLinePosition int
StopLine int
StopLinePosition int
MethodBody string
Parameters []JFullParameter
MethodBs MethodBadSmellInfo
}
type MethodBadSmellInfo struct {
IfSize int
SwitchSize int
}
type ClassBadSmellInfo struct {
OverrideSize int
PublicVarSize int
}
type JFullParameter struct {
Name string
Type string
}
func NewJFullClassNode() *JFullClassNode {
info := &ClassBadSmellInfo{0, 0};
return &JFullClassNode{
"",
"",
"",
"",
"",
nil,
nil,
nil,
*info}
}
package main
import (
"encoding/json"
. "github.com/phodal/coca/bs"
. "github.com/phodal/coca/utils"
)
func main() {
bsApp := new(BadSmellApp)
bsList := bsApp.AnalysisPath("examples/")
bsModel, _ := json.MarshalIndent(bsList, "", "\t")
WriteToFile("bs.json", string(bsModel))
}
package cmd
import (
"encoding/json"
"github.com/spf13/cobra"
. "github.com/phodal/coca/bs"
. "github.com/phodal/coca/utils"
)
var badsmellCmd *cobra.Command = &cobra.Command{
Use: "badsmell",
Short: "Bad Code Smell",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
importPath := cmd.Flag("path").Value.String()
if importPath != "" {
bsApp := new(BadSmellApp)
bsList := bsApp.AnalysisPath(importPath)
bsModel, _ := json.MarshalIndent(bsList, "", "\t")
WriteToFile("bs.json", string(bsModel))
}
},
}
func init() {
rootCmd.AddCommand(badsmellCmd)
badsmellCmd.PersistentFlags().StringP("path", "p", "Code Path", "example -p src/main")
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册