aboutsummaryrefslogtreecommitdiffstats
path: root/python/tidy.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/tidy.py')
-rw-r--r--python/tidy.py150
1 files changed, 111 insertions, 39 deletions
diff --git a/python/tidy.py b/python/tidy.py
index 9ea9de24f30..95691893525 100644
--- a/python/tidy.py
+++ b/python/tidy.py
@@ -16,26 +16,18 @@ import StringIO
import sys
from licenseck import licenses
-filetypes_to_check = [".rs", ".rc", ".cpp", ".c", ".h", ".py", ".toml", ".webidl"]
+filetypes_to_check = [".rs", ".rc", ".cpp", ".c", ".h", ".lock", ".py", ".toml", ".webidl"]
reftest_dir = "./tests/ref"
reftest_filetype = ".list"
-python_dependencies = [
- "./python/dependencies/flake8-2.4.1-py2.py3-none-any.whl",
- "./python/dependencies/pep8-1.5.7-py2.py3-none-any.whl",
- "./python/dependencies/pyflakes-0.9.0-py2.py3-none-any.whl",
-]
ignored_files = [
# Upstream
"./support/*",
"./tests/wpt/*",
"./python/mach/*",
- "./python/mozdebug/*",
- "./python/mozinfo/*",
- "./python/mozlog/*",
- "./python/toml/*",
"./components/script/dom/bindings/codegen/parser/*",
"./components/script/dom/bindings/codegen/ply/*",
+ "./python/_virtualenv/*",
# Generated and upstream code combined with our own. Could use cleanup
"./target/*",
@@ -70,7 +62,7 @@ VIM_HEADER = "/* vim:"
def check_license(file_name, contents):
- if file_name.endswith(".toml"):
+ if file_name.endswith(".toml") or file_name.endswith(".lock"):
raise StopIteration
while contents.startswith(EMACS_HEADER) or contents.startswith(VIM_HEADER):
_, _, contents = contents.partition("\n")
@@ -148,6 +140,46 @@ def check_flake8(file_name, contents):
yield line_num, message.strip()
+def check_lock(file_name, contents):
+ if not file_name.endswith(".lock"):
+ raise StopIteration
+ contents = contents.splitlines(True)
+ idx = 1
+ packages = {}
+ exceptions = ["glutin", "wayland-kbd"] # package names to be neglected (as named by cargo)
+
+ while idx < len(contents):
+ content = contents[idx].strip()
+ if 'name' in content:
+ base_name = content.split('"')[1]
+ # we need the base package because some other package might demand a new version in the following lines
+ packages[base_name] = contents[idx + 1].split('"')[1], idx + 2, base_name
+ if 'dependencies' in content:
+ idx += 1
+ while contents[idx].strip() != ']':
+ package = contents[idx].strip().strip('",').split()
+ name, version = package[0], package[1]
+ if name not in packages: # store the line number & base package name for future comparison
+ packages[name] = (version, idx + 1, base_name)
+ elif all([packages[name][0] != version, name not in exceptions, base_name not in exceptions]):
+ line = idx + 1
+ version_1 = tuple(map(int, packages[name][0].split('.')))
+ version_2 = tuple(map(int, version.split('.')))
+ if version_1 < version_2: # get the line & base package containing the older version
+ packages[name], (version, line, base_name) = (version, line, base_name), packages[name]
+
+ message = 'conflicting versions for package "%s"' % name
+ error = '\n\t\033[93mexpected maximum version "{}"\033[0m'.format(packages[name][0]) + \
+ '\n\t\033[91mbut, "{}" demands "{}"\033[0m' \
+ .format(base_name, version)
+ suggest = "\n\t\033[93mtry upgrading with\033[0m " + \
+ "\033[96m./mach cargo-update -p {}:{}\033[0m" \
+ .format(name, version)
+ yield (line, message + error + suggest)
+ idx += 1
+ idx += 1
+
+
def check_toml(file_name, contents):
if not file_name.endswith(".toml"):
raise StopIteration
@@ -188,33 +220,35 @@ def check_rust(file_name, contents):
line = merged_lines + line
merged_lines = ''
- # get rid of strings and chars because cases like regex expression
- line = re.sub('".*?"|\'.*?\'', '', line)
+ # get rid of strings and chars because cases like regex expression, keep attributes
+ if not line_is_attribute(line):
+ line = re.sub('".*?"|\'.*?\'', '', line)
- # get rid of comments and attributes
- line = re.sub('//.*?$|/\*.*?$|^\*.*?$|^#.*?$', '', line)
+ # get rid of comments
+ line = re.sub('//.*?$|/\*.*?$|^\*.*?$', '', line)
- match = re.search(r",[A-Za-z0-9]", line)
- if match:
+ # get rid of attributes that do not contain =
+ line = re.sub('^#[A-Za-z0-9\(\)\[\]_]*?$', '', line)
+
+ match = re.search(r",[^\s]", line)
+ if match and '$' not in line:
yield (idx + 1, "missing space after ,")
- # Avoid flagging <Item=Foo> constructs
- def is_associated_type(match, line, index):
- open_angle = line[0:match.end()].rfind('<')
- close_angle = line[open_angle:].find('>') if open_angle != -1 else -1
- is_equals = match.group(0)[index] == '='
- generic_open = open_angle != -1 and open_angle < match.start()
- generic_close = close_angle != -1 and close_angle + open_angle >= match.end()
- return is_equals and generic_open and generic_close
-
- # - not included because of scientific notation (1e-6)
- match = re.search(r"[A-Za-z0-9][\+/\*%=]", line)
+ if line_is_attribute(line):
+ pre_space_re = r"[A-Za-z0-9]="
+ post_space_re = r"=[A-Za-z0-9\"]"
+ else:
+ # - not included because of scientific notation (1e-6)
+ pre_space_re = r"[A-Za-z0-9][\+/\*%=]"
+ # * not included because of dereferencing and casting
+ # - not included because of unary negation
+ post_space_re = r"[\+/\%=][A-Za-z0-9\"]"
+
+ match = re.search(pre_space_re, line)
if match and not is_associated_type(match, line, 1):
yield (idx + 1, "missing space before %s" % match.group(0)[1])
- # * not included because of dereferencing and casting
- # - not included because of unary negation
- match = re.search(r"[\+/\%=][A-Za-z0-9]", line)
+ match = re.search(post_space_re, line)
if match and not is_associated_type(match, line, 0):
yield (idx + 1, "missing space after %s" % match.group(0)[0])
@@ -243,6 +277,16 @@ def check_rust(file_name, contents):
if match:
yield (idx + 1, "missing space before {")
+ # ignored cases like {} and }}
+ match = re.search(r"[^\s{}]}", line)
+ if match and not (line.startswith("use") or line.startswith("pub use")):
+ yield (idx + 1, "missing space before }")
+
+ # ignored cases like {} and {{
+ match = re.search(r"{[^\s{}]", line)
+ if match and not (line.startswith("use") or line.startswith("pub use")):
+ yield (idx + 1, "missing space after {")
+
# imports must be in the same line and alphabetically sorted
if line.startswith("use "):
use = line[4:]
@@ -260,6 +304,20 @@ def check_rust(file_name, contents):
uses = []
+# Avoid flagging <Item=Foo> constructs
+def is_associated_type(match, line, index):
+ open_angle = line[0:match.end()].rfind('<')
+ close_angle = line[open_angle:].find('>') if open_angle != -1 else -1
+ is_equals = match.group(0)[index] == '='
+ generic_open = open_angle != -1 and open_angle < match.start()
+ generic_close = close_angle != -1 and close_angle + open_angle >= match.end()
+ return is_equals and generic_open and generic_close
+
+
+def line_is_attribute(line):
+ return re.search(r"#\[.*\]", line)
+
+
def check_webidl_spec(file_name, contents):
# Sorted by this function (in pseudo-Rust). The idea is to group the same
# organization together.
@@ -313,7 +371,17 @@ def check_spec(file_name, contents):
raise StopIteration
file_name = os.path.relpath(os.path.splitext(file_name)[0], base_path)
patt = re.compile("^\s*\/\/.+")
- pattern = "impl<'a> %sMethods for &'a %s {" % (file_name, file_name)
+
+ # Pattern representing a line with a macro
+ macro_patt = re.compile("^\s*\S+!(.*)$")
+
+ # Pattern representing a line with comment containing a spec link
+ link_patt = re.compile("^\s*///? https://.+$")
+
+ # Pattern representing a line with comment
+ comment_patt = re.compile("^\s*///?.+$")
+
+ pattern = "impl %sMethods for %s {" % (file_name, file_name)
contents = contents.splitlines(True)
brace_count = 0
in_impl = False
@@ -323,9 +391,16 @@ def check_spec(file_name, contents):
if not patt.match(line):
if pattern.lower() in line.lower():
in_impl = True
- if "fn " in line and brace_count == 1:
- if "// https://" not in contents[idx - 1] and "// https://" not in contents[idx - 2]:
- yield (idx + 1, "method declared in webidl is missing a comment with a specification link")
+ if ("fn " in line or macro_patt.match(line)) and brace_count == 1:
+ for up_idx in range(1, idx + 1):
+ up_line = contents[idx - up_idx]
+ if link_patt.match(up_line):
+ # Comment with spec link exists
+ break
+ if not comment_patt.match(up_line):
+ # No more comments exist above, yield warning
+ yield (idx + 1, "method declared in webidl is missing a comment with a specification link")
+ break
if '{' in line and in_impl:
brace_count += 1
if '}' in line and in_impl:
@@ -335,7 +410,6 @@ def check_spec(file_name, contents):
def collect_errors_for_files(files_to_check, checking_functions):
- base_path = "components/script/dom/"
for file_name in files_to_check:
with open(file_name, "r") as fp:
contents = fp.read()
@@ -384,13 +458,11 @@ def check_reftest_html_files_in_basic_list(reftest_dir):
def scan():
- sys.path += python_dependencies
-
all_files = (os.path.join(r, f) for r, _, files in os.walk(".") for f in files)
files_to_check = filter(should_check, all_files)
checking_functions = [check_license, check_by_line, check_flake8, check_toml,
- check_rust, check_webidl_spec, check_spec]
+ check_lock, check_rust, check_webidl_spec, check_spec]
errors = collect_errors_for_files(files_to_check, checking_functions)
reftest_files = (os.path.join(r, f) for r, _, files in os.walk(reftest_dir) for f in files)