# 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