Differential Revision: https://phabricator.services.mozilla.com/D278768
117 lines
3.8 KiB
Python
117 lines
3.8 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
import difflib
|
|
import os
|
|
import re
|
|
|
|
from mozlint import result
|
|
from mozlint.pathutils import expand_exclusions
|
|
|
|
comment_remover = re.compile(r"((?://[^\r\n]*)|(?:/\*.*?\*/)|\s)", re.DOTALL)
|
|
header_guard = re.compile(
|
|
r"^\s*#\s*(?:(?:ifndef\s(\w+))|(?:if\s+\!\s*defined\(\s*(\w+)\s*\))|(?:pragma\s+once)).*"
|
|
)
|
|
|
|
|
|
def check_missing_header_guards(results, topsrcdir, path, raw_content, config, fix):
|
|
offset = 0
|
|
comment_prelude = re.match(comment_remover, raw_content)
|
|
while comment_prelude:
|
|
offset += len(comment_prelude.group(1))
|
|
comment_prelude = re.match(comment_remover, raw_content[offset:])
|
|
stripped_content = raw_content[offset:]
|
|
lineno = raw_content.count("\n", 0, offset)
|
|
if m := re.match(header_guard, stripped_content):
|
|
groups = m.groups()
|
|
if any(groups):
|
|
existing_guard = groups[0] or groups[1]
|
|
if "__" in existing_guard:
|
|
results["results"].append(
|
|
result.from_config(
|
|
config,
|
|
path=path,
|
|
message=f"invalid header guard {existing_guard}, using '__' in a macro name is reserved",
|
|
level="error",
|
|
line=lineno,
|
|
)
|
|
)
|
|
if re.match("^_[A-Z]", existing_guard):
|
|
results["results"].append(
|
|
result.from_config(
|
|
config,
|
|
path=path,
|
|
message=f"invalid header guard {existing_guard}, leading underscore followed by a capital letter in a macro name is reserved",
|
|
level="error",
|
|
line=lineno,
|
|
)
|
|
)
|
|
return
|
|
guard = make_guard(topsrcdir, path)
|
|
if fix:
|
|
fix_guard(guard, path, raw_content, lineno)
|
|
results["fixed"] += 1
|
|
else:
|
|
diff = generate_diff(guard, path, raw_content, lineno)
|
|
results["results"].append(
|
|
result.from_config(
|
|
config,
|
|
path=path,
|
|
message="missing header guard",
|
|
level="error",
|
|
diff=diff,
|
|
)
|
|
)
|
|
|
|
|
|
def make_guard(topsrcdir, path):
|
|
guard = f"{os.path.splitext(path[1 + len(topsrcdir) :])[0]}_H_"
|
|
guard = re.sub(r"[/.-]", "_", guard)
|
|
guard = re.sub("_+", "_", guard)
|
|
guard = guard.upper()
|
|
return guard
|
|
|
|
|
|
def insert_guard(guard, sequence, lineno):
|
|
new_sequence = sequence + [f"#endif // {guard}"]
|
|
new_sequence.insert(lineno, f"#ifndef {guard}")
|
|
new_sequence.insert(lineno + 1, f"#define {guard}")
|
|
new_sequence.insert(lineno + 2, "")
|
|
return new_sequence
|
|
|
|
|
|
def fix_guard(guard, path, raw_content, lineno):
|
|
prev_content = raw_content.split("\n")
|
|
new_content = insert_guard(guard, prev_content, lineno)
|
|
with open(path, "w") as fd:
|
|
fd.write("\n".join(new_content))
|
|
|
|
|
|
def generate_diff(guard, path, raw_content, lineno):
|
|
prev_content = raw_content.split("\n")
|
|
new_content = insert_guard(guard, prev_content, lineno)
|
|
diff = "\n".join(
|
|
difflib.unified_diff(prev_content, new_content, fromfile=path, tofile=path)
|
|
)
|
|
return diff
|
|
|
|
|
|
def lint(paths, config, **lintargs):
|
|
results = {"results": [], "fixed": 0}
|
|
paths = list(expand_exclusions(paths, config, lintargs["root"]))
|
|
fix = lintargs.get("fix")
|
|
|
|
for path in paths:
|
|
try:
|
|
with open(path) as fd:
|
|
raw_content = fd.read()
|
|
except UnicodeDecodeError:
|
|
continue
|
|
|
|
check_missing_header_guards(
|
|
results, lintargs["root"], path, raw_content, config, fix
|
|
)
|
|
|
|
return results
|