Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / tools / lint / libpref / __init__.py
blob5c93750830953afc1555011dfb1679802b02d80e
1 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
2 # vim: set filetype=python:
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 import re
8 import sys
10 import yaml
11 from mozlint import result
12 from mozlint.pathutils import expand_exclusions
14 # This simple linter checks for duplicates from
15 # modules/libpref/init/StaticPrefList.yaml against modules/libpref/init/all.js
17 # If for any reason a pref needs to appear in both files, add it to this set.
18 IGNORE_PREFS = {
19 "devtools.console.stdout.chrome", # Uses the 'sticky' attribute.
20 "devtools.console.stdout.content", # Uses the 'sticky' attribute.
21 "fission.autostart", # Uses the 'locked' attribute.
22 "browser.dom.window.dump.enabled", # Uses the 'sticky' attribute.
23 "apz.fling_curve_function_y2", # This pref is a part of a series.
24 "dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", # NOQA: E501; Uses the 'locked' attribute.
25 "extensions.backgroundServiceWorkerEnabled.enabled", # NOQA: E501; Uses the 'locked' attribute.
27 PATTERN = re.compile(r"\s*pref\(\s*\"(?P<pref>.+)\"\s*,\s*(?P<val>.+)\)\s*;.*")
30 def get_names(pref_list_filename):
31 pref_names = {}
32 # We want to transform patterns like 'foo: @VAR@' into valid yaml. This
33 # pattern does not happen in 'name', so it's fine to ignore these.
34 # We also want to evaluate all branches of #ifdefs for pref names, so we
35 # ignore anything else preprocessor related.
36 file = open(pref_list_filename, encoding="utf-8").read().replace("@", "")
37 try:
38 pref_list = yaml.safe_load(file)
39 except (IOError, ValueError) as e:
40 print("{}: error:\n {}".format(pref_list_filename, e), file=sys.stderr)
41 sys.exit(1)
43 for pref in pref_list:
44 if pref["name"] not in IGNORE_PREFS:
45 pref_names[pref["name"]] = pref["value"]
47 return pref_names
50 # Check the names of prefs against each other, and if the pref is a duplicate
51 # that has not previously been noted, add that name to the list of errors.
52 def check_against(path, pref_names):
53 errors = []
54 prefs = read_prefs(path)
55 for pref in prefs:
56 if pref["name"] in pref_names:
57 errors.extend(check_value_for_pref(pref, pref_names[pref["name"]], path))
58 return errors
61 def check_value_for_pref(some_pref, some_value, path):
62 errors = []
63 if some_pref["value"] == some_value:
64 errors.append(
66 "path": path,
67 "message": some_pref["raw"],
68 "lineno": some_pref["line"],
69 "hint": "Remove the duplicate pref or add it to IGNORE_PREFS.",
70 "level": "error",
73 return errors
76 # The entries in the *.js pref files are regular enough to use simple pattern
77 # matching to load in prefs.
78 def read_prefs(path):
79 prefs = []
80 with open(path, encoding="utf-8") as source:
81 for lineno, line in enumerate(source, start=1):
82 match = PATTERN.match(line)
83 if match:
84 prefs.append(
86 "name": match.group("pref"),
87 "value": evaluate_pref(match.group("val")),
88 "line": lineno,
89 "raw": line,
92 return prefs
95 def evaluate_pref(value):
96 bools = {"true": True, "false": False}
97 if value in bools:
98 return bools[value]
99 elif value.isdigit():
100 return int(value)
101 return value
104 def checkdupes(paths, config, **kwargs):
105 results = []
106 errors = []
107 pref_names = get_names(config["support-files"][0])
108 files = list(expand_exclusions(paths, config, kwargs["root"]))
109 for file in files:
110 errors.extend(check_against(file, pref_names))
111 for error in errors:
112 results.append(result.from_config(config, **error))
113 return results