From b8cc63ee6d785a0d5921fce6df594037e16c99a3 Mon Sep 17 00:00:00 2001 From: Clemens Hammacher Date: Mon, 11 Sep 2017 14:14:14 +0200 Subject: [PATCH] [presubmit] Add check for unbalanced #define / #undef With jumbo builds, we get spurious errors if several .cc files define the same preprocessor macro without undefining it. This is also disallowed by the style guide. This patch adds a presubmit check to check that each #define is eventually followed by a corresponding #undef. Special care has to be taken for conditionally defined macros. Here, the logic to check that there are not multiple defines and that they are undefined in all cases is not perfect; it's optimistic in order to avoid false positives. R=mstarzinger@chromium.org, machenbach@chromium.org Bug: v8:6811 Change-Id: I55cde499399d97a5eb02fbc5ecfa34e6a8099d06 Reviewed-on: https://chromium-review.googlesource.com/657104 Commit-Queue: Clemens Hammacher Reviewed-by: Michael Achenbach Reviewed-by: Michael Starzinger Cr-Commit-Position: refs/heads/master@{#47944} --- PRESUBMIT.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 2d79ae682c..d3195c24a7 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -279,6 +279,7 @@ def _CommonChecks(input_api, output_api): _CheckNoInlineHeaderIncludesInNormalHeaders(input_api, output_api)) results.extend(_CheckMissingFiles(input_api, output_api)) results.extend(_CheckJSONFiles(input_api, output_api)) + results.extend(_CheckMacroUndefs(input_api, output_api)) return results @@ -337,6 +338,66 @@ def _CheckJSONFiles(input_api, output_api): return [output_api.PresubmitError(r) for r in results] +def _CheckMacroUndefs(input_api, output_api): + """ + Checks that each #define in a .cc file is eventually followed by an #undef. + + TODO(clemensh): This check should eventually be enabled for all cc files via + tools/presubmit.py (https://crbug.com/v8/6811). + """ + def FilterFile(affected_file): + # Skip header files, as they often define type lists which are used in + # other files. + white_list = (r'.+\.cc',r'.+\.cpp',r'.+\.c') + return input_api.FilterSourceFile(affected_file, white_list=white_list) + + def TouchesMacros(f): + for line in f.GenerateScmDiff().splitlines(): + if not line.startswith('+') and not line.startswith('-'): + continue + if define_pattern.match(line[1:]) or undef_pattern.match(line[1:]): + return True + return False + + define_pattern = input_api.re.compile(r'#define (\w+)') + undef_pattern = input_api.re.compile(r'#undef (\w+)') + errors = [] + for f in input_api.AffectedFiles( + file_filter=FilterFile, include_deletes=False): + if not TouchesMacros(f): + continue + + defined_macros = dict() + with open(f.LocalPath()) as fh: + line_nr = 0 + for line in fh: + line_nr += 1 + + define_match = define_pattern.match(line) + if define_match: + name = define_match.group(1) + defined_macros[name] = line_nr + + undef_match = undef_pattern.match(line) + if undef_match: + name = undef_match.group(1) + if not name in defined_macros: + errors.append('{}:{}: Macro named \'{}\' was not defined before.' + .format(f.LocalPath(), line_nr, name)) + else: + del defined_macros[name] + for name, line_nr in sorted(defined_macros.items(), key=lambda e: e[1]): + errors.append('{}:{}: Macro missing #undef: {}' + .format(f.LocalPath(), line_nr, name)) + + if errors: + return [output_api.PresubmitPromptOrNotify( + 'Detected mismatches in #define / #undef in the file(s) where you ' + 'modified preprocessor macros.', + errors)] + return [] + + def CheckChangeOnUpload(input_api, output_api): results = [] results.extend(_CommonChecks(input_api, output_api))