# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import sys
import os
import re
sys.path += [os.path.dirname(os.path.dirname(__file__))]
from style_variable_generator.css_generator import CSSStyleGenerator
from style_variable_generator.presubmit_support import RunGit
# TODO(calamity): extend this checker to find unused C++ variables
def FindInvalidCSSVariables(file_to_json_strings, git_runner=RunGit):
style_generator = CSSStyleGenerator()
css_prefixes = set()
referenced_vars = set()
for f, json_string in file_to_json_strings.items():
style_generator.AddJSONToModel(json_string, in_file=f)
referenced_vars |= set(re.findall(r'\$([a-z_0-9]+)', json_string))
context = style_generator.in_file_to_context.get(f, {}).get('CSS')
if (not context or 'prefix' not in context):
raise KeyError('This tool only works on files with a CSS prefix.')
css_prefixes.add('--' + context['prefix'] + '-')
unspecified_file_and_names = []
css_var_names = style_generator.GetCSSVarNames()
valid_names = set(css_var_names.keys())
unused = set(css_var_names.values()).difference(referenced_vars)
for css_prefix in css_prefixes:
grep_result = git_runner([
'grep', '-on',
'\\%s[a-z0-9-]*' % css_prefix, '--', '*.css', '*.html', '*.js'
]).decode('utf-8').splitlines()
found_files_and_names = [x.split(':') for x in grep_result]
found_names = set()
for (filename, line, name) in found_files_and_names:
found_names.add(name)
if name in valid_names and css_var_names[name] in unused:
unused.remove(css_var_names[name])
unspecified = found_names.difference(valid_names)
for (filename, line, name) in found_files_and_names:
if filename.find('test') != -1:
continue
if name in unspecified:
unspecified_file_and_names.append('%s:%s:%s' %
(filename, line, name))
return {
'unspecified': unspecified_file_and_names,
# TODO(calamity): This should also account for names referenced in json5
# files.
'unused': unused,
'css_prefix': css_prefix,
}
def main():
parser = argparse.ArgumentParser(
description='''Finds CSS variables in the codebase that are prefixed
with |input_files|' CSS prefix but aren't specified in |input_files|.'''
)
parser.add_argument('targets', nargs='+', help='source json5 color files', )
args = parser.parse_args()
input_files = args.targets
file_to_json_strings = {}
for input_file in input_files:
with open(input_file, 'r') as f:
file_to_json_strings[input_file] = f.read()
result = FindInvalidCSSVariables(file_to_json_strings)
print('Has prefix %s but not in %s:' % (result['css_prefix'], input_files))
for name in sorted(result['unspecified']):
print(name)
print('\nGenerated by %s but not used in codebase:' % input_files)
for name in sorted(result['unused']):
print(name)
return 0
if __name__ == '__main__':
sys.exit(main())