// 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.+):(?P\d+):(?P\d+): (?P\S+): (?P.*?)(?: \[(?P.*)\])?\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) }