| # Common utils for the several housekeeping scripts. |
| |
| import os |
| import re |
| import collections |
| |
| import logging as log |
| log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s') |
| |
| Lint = collections.namedtuple('Lint', 'name level doc sourcefile group') |
| Config = collections.namedtuple('Config', 'name ty doc default') |
| |
| lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''') |
| group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''') |
| conf_re = re.compile(r'''define_Conf! {\n((?!\n})[\s\S])*\n}''', re.MULTILINE) |
| confvar_re = re.compile( |
| r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\(([^:]+):\s*([^\s=]+)\s*=\s*([^\.\)]+).*\),''', re.MULTILINE) |
| comment_re = re.compile(r'''\s*/// ?(.*)''') |
| |
| lint_levels = { |
| "correctness": 'Deny', |
| "suspicious": 'Warn', |
| "style": 'Warn', |
| "complexity": 'Warn', |
| "perf": 'Warn', |
| "restriction": 'Allow', |
| "pedantic": 'Allow', |
| "nursery": 'Allow', |
| "cargo": 'Allow', |
| } |
| |
| |
| def parse_lints(lints, filepath): |
| comment = [] |
| clippy = False |
| deprecated = False |
| name = "" |
| |
| with open(filepath) as fp: |
| for line in fp: |
| if clippy or deprecated: |
| m = lintname_re.search(line) |
| if m: |
| name = m.group(1).lower() |
| line = next(fp) |
| |
| if deprecated: |
| level = "Deprecated" |
| group = "deprecated" |
| else: |
| while True: |
| g = group_re.search(line) |
| if g: |
| group = g.group(1).lower() |
| level = lint_levels.get(group, None) |
| break |
| line = next(fp) |
| |
| if level is None: |
| continue |
| |
| log.info("found %s with level %s in %s", |
| name, level, filepath) |
| lints.append(Lint(name, level, comment, filepath, group)) |
| comment = [] |
| |
| clippy = False |
| deprecated = False |
| name = "" |
| else: |
| m = comment_re.search(line) |
| if m: |
| comment.append(m.group(1)) |
| elif line.startswith("declare_clippy_lint!"): |
| clippy = True |
| deprecated = False |
| elif line.startswith("declare_deprecated_lint!"): |
| clippy = False |
| deprecated = True |
| elif line.startswith("declare_lint!"): |
| import sys |
| print( |
| "don't use `declare_lint!` in Clippy, " |
| "use `declare_clippy_lint!` instead" |
| ) |
| sys.exit(42) |
| |
| |
| def parse_configs(path): |
| configs = {} |
| with open(os.path.join(path, 'utils/conf.rs')) as fp: |
| contents = fp.read() |
| |
| match = re.search(conf_re, contents) |
| confvars = re.findall(confvar_re, match.group(0)) |
| |
| for (lints, doc, name, ty, default) in confvars: |
| for lint in lints.split(','): |
| configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default) |
| return configs |
| |
| |
| def parse_all(path="clippy_lints/src"): |
| lints = [] |
| for root, dirs, files in os.walk(path): |
| for fn in files: |
| if fn.endswith('.rs'): |
| parse_lints(lints, os.path.join(root, fn)) |
| |
| log.info("got %s lints", len(lints)) |
| |
| configs = parse_configs(path) |
| log.info("got %d configs", len(configs)) |
| |
| return lints, configs |