clang-tidy parser and executor #26
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
Subproject commit a16c54c3ac3254437b8d523f1a3fa5bd3067a81a
 | 
					Subproject commit a69a7e87fddaddf21fc4f9cd6774e310fa7137c1
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package parsers
 | 
					package parsers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/clang_tidy"
 | 
						_ "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"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,184 +0,0 @@
 | 
				
			||||||
// 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"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	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"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
// Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py
 | 
					// Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py
 | 
				
			||||||
package clang_tidy
 | 
					package clangtidy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ func newClangMessage(filepath string, line int, column int, level Level, message
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func LevelFromString(levelString string) Level {
 | 
					func levelFromString(levelString string) Level {
 | 
				
			||||||
	switch levelString {
 | 
						switch levelString {
 | 
				
			||||||
	case "note":
 | 
						case "note":
 | 
				
			||||||
		return NOTE
 | 
							return NOTE
 | 
				
			||||||
| 
						 | 
					@ -66,12 +66,12 @@ func LevelFromString(levelString string) Level {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func is_ignored(line string) bool {
 | 
					func isIgnored(line string) bool {
 | 
				
			||||||
	IGNORE_REGEX := regexp.MustCompile("^error:.*$")
 | 
						IGNORE_REGEX := regexp.MustCompile("^error:.*$")
 | 
				
			||||||
	return IGNORE_REGEX.MatchString(line)
 | 
						return IGNORE_REGEX.MatchString(line)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parse_message(line string) ClangMessage {
 | 
					func parseMessage(line string) ClangMessage {
 | 
				
			||||||
	MESSAGE_REGEX := regexp.MustCompile(`^(?P<filepath>.+):(?P<line>\d+):(?P<column>\d+): (?P<level>\S+): (?P<message>.*?)(?: \[(?P<diagnostic_name>.*)\])?\n$`)
 | 
						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)
 | 
						regex_res := MESSAGE_REGEX.FindStringSubmatch(line)
 | 
				
			||||||
	if len(regex_res) == 0 {
 | 
						if len(regex_res) == 0 {
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ func parse_message(line string) ClangMessage {
 | 
				
			||||||
		filepath := regex_res[1]
 | 
							filepath := regex_res[1]
 | 
				
			||||||
		line, _ := strconv.Atoi(regex_res[2])
 | 
							line, _ := strconv.Atoi(regex_res[2])
 | 
				
			||||||
		column, _ := strconv.Atoi(regex_res[3])
 | 
							column, _ := strconv.Atoi(regex_res[3])
 | 
				
			||||||
		level := LevelFromString(regex_res[4])
 | 
							level := levelFromString(regex_res[4])
 | 
				
			||||||
		message := regex_res[5]
 | 
							message := regex_res[5]
 | 
				
			||||||
		diagnostic_name := regex_res[6]
 | 
							diagnostic_name := regex_res[6]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,7 @@ func parse_message(line string) ClangMessage {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func group_messages(messages []ClangMessage) []ClangMessage {
 | 
					func groupMessages(messages []ClangMessage) []ClangMessage {
 | 
				
			||||||
	grouped_messages := make([]ClangMessage, 0)
 | 
						grouped_messages := make([]ClangMessage, 0)
 | 
				
			||||||
	for _, message := range messages {
 | 
						for _, message := range messages {
 | 
				
			||||||
		if message.level == NOTE {
 | 
							if message.level == NOTE {
 | 
				
			||||||
| 
						 | 
					@ -109,26 +109,26 @@ func group_messages(messages []ClangMessage) []ClangMessage {
 | 
				
			||||||
	return grouped_messages
 | 
						return grouped_messages
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func convert_paths_to_relative(messages *[]ClangMessage, conf Conf) {
 | 
					func convertPathsToRelative(messages *[]ClangMessage, conf Conf) {
 | 
				
			||||||
	currentDir := conf.RootDir
 | 
						currentDir := conf.RootDir
 | 
				
			||||||
	for i := range *messages {
 | 
						for i := range *messages {
 | 
				
			||||||
		(*messages)[i].filepath, _ = filepath.Rel(currentDir, (*messages)[i].filepath)
 | 
							(*messages)[i].filepath, _ = filepath.Rel(currentDir, (*messages)[i].filepath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parse_lines(lines []string, conf Conf) []ClangMessage {
 | 
					func ParseLines(lines []string, conf Conf) []ClangMessage {
 | 
				
			||||||
	messages := make([]ClangMessage, 0)
 | 
						messages := make([]ClangMessage, 0)
 | 
				
			||||||
	for _, line := range lines {
 | 
						for _, line := range lines {
 | 
				
			||||||
		if is_ignored(string(line)) {
 | 
							if isIgnored(string(line)) {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		message := parse_message(string(line))
 | 
							message := parseMessage(string(line))
 | 
				
			||||||
		if message.level == UNKNOWN && len(messages) > 0 {
 | 
							if message.level == UNKNOWN && len(messages) > 0 {
 | 
				
			||||||
			messages[len(messages)-1].detailsLines = append(messages[len(messages)-1].detailsLines, string(line))
 | 
								messages[len(messages)-1].detailsLines = append(messages[len(messages)-1].detailsLines, string(line))
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			messages = append(messages, message)
 | 
								messages = append(messages, message)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	convert_paths_to_relative(&messages, conf)
 | 
						convertPathsToRelative(&messages, conf)
 | 
				
			||||||
	return group_messages(messages)
 | 
						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:"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"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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{} {
 | 
				
			||||||
 | 
						location_list := []map[string]interface{}{}
 | 
				
			||||||
 | 
						for _, child := range message.children {
 | 
				
			||||||
 | 
							location_list = append(location_list, extractLocation(child))
 | 
				
			||||||
 | 
							location_list = append(location_list, extractOtherLocations(child)...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return location_list
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
package clang_tidy
 | 
					package clangtidy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 | 
					import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var name = "clang-tidy"
 | 
					var name = "clangtidy"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	stage.RegisterParser(name, &ClangTidy{})
 | 
						stage.RegisterParser(name, &ClangTidy{})
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package clang_tidy
 | 
					package clangtidy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
| 
						 | 
					@ -26,8 +26,8 @@ func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult {
 | 
				
			||||||
	stderr := executorResult.Files["stderr"]
 | 
						stderr := executorResult.Files["stderr"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lines := strings.SplitAfter(stdout, "\n")
 | 
						lines := strings.SplitAfter(stdout, "\n")
 | 
				
			||||||
	messages := parse_lines(lines, conf)
 | 
						messages := ParseLines(lines, conf)
 | 
				
			||||||
	formatted_messages := format(messages)
 | 
						formattedMessages := Format(messages)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if executorResult.Status != stage.Status(envexec.StatusAccepted) {
 | 
						if executorResult.Status != stage.Status(envexec.StatusAccepted) {
 | 
				
			||||||
		if !((executorResult.Status == stage.Status(envexec.StatusNonzeroExitStatus)) &&
 | 
							if !((executorResult.Status == stage.Status(envexec.StatusNonzeroExitStatus)) &&
 | 
				
			||||||
| 
						 | 
					@ -42,8 +42,8 @@ func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return stage.ParserResult{
 | 
						return stage.ParserResult{
 | 
				
			||||||
		Score:   get_score(formatted_messages, conf),
 | 
							Score:   GetScore(formattedMessages, conf),
 | 
				
			||||||
		Comment: get_comment(formatted_messages),
 | 
							Comment: GetComment(formattedMessages),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
package clang_tidy
 | 
					package clangtidy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Contains(arr []string, element string) bool {
 | 
					func contains(arr []string, element string) bool {
 | 
				
			||||||
	for i := range arr {
 | 
						for i := range arr {
 | 
				
			||||||
		// TODO: The keyword in json report might also be an array, need to split it
 | 
							// TODO: The keyword in json report might also be an array, need to split it
 | 
				
			||||||
		if strings.Contains(arr[i], element) {
 | 
							if strings.Contains(arr[i], element) {
 | 
				
			||||||
| 
						 | 
					@ -15,12 +15,12 @@ func Contains(arr []string, element string) bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func get_score(json_messages []json_message, conf Conf) int {
 | 
					func GetScore(json_messages []JsonMessage, conf Conf) int {
 | 
				
			||||||
	fullmark := conf.Score
 | 
						fullmark := conf.Score
 | 
				
			||||||
	for _, json_message := range json_messages {
 | 
						for _, json_message := range json_messages {
 | 
				
			||||||
		keyword := json_message.Check_name
 | 
							keyword := json_message.CheckName
 | 
				
			||||||
		for _, match := range conf.Matches {
 | 
							for _, match := range conf.Matches {
 | 
				
			||||||
			if Contains(match.Keyword, keyword) {
 | 
								if contains(match.Keyword, keyword) {
 | 
				
			||||||
				fullmark -= match.Score
 | 
									fullmark -= match.Score
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ func get_score(json_messages []json_message, conf Conf) int {
 | 
				
			||||||
	return fullmark
 | 
						return fullmark
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func get_comment(json_messages []json_message) string {
 | 
					func GetComment(jsonMessages []JsonMessage) string {
 | 
				
			||||||
	res := "### Test results summary\n\n"
 | 
						res := "### Test results summary\n\n"
 | 
				
			||||||
	keys := [...]string{
 | 
						keys := [...]string{
 | 
				
			||||||
		"codequality-unchecked-malloc-result",
 | 
							"codequality-unchecked-malloc-result",
 | 
				
			||||||
| 
						 | 
					@ -55,8 +55,8 @@ func get_comment(json_messages []json_message) string {
 | 
				
			||||||
	for _, key := range keys {
 | 
						for _, key := range keys {
 | 
				
			||||||
		mapping[key] = 0
 | 
							mapping[key] = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, json_message := range json_messages {
 | 
						for _, jsonMessage := range jsonMessages {
 | 
				
			||||||
		keyword := json_message.Check_name
 | 
							keyword := jsonMessage.CheckName
 | 
				
			||||||
		flag := true
 | 
							flag := true
 | 
				
			||||||
		for key := range mapping {
 | 
							for key := range mapping {
 | 
				
			||||||
			if strings.Contains(keyword, key) {
 | 
								if strings.Contains(keyword, key) {
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user