Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
parse_clang_tidy.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file parse_clang_tidy.py
1 #!/usr/bin/env python3
2 
3 """
4 This produces a structured normalized report json file based on warnings generated by another tool.
5 Currently implemented is clang-tidy warnings.
6 """
7 
8 import argparse
9 import re
10 from collections import namedtuple
11 from itertools import groupby
12 import os
13 import html
14 from fnmatch import fnmatch
15 import json
16 import sys
17 from dataclasses import dataclass
18 from pathlib import Path
19 
20 from item import Item, ItemCollection
21 
22 
23 def parse_clang_tidy_item(itemstr):
24 
25  try:
26  m = re.match(
27  r"(?P<file>[/.\-+\w]+):(?P<line>\d+):(?P<col>\d+): (?P<sev>.*?):(?P<msg>[\s\S]*)\[(?P<code>.*)\]\n(?P<info>[\s\S]*)",
28  itemstr,
29  )
30 
31  lines = itemstr.split("\n")
32 
33  item = Item(
34  path=Path(m.group("file")),
35  line=int(m.group("line")),
36  col=int(m.group("col")),
37  # message=m.group("msg").strip(),
38  message=m.group("msg").strip() + "\n" + "\n".join(lines[1:]),
39  code=m.group("code"),
40  severity=m.group("sev"),
41  )
42 
43  # print(repr(item))
44 
45  return item
46  except:
47  print("Failed parsing clang-tidy item:")
48  print("-" * 20)
49  print(itemstr)
50  print("-" * 20)
51  raise
52 
53 
55 
56  # cleanup
57  itemstr = output
58  itemstr = re.sub(r"Enabled checks:\n[\S\s]+?\n\n", "", itemstr)
59  itemstr = re.sub(r"clang-tidy-\d\.\d.*\n?", "", itemstr)
60  itemstr = re.sub(r"clang-apply-.*", "", itemstr)
61  itemstr = re.sub(r".*-header-filter.*", "", itemstr)
62 
63  items = []
64  prevstart = 0
65 
66  matches = list(
67  re.finditer(r"([\w/.\-+]+):(\d+):(\d+): (?:(?:warning)|(?:error)):", itemstr)
68  )
69  for idx, m in enumerate(matches):
70  # print(m)
71  start, end = m.span()
72  if idx > 0:
73  item = itemstr[prevstart:start]
74  items.append(item)
75  if idx + 1 == len(matches):
76  item = itemstr[start:]
77  items.append(item)
78  prevstart = start
79 
80  items = set(map(parse_clang_tidy_item, sorted(items)))
81 
82  return items
83 
84 
85 def main():
86  p = argparse.ArgumentParser(description=__doc__)
87  p.add_argument("inputfile", help="The input file containing the warnings")
88  p.add_argument(
89  "output", default="codereport_clang_tidy.json", help="The resulting JSON file"
90  )
91  p.add_argument(
92  "--exclude",
93  "-e",
94  action="append",
95  default=[],
96  help="Exclude files that match any of these patterns",
97  )
98  p.add_argument(
99  "--filter",
100  action="append",
101  default=[],
102  help="Only include files that match any of these patterns",
103  )
104  p.add_argument(
105  "--ignore",
106  action="append",
107  default=[],
108  help="Ignore items with codes matching any of these patterns",
109  )
110  p.add_argument("--cwd", type=Path)
111  p.add_argument("--strip-common", action="store_true")
112 
113  args = p.parse_args()
114 
115  with open(args.inputfile, "r", encoding="utf-8") as f:
116  inputstr = f.read()
117  items = parse_clang_tidy_output(inputstr)
118 
119  def select(item):
120  accept = True
121  if len(args.filter) > 0:
122  accept = accept and all(fnmatch(item.path, e) for e in args.filter)
123 
124  accept = accept and not any(fnmatch(item.path, e) for e in args.exclude)
125 
126  accept = accept and not any(fnmatch(item.code, i) for i in args.ignore)
127 
128  return accept
129 
130  items = list(filter(select, items))
131 
132  if args.cwd:
133  for item in items:
134  item.path = (args.cwd / item.path).resolve()
135 
136  if args.strip_common:
137  prefix = Path(os.path.commonprefix([i.path for i in items]))
138 
139  def subpath(m):
140  path, line, col = m.groups()
141  path = Path(path).resolve().relative_to(prefix)
142  return f"{path}:{line}:{col}:"
143 
144  for item in items:
145  item.path = item.path.relative_to(prefix)
146 
147  item.message = re.sub(r"([\w/.\-+]+):(\d+):(\d+):", subpath, item.message)
148 
149  print("Write to", args.output)
150  with open(args.output, "w+") as jf:
151  jf.write(ItemCollection(__root__=items).json(indent=2))
152 
153 
154 if "__main__" == __name__:
155  main()