summaryrefslogtreecommitdiffstats
path: root/tools/lint/libpref/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/lint/libpref/__init__.py')
-rw-r--r--tools/lint/libpref/__init__.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/tools/lint/libpref/__init__.py b/tools/lint/libpref/__init__.py
new file mode 100644
index 0000000000..e986be39d0
--- /dev/null
+++ b/tools/lint/libpref/__init__.py
@@ -0,0 +1,112 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=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 re
+import sys
+
+import yaml
+from mozlint import result
+from mozlint.pathutils import expand_exclusions
+
+# This simple linter checks for duplicates from
+# modules/libpref/init/StaticPrefList.yaml against modules/libpref/init/all.js
+
+# If for any reason a pref needs to appear in both files, add it to this set.
+IGNORE_PREFS = {
+ "devtools.console.stdout.chrome", # Uses the 'sticky' attribute.
+ "devtools.console.stdout.content", # Uses the 'sticky' attribute.
+ "fission.autostart", # Uses the 'locked' attribute.
+ "browser.dom.window.dump.enabled", # Uses the 'sticky' attribute.
+ "apz.fling_curve_function_y2", # This pref is a part of a series.
+ "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", # NOQA: E501; Uses the 'locked' attribute.
+}
+PATTERN = re.compile(r"\s*pref\(\s*\"(?P<pref>.+)\"\s*,\s*(?P<val>.+)\)\s*;.*")
+
+
+def get_names(pref_list_filename):
+ pref_names = {}
+ # We want to transform patterns like 'foo: @VAR@' into valid yaml. This
+ # pattern does not happen in 'name', so it's fine to ignore these.
+ # We also want to evaluate all branches of #ifdefs for pref names, so we
+ # ignore anything else preprocessor related.
+ file = open(pref_list_filename).read().replace("@", "")
+ try:
+ pref_list = yaml.safe_load(file)
+ except (IOError, ValueError) as e:
+ print("{}: error:\n {}".format(pref_list_filename, e), file=sys.stderr)
+ sys.exit(1)
+
+ for pref in pref_list:
+ if pref["name"] not in IGNORE_PREFS:
+ pref_names[pref["name"]] = pref["value"]
+
+ return pref_names
+
+
+# Check the names of prefs against each other, and if the pref is a duplicate
+# that has not previously been noted, add that name to the list of errors.
+def check_against(path, pref_names):
+ errors = []
+ prefs = read_prefs(path)
+ for pref in prefs:
+ if pref["name"] in pref_names:
+ errors.extend(check_value_for_pref(pref, pref_names[pref["name"]], path))
+ return errors
+
+
+def check_value_for_pref(some_pref, some_value, path):
+ errors = []
+ if some_pref["value"] == some_value:
+ errors.append(
+ {
+ "path": path,
+ "message": some_pref["raw"],
+ "lineno": some_pref["line"],
+ "hint": "Remove the duplicate pref or add it to IGNORE_PREFS.",
+ "level": "error",
+ }
+ )
+ return errors
+
+
+# The entries in the *.js pref files are regular enough to use simple pattern
+# matching to load in prefs.
+def read_prefs(path):
+ prefs = []
+ with open(path) as source:
+ for lineno, line in enumerate(source, start=1):
+ match = PATTERN.match(line)
+ if match:
+ prefs.append(
+ {
+ "name": match.group("pref"),
+ "value": evaluate_pref(match.group("val")),
+ "line": lineno,
+ "raw": line,
+ }
+ )
+ return prefs
+
+
+def evaluate_pref(value):
+ bools = {"true": True, "false": False}
+ if value in bools:
+ return bools[value]
+ elif value.isdigit():
+ return int(value)
+ return value
+
+
+def checkdupes(paths, config, **kwargs):
+ results = []
+ errors = []
+ pref_names = get_names(config["support-files"][0])
+ files = list(expand_exclusions(paths, config, kwargs["root"]))
+ for file in files:
+ errors.extend(check_against(file, pref_names))
+ for error in errors:
+ results.append(result.from_config(config, **error))
+ return results