// 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)) // }