feat(internal/executors/clang_tidy,-internal/parsers/clang_tidy,-cmd/joj3/main_test.go): Parsers and executors for clang-tidy
This commit is contained in:
		
							parent
							
								
									eb4815f10d
								
							
						
					
					
						commit
						e133b13a84
					
				|  | @ -53,6 +53,11 @@ func TestMain(t *testing.T) { | ||||||
| 				{Score: 100, Comment: "executor status: run time: \\d+ ns, memory: \\d+ bytes"}, | 				{Score: 100, Comment: "executor status: run time: \\d+ ns, memory: \\d+ bytes"}, | ||||||
| 			}}, | 			}}, | ||||||
| 		}}, | 		}}, | ||||||
|  | 		{"clang_tidy", []stage.StageResult{ | ||||||
|  | 			{Name: "clang-tidy", Results: []stage.ParserResult{ | ||||||
|  | 				{Score: -200, Comment: ""}, | ||||||
|  | 			}}, | ||||||
|  | 		}}, | ||||||
| 		{"compile_error", []stage.StageResult{ | 		{"compile_error", []stage.StageResult{ | ||||||
| 			{Name: "compile", Results: []stage.ParserResult{ | 			{Name: "compile", Results: []stage.ParserResult{ | ||||||
| 				{Score: 0, Comment: "Unexpected executor status: Nonzero Exit Status\\."}, | 				{Score: 0, Comment: "Unexpected executor status: Nonzero Exit Status\\."}, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,10 @@ | ||||||
| package clang_tidy | package clang_tidy | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"os/exec" | ||||||
|  | 
 | ||||||
| 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | ||||||
| 	"github.com/criyle/go-judge/envexec" | 	"github.com/criyle/go-judge/envexec" | ||||||
| ) | ) | ||||||
|  | @ -9,17 +13,49 @@ type ClangTidy struct{} | ||||||
| 
 | 
 | ||||||
| func (e *ClangTidy) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) { | func (e *ClangTidy) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) { | ||||||
| 	var res []stage.ExecutorResult | 	var res []stage.ExecutorResult | ||||||
| 	for range cmds { | 
 | ||||||
| 		res = append(res, stage.ExecutorResult{ | 	for _, cmd := range cmds { | ||||||
| 			Status:     stage.Status(envexec.StatusInvalid), | 		args := "" | ||||||
|  | 		for _, arg := range cmd.Args { | ||||||
|  | 			args += fmt.Sprint(arg) | ||||||
|  | 			args += " " | ||||||
|  | 		} | ||||||
|  | 		clang_tidy_Cmd := exec.Command("bash", "-c", args) | ||||||
|  | 		clang_tidy_stdout, err1 := clang_tidy_Cmd.StdoutPipe() | ||||||
|  | 		clang_tidy_stderr, err2 := clang_tidy_Cmd.StderrPipe() | ||||||
|  | 		if err1 != nil { | ||||||
|  | 			return nil, err1 | ||||||
|  | 		} | ||||||
|  | 		if err2 != nil { | ||||||
|  | 			return nil, err2 | ||||||
|  | 		} | ||||||
|  | 		_ = clang_tidy_Cmd.Start() | ||||||
|  | 		clang_tidy_Out, err1 := io.ReadAll(clang_tidy_stdout) | ||||||
|  | 		clang_tidy_Err, err2 := io.ReadAll(clang_tidy_stderr) | ||||||
|  | 
 | ||||||
|  | 		if err1 != nil { | ||||||
|  | 			return nil, err1 | ||||||
|  | 		} | ||||||
|  | 		if err2 != nil { | ||||||
|  | 			return nil, err2 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		_ = clang_tidy_Cmd.Wait() | ||||||
|  | 
 | ||||||
|  | 		r := stage.ExecutorResult{ | ||||||
|  | 			Status:     stage.Status(envexec.StatusAccepted), | ||||||
| 			ExitStatus: 0, | 			ExitStatus: 0, | ||||||
| 			Error:      "I'm a dummy", | 			Error:      "", | ||||||
| 			Time:       0, | 			Time:       0, | ||||||
| 			Memory:     0, | 			Memory:     0, | ||||||
| 			RunTime:    0, | 			RunTime:    0, | ||||||
| 			Files:      map[string]string{}, | 			Files:      map[string]string{}, | ||||||
| 			FileIDs:    map[string]string{}, | 			FileIDs:    map[string]string{}, | ||||||
| 		}) | 		} | ||||||
|  | 		r.Files["stdout"] = string(clang_tidy_Out) | ||||||
|  | 		// TODO: We may don't want stderr
 | ||||||
|  | 		r.Files["stderr"] = string(clang_tidy_Err) | ||||||
|  | 		res = append(res, r) | ||||||
| 	} | 	} | ||||||
| 	return res, nil | 	return res, nil | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										135
									
								
								internal/parsers/clang_tidy/convert.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								internal/parsers/clang_tidy/convert.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,135 @@ | ||||||
|  | // Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py
 | ||||||
|  | package clang_tidy | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"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 is_ignored(line string) bool { | ||||||
|  | 	IGNORE_REGEX := regexp.MustCompile("^error:.*$") | ||||||
|  | 	return IGNORE_REGEX.MatchString(line) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func parse_message(line string) ClangMessage { | ||||||
|  | 	MESSAGE_REGEX := regexp.MustCompile(`^(?P<filepath>.+):(?P<line>\d+):(?P<column>\d+): (?P<level>\S+): (?P<message>.*?)(?: \[(?P<diagnostic_name>.*)\])?\n$`) | ||||||
|  | 	regex_res := MESSAGE_REGEX.FindStringSubmatch(line) | ||||||
|  | 	if len(regex_res) == 0 { | ||||||
|  | 		return *newClangMessage("", 0, 0, UNKNOWN, "", "", nil, nil) | ||||||
|  | 	} else { | ||||||
|  | 		filepath := regex_res[1] | ||||||
|  | 		line, _ := strconv.Atoi(regex_res[2]) | ||||||
|  | 		column, _ := strconv.Atoi(regex_res[3]) | ||||||
|  | 		level := LevelFromString(regex_res[4]) | ||||||
|  | 		message := regex_res[5] | ||||||
|  | 		diagnostic_name := regex_res[6] | ||||||
|  | 
 | ||||||
|  | 		return ClangMessage{ | ||||||
|  | 			filepath:       filepath, | ||||||
|  | 			line:           line, | ||||||
|  | 			column:         column, | ||||||
|  | 			level:          level, | ||||||
|  | 			message:        message, | ||||||
|  | 			diagnosticName: diagnostic_name, | ||||||
|  | 			detailsLines:   make([]string, 0), | ||||||
|  | 			children:       make([]ClangMessage, 0), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func group_messages(messages []ClangMessage) []ClangMessage { | ||||||
|  | 	grouped_messages := make([]ClangMessage, 0) | ||||||
|  | 	for _, message := range messages { | ||||||
|  | 		if message.level == NOTE { | ||||||
|  | 			grouped_messages[len(grouped_messages)-1].children = append(grouped_messages[len(grouped_messages)-1].children, message) | ||||||
|  | 		} else { | ||||||
|  | 			grouped_messages = append(grouped_messages, message) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return grouped_messages | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func convert_paths_to_relative(messages *[]ClangMessage) { | ||||||
|  | 	currentDir, _ := os.Getwd() | ||||||
|  | 	for i := range *messages { | ||||||
|  | 		(*messages)[i].filepath, _ = filepath.Rel(currentDir, (*messages)[i].filepath) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func parse_lines(lines []string) []ClangMessage { | ||||||
|  | 	messages := make([]ClangMessage, 0) | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		if is_ignored(string(line)) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		message := parse_message(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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	convert_paths_to_relative(&messages) | ||||||
|  | 	return group_messages(messages) | ||||||
|  | } | ||||||
							
								
								
									
										201
									
								
								internal/parsers/clang_tidy/formatter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								internal/parsers/clang_tidy/formatter.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,201 @@ | ||||||
|  | // Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py
 | ||||||
|  | package clang_tidy | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type json_message struct { | ||||||
|  | 	Type        string                 `json:"type"` | ||||||
|  | 	Check_name  string                 `json:"check_name"` | ||||||
|  | 	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"` | ||||||
|  | 	Fingerprint string                 `json:"fingerprint"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func format(messages []ClangMessage) []json_message { | ||||||
|  | 	formatted_messages := make([]json_message, len(messages)) | ||||||
|  | 	for i, message := range messages { | ||||||
|  | 		formatted_messages[i] = format_message(message) | ||||||
|  | 	} | ||||||
|  | 	return formatted_messages | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func format_message(message ClangMessage) json_message { | ||||||
|  | 	result := json_message{ | ||||||
|  | 		Type:        "issue", | ||||||
|  | 		Check_name:  message.diagnosticName, | ||||||
|  | 		Description: message.message, | ||||||
|  | 		Content:     extract_content(message), | ||||||
|  | 		Categories:  extract_categories(message), | ||||||
|  | 		Location:    extract_location(message), | ||||||
|  | 		Trace:       extract_trace(message), | ||||||
|  | 		Severity:    extract_severity(message), | ||||||
|  | 		Fingerprint: "", | ||||||
|  | 		// Fingerprint: generate_fingerprint(message),
 | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func messages_to_text(messages []ClangMessage) []string { | ||||||
|  | 	text_lines := []string{} | ||||||
|  | 	for _, message := range messages { | ||||||
|  | 		text_lines = append(text_lines, fmt.Sprintf("%s:%d:%d: %s", message.filepath, message.line, message.column, message.message)) | ||||||
|  | 		text_lines = append(text_lines, message.detailsLines...) | ||||||
|  | 		text_lines = append(text_lines, messages_to_text(message.children)...) | ||||||
|  | 	} | ||||||
|  | 	return text_lines | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extract_content(message ClangMessage) map[string]interface{} { | ||||||
|  | 	detailLines := "" | ||||||
|  | 	for _, line := range message.detailsLines { | ||||||
|  | 		if line == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		detailLines += (line + "\n") | ||||||
|  | 	} | ||||||
|  | 	for _, line := range messages_to_text(message.children) { | ||||||
|  | 		if line == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		detailLines += (line + "\n") | ||||||
|  | 	} | ||||||
|  | 	result := map[string]interface{}{ | ||||||
|  | 		"body": "```\n" + detailLines + "```", | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func remove_duplicates(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 extract_categories(message ClangMessage) []string { | ||||||
|  | 	BUGRISC_CATEGORY := "Bug Risk" | ||||||
|  | 	CLARITY_CATEGORY := "Clarity" | ||||||
|  | 	COMPATIBILITY_CATEGORY := "Compatibility" | ||||||
|  | 	COMPLEXITY_CATEGORY := "Complexity" | ||||||
|  | 	DUPLICATION_CATEGORY := "Duplication" | ||||||
|  | 	PERFORMANCE_CATEGORY := "Performance" | ||||||
|  | 	SECURITY_CATEGORY := "Security" | ||||||
|  | 	STYLE_CATEGORY := "Style" | ||||||
|  | 
 | ||||||
|  | 	categories := []string{} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "bugprone") { | ||||||
|  | 		categories = append(categories, BUGRISC_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "modernize") { | ||||||
|  | 		categories = append(categories, COMPATIBILITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "portability") { | ||||||
|  | 		categories = append(categories, COMPATIBILITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "performance") { | ||||||
|  | 		categories = append(categories, PERFORMANCE_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "readability") { | ||||||
|  | 		categories = append(categories, CLARITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "cloexec") { | ||||||
|  | 		categories = append(categories, SECURITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "security") { | ||||||
|  | 		categories = append(categories, SECURITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "naming") { | ||||||
|  | 		categories = append(categories, STYLE_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "misc") { | ||||||
|  | 		categories = append(categories, STYLE_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "cppcoreguidelines") { | ||||||
|  | 		categories = append(categories, STYLE_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "hicpp") { | ||||||
|  | 		categories = append(categories, STYLE_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "simplify") { | ||||||
|  | 		categories = append(categories, COMPLEXITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.Contains(message.diagnosticName, "redundant") { | ||||||
|  | 		categories = append(categories, DUPLICATION_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if strings.HasPrefix(message.diagnosticName, "boost-use-to-string") { | ||||||
|  | 		categories = append(categories, COMPATIBILITY_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	if len(categories) == 0 { | ||||||
|  | 		categories = append(categories, BUGRISC_CATEGORY) | ||||||
|  | 	} | ||||||
|  | 	return remove_duplicates(categories) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extract_location(message ClangMessage) map[string]interface{} { | ||||||
|  | 	location := map[string]interface{}{ | ||||||
|  | 		"path": message.filepath, | ||||||
|  | 		"lines": map[string]interface{}{ | ||||||
|  | 			"begin": message.line, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return location | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extract_other_locations(message ClangMessage) []map[string]interface{} { | ||||||
|  | 	location_list := []map[string]interface{}{} | ||||||
|  | 	for _, child := range message.children { | ||||||
|  | 		location_list = append(location_list, extract_location(child)) | ||||||
|  | 		location_list = append(location_list, extract_other_locations(child)...) | ||||||
|  | 	} | ||||||
|  | 	return location_list | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extract_trace(message ClangMessage) map[string]interface{} { | ||||||
|  | 	result := map[string]interface{}{ | ||||||
|  | 		"locations": extract_other_locations(message), | ||||||
|  | 	} | ||||||
|  | 	return result | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func extract_severity(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" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // func generate_fingerprint(message ClangMessage) string {
 | ||||||
|  | // 	h := md5.New()
 | ||||||
|  | // 	h.Write([]byte(message.filepath))
 | ||||||
|  | // 	h.Write([]byte(fmt.Sprintf("%d", message.line)))
 | ||||||
|  | // 	h.Write([]byte(fmt.Sprintf("%d", message.column)))
 | ||||||
|  | // 	h.Write([]byte(message.message))
 | ||||||
|  | // 	h.Write([]byte(message.diagnosticName))
 | ||||||
|  | // 	for _, child := range message.children {
 | ||||||
|  | // 		childFingerprint := generate_fingerprint(child)
 | ||||||
|  | // 		h.Write([]byte(childFingerprint))
 | ||||||
|  | // 	}
 | ||||||
|  | // 	return hex.EncodeToString(h.Sum(nil))
 | ||||||
|  | // }
 | ||||||
|  | @ -3,15 +3,21 @@ package clang_tidy | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | ||||||
| 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/dummy" |  | ||||||
| 	"github.com/criyle/go-judge/envexec" | 	"github.com/criyle/go-judge/envexec" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type Match struct { | ||||||
|  | 	Keyword []string | ||||||
|  | 	Score   int | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Conf struct { | type Conf struct { | ||||||
| 	Score   int | 	Score   int | ||||||
| 	Comment string | 	Matches []Match | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ClangTidy struct{} | type ClangTidy struct{} | ||||||
|  | @ -19,6 +25,20 @@ type ClangTidy struct{} | ||||||
| func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult { | func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult { | ||||||
| 	stdout := executorResult.Files["stdout"] | 	stdout := executorResult.Files["stdout"] | ||||||
| 	stderr := executorResult.Files["stderr"] | 	stderr := executorResult.Files["stderr"] | ||||||
|  | 
 | ||||||
|  | 	lines := strings.SplitAfter(stdout, "\n") | ||||||
|  | 	messages := parse_lines(lines) | ||||||
|  | 	formatted_messages := format(messages) | ||||||
|  | 
 | ||||||
|  | 	// TODO: Handle the json file (parse into markdown and delete it?)
 | ||||||
|  | 	json_file, _ := os.Create("./clangtidy_result.json") | ||||||
|  | 	defer json_file.Close() | ||||||
|  | 
 | ||||||
|  | 	encoder := json.NewEncoder(json_file) | ||||||
|  | 	encoder.SetEscapeHTML(false) | ||||||
|  | 	encoder.SetIndent("", "  ") | ||||||
|  | 	_ = encoder.Encode(formatted_messages) | ||||||
|  | 
 | ||||||
| 	if executorResult.Status != stage.Status(envexec.StatusAccepted) { | 	if executorResult.Status != stage.Status(envexec.StatusAccepted) { | ||||||
| 		return stage.ParserResult{ | 		return stage.ParserResult{ | ||||||
| 			Score: 0, | 			Score: 0, | ||||||
|  | @ -28,17 +48,9 @@ func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult { | ||||||
| 			), | 			), | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	var dummyResult dummy.Result |  | ||||||
| 	err := json.Unmarshal([]byte(stdout), &dummyResult) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return stage.ParserResult{ |  | ||||||
| 			Score:   0, |  | ||||||
| 			Comment: fmt.Sprintf("Failed to parse result: %s", err), |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return stage.ParserResult{ | 	return stage.ParserResult{ | ||||||
| 		Score:   dummyResult.Score + conf.Score, | 		Score:   get_score(formatted_messages, conf), | ||||||
| 		Comment: dummyResult.Comment + conf.Comment, | 		Comment: "", | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								internal/parsers/clang_tidy/score.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								internal/parsers/clang_tidy/score.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | package clang_tidy | ||||||
|  | 
 | ||||||
|  | func Contains[T comparable](arr []T, element T) bool { | ||||||
|  | 	for i := range arr { | ||||||
|  | 		// TODO: The keyword in json report might also be an array, need to split it
 | ||||||
|  | 		// TODO: Might use string.Contains() rather than ==
 | ||||||
|  | 		if element == arr[i] { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func get_score(json_messages []json_message, conf Conf) int { | ||||||
|  | 	fullmark := conf.Score | ||||||
|  | 	for _, json_message := range json_messages { | ||||||
|  | 		keyword := json_message.Check_name | ||||||
|  | 		flag := false | ||||||
|  | 		for _, match := range conf.Matches { | ||||||
|  | 			if Contains(match.Keyword, keyword) { | ||||||
|  | 				fullmark -= match.Score | ||||||
|  | 				flag = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if !flag { | ||||||
|  | 			fullmark -= 1 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return fullmark | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user