forked from Floorp-Projects/Floorp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1466070 - Integrate clang-format into mozlint r=linter-reviewers,…
…ahal For now, the local detection sucks. I will fix that once bug 1625884 is fixed Differential Revision: https://phabricator.services.mozilla.com/D69683
- Loading branch information
Showing
11 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
clang-format | ||
============ | ||
|
||
`clang-format <https://clang.llvm.org/docs/ClangFormat.html>`__ is a tool to reformat C/C++ to the right coding style. | ||
|
||
Run Locally | ||
----------- | ||
|
||
The mozlint integration of clang-format can be run using mach: | ||
|
||
.. parsed-literal:: | ||
$ mach lint --linter clang-format <file paths> | ||
Configuration | ||
------------- | ||
|
||
To enable clang-format on new directory, add the path to the include | ||
section in the `clang-format.yml <https://searchfox.org/mozilla-central/source/tools/lint/clang-format.yml>`_ file. | ||
|
||
While excludes: will work, this linter will read the ignore list from `.clang-format-ignore file <https://searchfox.org/mozilla-central/source/.clang-format-ignore>`_ | ||
at the root directory. This because it is also used by the ./mach clang-format -p command. | ||
|
||
Autofix | ||
------- | ||
|
||
clang-format can reformat the code with the option `--fix` (based on the upstream option `-i`). | ||
To highlight the results, we are using the ``--dry-run`` option (from clang-format 10). | ||
|
||
Sources | ||
------- | ||
|
||
* `Configuration (YAML) <https://searchfox.org/mozilla-central/source/tools/lint/clang-format.yml>`_ | ||
* `Source <https://searchfox.org/mozilla-central/source/tools/lint/clang-format/__init__.py>`_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
clang-format: | ||
description: Reformat C/C++ | ||
include: | ||
- '.' | ||
extensions: ['cpp', 'c', 'cc', 'h', 'm', 'mm'] | ||
support-files: | ||
- 'tools/lint/clang-format/**' | ||
type: external | ||
payload: clang-format:lint | ||
code_review_warnings: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
# 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 os | ||
import signal | ||
import re | ||
|
||
from buildconfig import substs | ||
from mozboot.util import get_state_dir | ||
from mozlint import result | ||
from mozlint.pathutils import expand_exclusions | ||
from mozprocess import ProcessHandler | ||
|
||
CLANG_FORMAT_NOT_FOUND = """ | ||
Could not find clang-format! Install clang-format with: | ||
$ ./mach bootstrap | ||
And make sure that it is in the PATH | ||
""".strip() | ||
|
||
|
||
def parse_issues(config, output, paths, log): | ||
|
||
diff_line = re.compile("^(.*):(.*):(.*): warning: .*;(.*);(.*)") | ||
results = [] | ||
for line in output: | ||
match = diff_line.match(line) | ||
file, line_no, col, diff, diff2 = match.groups() | ||
log.debug("file={} line={} col={} diff={} diff2={}".format( | ||
file, line_no, col, diff, diff2)) | ||
d = diff + "\n" + diff2 | ||
res = { | ||
"path": file, | ||
"diff": d, | ||
"level": "warning", | ||
"lineno": line_no, | ||
"column": col, | ||
} | ||
results.append(result.from_config(config, **res)) | ||
|
||
return results | ||
|
||
|
||
class ClangFormatProcess(ProcessHandler): | ||
def __init__(self, config, *args, **kwargs): | ||
self.config = config | ||
kwargs["stream"] = False | ||
kwargs["universal_newlines"] = True | ||
ProcessHandler.__init__(self, *args, **kwargs) | ||
|
||
def run(self, *args, **kwargs): | ||
orig = signal.signal(signal.SIGINT, signal.SIG_IGN) | ||
ProcessHandler.run(self, *args, **kwargs) | ||
signal.signal(signal.SIGINT, orig) | ||
|
||
|
||
def run_process(config, cmd): | ||
proc = ClangFormatProcess(config, cmd) | ||
proc.run() | ||
try: | ||
proc.wait() | ||
except KeyboardInterrupt: | ||
proc.kill() | ||
|
||
return proc.output | ||
|
||
|
||
def get_clang_format_binary(): | ||
""" | ||
Returns the path of the first clang-format binary available | ||
if not found returns None | ||
""" | ||
binary = os.environ.get("CLANG_FORMAT") | ||
if binary: | ||
return binary | ||
|
||
clang_tools_path = os.path.join(get_state_dir(), "clang-tools") | ||
bin_path = os.path.join(clang_tools_path, "clang-tidy", "bin") | ||
return os.path.join(bin_path, "clang-format" + substs.get('BIN_SUFFIX', '')) | ||
|
||
|
||
def is_ignored_path(ignored_dir_re, topsrcdir, f): | ||
# Remove up to topsrcdir in pathname and match | ||
if f.startswith(topsrcdir + '/'): | ||
match_f = f[len(topsrcdir + '/'):] | ||
else: | ||
match_f = f | ||
return re.match(ignored_dir_re, match_f) | ||
|
||
|
||
def remove_ignored_path(paths, topsrcdir, log): | ||
path_to_third_party = os.path.join(topsrcdir, '.clang-format-ignore') | ||
|
||
ignored_dir = [] | ||
with open(path_to_third_party, 'r') as fh: | ||
for line in fh: | ||
# In case it starts with a space | ||
line = line.strip() | ||
# Remove comments and empty lines | ||
if line.startswith('#') or len(line) == 0: | ||
continue | ||
# The regexp is to make sure we are managing relative paths | ||
ignored_dir.append(r"^[\./]*" + line.rstrip()) | ||
|
||
# Generates the list of regexp | ||
ignored_dir_re = '(%s)' % '|'.join(ignored_dir) | ||
|
||
path_list = [] | ||
for f in paths: | ||
if is_ignored_path(ignored_dir_re, topsrcdir, f): | ||
# Early exit if we have provided an ignored directory | ||
log.debug("Ignored third party code '{0}'".format(f)) | ||
continue | ||
path_list.append(f) | ||
|
||
return path_list | ||
|
||
|
||
def lint(paths, config, fix=None, **lintargs): | ||
log = lintargs['log'] | ||
paths = list(expand_exclusions(paths, config, lintargs['root'])) | ||
|
||
# We ignored some specific files for a bunch of reasons. | ||
# Not using excluding to avoid duplication | ||
if lintargs.get('use_filters', True): | ||
paths = remove_ignored_path(paths, lintargs['root'], log) | ||
|
||
# An empty path array can occur when the user passes in `-n`. If we don't | ||
# return early in this case, rustfmt will attempt to read stdin and hang. | ||
if not paths: | ||
return [] | ||
|
||
binary = get_clang_format_binary() | ||
|
||
if not binary: | ||
print(CLANG_FORMAT_NOT_FOUND) | ||
if "MOZ_AUTOMATION" in os.environ: | ||
return 1 | ||
return [] | ||
|
||
cmd_args = [binary] | ||
if fix: | ||
cmd_args.append("-i") | ||
else: | ||
cmd_args.append("--dry-run") | ||
base_command = cmd_args + paths | ||
log.debug("Command: {}".format(' '.join(cmd_args))) | ||
output = run_process(config, base_command) | ||
output_list = [] | ||
|
||
if len(output) % 3 != 0: | ||
raise Exception("clang-format output should be a multiple of 3. Output: %s" % output) | ||
|
||
for i in range(0, len(output), 3): | ||
# Merge the element 3 by 3 (clang-format output) | ||
line = output[i] | ||
line += ";" + output[i+1] | ||
line += ";" + output[i+2] | ||
output_list.append(line) | ||
|
||
if fix: | ||
# clang-format is able to fix all issues so don't bother parsing the output. | ||
return [] | ||
return parse_issues(config, output_list, paths, log) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
int main ( ) { | ||
|
||
return 0; | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include "bad2.h" | ||
|
||
|
||
int bad2() { | ||
int a =2; | ||
return a; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
int bad2(void ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
int main() { return 0; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
int main() { return 0; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import mozunit | ||
|
||
from conftest import build | ||
|
||
LINTER = 'clang-format' | ||
|
||
|
||
def test_good(lint, config, paths): | ||
results = lint(paths("good/"), root=build.topsrcdir, use_filters=False) | ||
print(results) | ||
assert len(results) == 0 | ||
|
||
|
||
def test_basic(lint, config, paths): | ||
results = lint(paths("bad/bad.cpp"), root=build.topsrcdir, use_filters=False) | ||
print(results) | ||
assert len(results) >= 1 | ||
|
||
assert "Reformat C/C++" in results[0].message | ||
assert results[0].level == "warning" | ||
assert results[0].lineno == 1 | ||
assert results[0].column == 4 | ||
assert "bad.cpp" in results[0].path | ||
assert 'int main ( ) {' in results[0].diff | ||
|
||
|
||
def test_dir(lint, config, paths): | ||
results = lint(paths("bad/"), root=build.topsrcdir, use_filters=False) | ||
print(results) | ||
assert len(results) >= 4 | ||
|
||
assert "Reformat C/C++" in results[0].message | ||
assert results[0].level == "warning" | ||
assert results[0].lineno == 1 | ||
assert results[0].column == 4 | ||
assert "bad.cpp" in results[0].path | ||
assert 'int main ( ) {' in results[0].diff | ||
|
||
assert "Reformat C/C++" in results[5].message | ||
assert results[5].level == "warning" | ||
assert results[5].lineno == 1 | ||
assert results[5].column == 18 | ||
assert "bad2.c" in results[5].path | ||
assert "#include" in results[5].diff | ||
|
||
|
||
if __name__ == '__main__': | ||
mozunit.main() |