feat: clang-tidy parser (#26)
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			Co-authored-by: zjc_he <zjc_he@sjtu.edu.cn> Reviewed-on: FOCS-dev/JOJ3#26 Reviewed-by: 张泊明518370910136 <bomingzh@sjtu.edu.cn> Co-authored-by: 张佳澈520370910044 <zjc_he@sjtu.edu.cn> Co-committed-by: 张佳澈520370910044 <zjc_he@sjtu.edu.cn>
This commit is contained in:
		
							parent
							
								
									16a59a7fc2
								
							
						
					
					
						commit
						a042ac01ea
					
				
							
								
								
									
										10
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							|  | @ -22,7 +22,11 @@ | ||||||
| 	path = examples/keyword/cpplint/sillycode | 	path = examples/keyword/cpplint/sillycode | ||||||
| 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
| 	branch = keyword/cpplint/sillycode | 	branch = keyword/cpplint/sillycode | ||||||
| [submodule "examples/keyword/clang-tidy/sillycode"] | [submodule "examples/clangtidy/sillycode"] | ||||||
| 	path = examples/keyword/clang-tidy/sillycode | 	path = examples/clangtidy/sillycode | ||||||
| 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
| 	branch = keyword/clang-tidy/sillycode | 	branch = clangtidy/sillycode | ||||||
|  | [submodule "examples/keyword/clangtidy/sillycode"] | ||||||
|  | 	path = examples/keyword/clangtidy/sillycode | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = keyword/clangtidy/sillycode | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								examples/clangtidy/sillycode
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/clangtidy/sillycode
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit a69a7e87fddaddf21fc4f9cd6774e310fa7137c1 | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package parsers | package parsers | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/clangtidy" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/dummy" | ||||||
|  |  | ||||||
							
								
								
									
										134
									
								
								internal/parsers/clangtidy/convert.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								internal/parsers/clangtidy/convert.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | // Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py
 | ||||||
|  | package clangtidy | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strconv" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Level int | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	UNKNOWN Level = iota | ||||||
|  | 	NOTE | ||||||
|  | 	REMARK | ||||||
|  | 	WARNING | ||||||
|  | 	ERROR | ||||||
|  | 	FATAL | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ClangMessage struct { | ||||||
|  | 	filepath       string | ||||||
|  | 	line           int | ||||||
|  | 	column         int | ||||||
|  | 	level          Level | ||||||
|  | 	message        string | ||||||
|  | 	diagnosticName string | ||||||
|  | 	detailsLines   []string | ||||||
|  | 	children       []ClangMessage | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newClangMessage(filepath string, line int, column int, level Level, message string, diagnosticName string, detailsLines []string, children []ClangMessage) *ClangMessage { | ||||||
|  | 	if detailsLines == nil { | ||||||
|  | 		detailsLines = make([]string, 0) | ||||||
|  | 	} | ||||||
|  | 	if children == nil { | ||||||
|  | 		children = make([]ClangMessage, 0) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &ClangMessage{ | ||||||
|  | 		filepath:       filepath, | ||||||
|  | 		line:           line, | ||||||
|  | 		column:         column, | ||||||
|  | 		level:          level, | ||||||
|  | 		message:        message, | ||||||
|  | 		diagnosticName: diagnosticName, | ||||||
|  | 		detailsLines:   detailsLines, | ||||||
|  | 		children:       children, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func levelFromString(levelString string) Level { | ||||||
|  | 	switch levelString { | ||||||
|  | 	case "note": | ||||||
|  | 		return NOTE | ||||||
|  | 	case "remark": | ||||||
|  | 		return REMARK | ||||||
|  | 	case "warning": | ||||||
|  | 		return WARNING | ||||||
|  | 	case "error": | ||||||
|  | 		return ERROR | ||||||
|  | 	case "fatal": | ||||||
|  | 		return FATAL | ||||||
|  | 	default: | ||||||
|  | 		return UNKNOWN | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isIgnored(line string) bool { | ||||||
|  | 	ignoreRegex := regexp.MustCompile("^error:.*$") | ||||||
|  | 	return ignoreRegex.MatchString(line) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func parseMessage(line string) ClangMessage { | ||||||
|  | 	messageRegex := regexp.MustCompile(`^(?P<filepath>.+):(?P<line>\d+):(?P<column>\d+): (?P<level>\S+): (?P<message>.*?)(?: \[(?P<diagnostic_name>.*)\])?\n$`) | ||||||
|  | 	regexRes := messageRegex.FindStringSubmatch(line) | ||||||
|  | 	if len(regexRes) == 0 { | ||||||
|  | 		return *newClangMessage("", 0, 0, UNKNOWN, "", "", nil, nil) | ||||||
|  | 	} else { | ||||||
|  | 		filepath := regexRes[1] | ||||||
|  | 		line, _ := strconv.Atoi(regexRes[2]) | ||||||
|  | 		column, _ := strconv.Atoi(regexRes[3]) | ||||||
|  | 		level := levelFromString(regexRes[4]) | ||||||
|  | 		message := regexRes[5] | ||||||
|  | 		diagnosticName := regexRes[6] | ||||||
|  | 
 | ||||||
|  | 		return ClangMessage{ | ||||||
|  | 			filepath:       filepath, | ||||||
|  | 			line:           line, | ||||||
|  | 			column:         column, | ||||||
|  | 			level:          level, | ||||||
|  | 			message:        message, | ||||||
|  | 			diagnosticName: diagnosticName, | ||||||
|  | 			detailsLines:   make([]string, 0), | ||||||
|  | 			children:       make([]ClangMessage, 0), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func groupMessages(messages []ClangMessage) []ClangMessage { | ||||||
|  | 	groupedMessages := make([]ClangMessage, 0) | ||||||
|  | 	for _, message := range messages { | ||||||
|  | 		if message.level == NOTE { | ||||||
|  | 			groupedMessages[len(groupedMessages)-1].children = append(groupedMessages[len(groupedMessages)-1].children, message) | ||||||
|  | 		} else { | ||||||
|  | 			groupedMessages = append(groupedMessages, message) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return groupedMessages | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func convertPathsToRelative(messages *[]ClangMessage, conf Conf) { | ||||||
|  | 	currentDir := conf.RootDir | ||||||
|  | 	for i := range *messages { | ||||||
|  | 		(*messages)[i].filepath, _ = filepath.Rel(currentDir, (*messages)[i].filepath) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ParseLines(lines []string, conf Conf) []ClangMessage { | ||||||
|  | 	messages := make([]ClangMessage, 0) | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		if isIgnored(string(line)) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		message := parseMessage(string(line)) | ||||||
|  | 		if message.level == UNKNOWN && len(messages) > 0 { | ||||||
|  | 			messages[len(messages)-1].detailsLines = append(messages[len(messages)-1].detailsLines, string(line)) | ||||||
|  | 		} else { | ||||||
|  | 			messages = append(messages, message) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	convertPathsToRelative(&messages, conf) | ||||||
|  | 	return groupMessages(messages) | ||||||
|  | } | ||||||
							
								
								
									
										184
									
								
								internal/parsers/clangtidy/formatter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								internal/parsers/clangtidy/formatter.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,184 @@ | ||||||
|  | // Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py
 | ||||||
|  | package clangtidy | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type JsonMessage struct { | ||||||
|  | 	Type        string                 `json:"type"` | ||||||
|  | 	CheckName   string                 `json:"checkname"` | ||||||
|  | 	Description string                 `json:"description"` | ||||||
|  | 	Content     map[string]interface{} `json:"content"` | ||||||
|  | 	Categories  []string               `json:"categories"` | ||||||
|  | 	Location    map[string]interface{} `json:"location"` | ||||||
|  | 	Trace       map[string]interface{} `json:"trace"` | ||||||
|  | 	Severity    string                 `json:"severity"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Format(messages []ClangMessage) []JsonMessage { | ||||||
|  | 	formattedMessages := make([]JsonMessage, len(messages)) | ||||||
|  | 	for i, message := range messages { | ||||||
|  | 		formattedMessages[i] = formatMessage(message) | ||||||
|  | 	} | ||||||
|  | 	return formattedMessages | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func formatMessage(message ClangMessage) JsonMessage { | ||||||
|  | 	result := JsonMessage{ | ||||||
|  | 		Type:        "issue", | ||||||
|  | 		CheckName:   message.diagnosticName, | ||||||
|  | 		Description: message.message, | ||||||
|  | 		Content:     extractContent(message), | ||||||
|  | 		Categories:  extractCategories(message), | ||||||
|  | 		Location:    extractLocation(message), | ||||||
|  | 		Trace:       extractTrace(message), | ||||||
|  | 		Severity:    extractSeverity(message), | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func messagesToText(messages []ClangMessage) []string { | ||||||
|  | 	textLines := []string{} | ||||||
|  | 	for _, message := range messages { | ||||||
|  | 		textLines = append(textLines, fmt.Sprintf("%s:%d:%d: %s", message.filepath, message.line, message.column, message.message)) | ||||||
|  | 		textLines = append(textLines, message.detailsLines...) | ||||||
|  | 		textLines = append(textLines, messagesToText(message.children)...) | ||||||
|  | 	} | ||||||
|  | 	return textLines | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extractContent(message ClangMessage) map[string]interface{} { | ||||||
|  | 	detailLines := "" | ||||||
|  | 	for _, line := range message.detailsLines { | ||||||
|  | 		if line == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		detailLines += (line + "\n") | ||||||
|  | 	} | ||||||
|  | 	for _, line := range messagesToText(message.children) { | ||||||
|  | 		if line == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		detailLines += (line + "\n") | ||||||
|  | 	} | ||||||
|  | 	result := map[string]interface{}{ | ||||||
|  | 		"body": "```\n" + detailLines + "```", | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func removeDuplicates(list []string) []string { | ||||||
|  | 	uniqueMap := make(map[string]bool) | ||||||
|  | 	for _, v := range list { | ||||||
|  | 		uniqueMap[v] = true | ||||||
|  | 	} | ||||||
|  | 	result := []string{} | ||||||
|  | 	for k := range uniqueMap { | ||||||
|  | 		result = append(result, k) | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extractCategories(message ClangMessage) []string { | ||||||
|  | 	bugriskCategory := "Bug Risk" | ||||||
|  | 	clarityCategory := "Clarity" | ||||||
|  | 	compatibilityCategory := "Compatibility" | ||||||
|  | 	complexityCategory := "Complexity" | ||||||
|  | 	duplicationCategory := "Duplication" | ||||||
|  | 	performanceCategory := "Performance" | ||||||
|  | 	securityCategory := "Security" | ||||||
|  | 	styleCategory := "Style" | ||||||
|  | 
 | ||||||
|  | 	categories := []string{} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "bugprone") { | ||||||
|  | 		categories = append(categories, bugriskCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "modernize") { | ||||||
|  | 		categories = append(categories, compatibilityCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "portability") { | ||||||
|  | 		categories = append(categories, compatibilityCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "performance") { | ||||||
|  | 		categories = append(categories, performanceCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "readability") { | ||||||
|  | 		categories = append(categories, clarityCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "cloexec") { | ||||||
|  | 		categories = append(categories, securityCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "security") { | ||||||
|  | 		categories = append(categories, securityCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "naming") { | ||||||
|  | 		categories = append(categories, styleCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "misc") { | ||||||
|  | 		categories = append(categories, styleCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "cppcoreguidelines") { | ||||||
|  | 		categories = append(categories, styleCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "hicpp") { | ||||||
|  | 		categories = append(categories, styleCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "simplify") { | ||||||
|  | 		categories = append(categories, complexityCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "redundant") { | ||||||
|  | 		categories = append(categories, duplicationCategory) | ||||||
|  | 	} | ||||||
|  | 	if strings.HasPrefix(message.diagnosticName, "boost-use-to-string") { | ||||||
|  | 		categories = append(categories, compatibilityCategory) | ||||||
|  | 	} | ||||||
|  | 	if len(categories) == 0 { | ||||||
|  | 		categories = append(categories, bugriskCategory) | ||||||
|  | 	} | ||||||
|  | 	return removeDuplicates(categories) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extractLocation(message ClangMessage) map[string]interface{} { | ||||||
|  | 	location := map[string]interface{}{ | ||||||
|  | 		"path": message.filepath, | ||||||
|  | 		"lines": map[string]interface{}{ | ||||||
|  | 			"begin": message.line, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return location | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extractOtherLocations(message ClangMessage) []map[string]interface{} { | ||||||
|  | 	locationList := []map[string]interface{}{} | ||||||
|  | 	for _, child := range message.children { | ||||||
|  | 		locationList = append(locationList, extractLocation(child)) | ||||||
|  | 		locationList = append(locationList, extractOtherLocations(child)...) | ||||||
|  | 	} | ||||||
|  | 	return locationList | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extractTrace(message ClangMessage) map[string]interface{} { | ||||||
|  | 	result := map[string]interface{}{ | ||||||
|  | 		"locations": extractOtherLocations(message), | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extractSeverity(message ClangMessage) string { | ||||||
|  | 	switch message.level { | ||||||
|  | 	case NOTE: | ||||||
|  | 		return "info" | ||||||
|  | 	case REMARK: | ||||||
|  | 		return "minor" | ||||||
|  | 	case WARNING: | ||||||
|  | 		return "major" | ||||||
|  | 	case ERROR: | ||||||
|  | 		return "critical" | ||||||
|  | 	case FATAL: | ||||||
|  | 		return "blocker" | ||||||
|  | 	default: | ||||||
|  | 		return "unknown" | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								internal/parsers/clangtidy/meta.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/parsers/clangtidy/meta.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | package clangtidy | ||||||
|  | 
 | ||||||
|  | import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | ||||||
|  | 
 | ||||||
|  | var name = "clangtidy" | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	stage.RegisterParser(name, &ClangTidy{}) | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								internal/parsers/clangtidy/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								internal/parsers/clangtidy/parser.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | ||||||
|  | package clangtidy | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | ||||||
|  | 	"github.com/criyle/go-judge/envexec" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Match struct { | ||||||
|  | 	Keyword []string | ||||||
|  | 	Score   int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Conf struct { | ||||||
|  | 	Score   int    `default:"100"` | ||||||
|  | 	RootDir string `default:"/w"` | ||||||
|  | 	Matches []Match | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ClangTidy struct{} | ||||||
|  | 
 | ||||||
|  | func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult { | ||||||
|  | 	stdout := executorResult.Files["stdout"] | ||||||
|  | 	stderr := executorResult.Files["stderr"] | ||||||
|  | 
 | ||||||
|  | 	lines := strings.SplitAfter(stdout, "\n") | ||||||
|  | 	messages := ParseLines(lines, conf) | ||||||
|  | 	formattedMessages := Format(messages) | ||||||
|  | 
 | ||||||
|  | 	if executorResult.Status != stage.Status(envexec.StatusAccepted) { | ||||||
|  | 		if !((executorResult.Status == stage.Status(envexec.StatusNonzeroExitStatus)) && | ||||||
|  | 			(executorResult.ExitStatus == 1)) { | ||||||
|  | 			return stage.ParserResult{ | ||||||
|  | 				Score: 0, | ||||||
|  | 				Comment: fmt.Sprintf( | ||||||
|  | 					"Unexpected executor status: %s.\nStderr: %s", | ||||||
|  | 					executorResult.Status, stderr, | ||||||
|  | 				), | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return stage.ParserResult{ | ||||||
|  | 		Score:   GetScore(formattedMessages, conf), | ||||||
|  | 		Comment: GetComment(formattedMessages), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (*ClangTidy) Run(results []stage.ExecutorResult, confAny any) ( | ||||||
|  | 	[]stage.ParserResult, bool, error, | ||||||
|  | ) { | ||||||
|  | 	conf, err := stage.DecodeConf[Conf](confAny) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, true, err | ||||||
|  | 	} | ||||||
|  | 	var res []stage.ParserResult | ||||||
|  | 	for _, result := range results { | ||||||
|  | 		res = append(res, Parse(result, *conf)) | ||||||
|  | 	} | ||||||
|  | 	return res, false, nil | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								internal/parsers/clangtidy/score.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								internal/parsers/clangtidy/score.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | package clangtidy | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func contains(arr []string, element string) bool { | ||||||
|  | 	for i := range arr { | ||||||
|  | 		// TODO: The keyword in json report might also be an array, need to split it
 | ||||||
|  | 		if strings.Contains(arr[i], element) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetScore(jsonMessages []JsonMessage, conf Conf) int { | ||||||
|  | 	fullmark := conf.Score | ||||||
|  | 	for _, jsonMessage := range jsonMessages { | ||||||
|  | 		keyword := jsonMessage.CheckName | ||||||
|  | 		for _, match := range conf.Matches { | ||||||
|  | 			if contains(match.Keyword, keyword) { | ||||||
|  | 				fullmark -= match.Score | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return fullmark | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetComment(jsonMessages []JsonMessage) string { | ||||||
|  | 	res := "### Test results summary\n\n" | ||||||
|  | 	keys := [...]string{ | ||||||
|  | 		"codequality-unchecked-malloc-result", | ||||||
|  | 		"codequality-no-global-variables", | ||||||
|  | 		"codequality-no-header-guard", | ||||||
|  | 		"codequality-no-fflush-stdin", | ||||||
|  | 		"readability-function-size", | ||||||
|  | 		"readability-duplicate-include", | ||||||
|  | 		"readability-identifier-naming", | ||||||
|  | 		"readability-redundant", | ||||||
|  | 		"readability-misleading-indentation", | ||||||
|  | 		"readability-misplaced-array-index", | ||||||
|  | 		"cppcoreguidelines-init-variables", | ||||||
|  | 		"bugprone-suspicious-string-compare", | ||||||
|  | 		"google-global-names-in-headers", | ||||||
|  | 		"clang-diagnostic", | ||||||
|  | 		"clang-analyzer", | ||||||
|  | 		"misc", | ||||||
|  | 		"performance", | ||||||
|  | 		"others", | ||||||
|  | 	} | ||||||
|  | 	mapping := map[string]int{} | ||||||
|  | 	for _, key := range keys { | ||||||
|  | 		mapping[key] = 0 | ||||||
|  | 	} | ||||||
|  | 	for _, jsonMessage := range jsonMessages { | ||||||
|  | 		keyword := jsonMessage.CheckName | ||||||
|  | 		flag := true | ||||||
|  | 		for key := range mapping { | ||||||
|  | 			if strings.Contains(keyword, key) { | ||||||
|  | 				mapping[key] += 1 | ||||||
|  | 				flag = false | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if flag { | ||||||
|  | 			mapping["others"] += 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i, key := range keys { | ||||||
|  | 		res = fmt.Sprintf("%s%d. %s: %d\n", res, i+1, key, mapping[key]) | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user