package clangtidy import ( "fmt" "strings" ) // Referenced from https://github.com/yuriisk/clang-tidy-converter/blob/master/clang_tidy_converter/parser/clang_tidy_parser.py type JSONMessage struct { Type string `json:"type"` CheckName string `json:"checkname"` Description string `json:"description"` Content map[string]any `json:"content"` Categories []string `json:"categories"` Location map[string]any `json:"location"` Trace map[string]any `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]any { 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]any{ "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]any { location := map[string]any{ "path": message.filepath, "lines": map[string]any{ "begin": message.line, }, } return location } func extractOtherLocations(message ClangMessage) []map[string]any { locationList := []map[string]any{} for _, child := range message.children { locationList = append(locationList, extractLocation(child)) locationList = append(locationList, extractOtherLocations(child)...) } return locationList } func extractTrace(message ClangMessage) map[string]any { result := map[string]any{ "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" } }