Check OS_*, COMPILER_*, ARCH_CPU_*, WCHAR_T_IS_* usage without build/build_config.h
If we use these macros without including build_config.h, a preprocessor silently
removes enclosed code blocks, and developers might waste a lot of time to
investigate unexpected behavior. So checking build_config.h inclusion is helpful.
In many cases, build_config.h is included indirectly, and macros work well
without including it explicitly. However, we should include it explicitly anyway
because of IWYU.
Bug: 737403
Change-Id: I6e75167d36c176091b0100dd40a4091cc1e25d1b
Reviewed-on: https://chromium-review.googlesource.com/553658
Reviewed-by: Nico Weber <[email protected]>
Reviewed-by: Dirk Pranke <[email protected]>
Commit-Queue: Kent Tamura <[email protected]>
Cr-Commit-Position: refs/heads/master@{#483547}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 3b003c8..3ab7802 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -2089,6 +2089,7 @@
results.extend(_CheckPatchFiles(input_api, output_api))
results.extend(_CheckHardcodedGoogleHostsInLowerLayers(input_api, output_api))
results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api))
+ results.extend(_CheckBuildConfigMacrosWithoutInclude(input_api, output_api))
results.extend(_CheckForInvalidOSMacros(input_api, output_api))
results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api))
results.extend(_CheckFlakyTestUsage(input_api, output_api))
@@ -2132,6 +2133,51 @@
return []
+def _CheckBuildConfigMacrosWithoutInclude(input_api, output_api):
+ macro_re = input_api.re.compile(
+ r'^\s*#(el)?if.*\bdefined\(((OS_|COMPILER_|ARCH_CPU_|WCHAR_T_IS_)[^)]*)')
+ include_re = input_api.re.compile(
+ r'^#include\s+"build/build_config.h"', input_api.re.MULTILINE)
+ extension_re = input_api.re.compile(r'\.[a-z]+$')
+ errors = []
+ for f in input_api.AffectedFiles():
+ if not f.LocalPath().endswith(('.h', '.c', '.cc', '.cpp', '.m', '.mm')):
+ continue
+ found_line_number = None
+ found_macro = None
+ for line_num, line in f.ChangedContents():
+ match = macro_re.search(line)
+ if match:
+ found_line_number = line_num
+ found_macro = match.group(2)
+ break
+ if not found_line_number:
+ continue
+
+ found_include = False
+ for line in f.NewContents():
+ if include_re.search(line):
+ found_include = True
+ break
+ if found_include:
+ continue
+
+ if not f.LocalPath().endswith('.h'):
+ primary_header_path = extension_re.sub('.h', f.AbsoluteLocalPath())
+ try:
+ content = input_api.ReadFile(primary_header_path, 'r')
+ if include_re.search(content):
+ continue
+ except IOError:
+ pass
+ errors.append('%s:%d %s macro is used without including build/'
+ 'build_config.h.'
+ % (f.LocalPath(), found_line_number, found_macro))
+ if errors:
+ return [output_api.PresubmitPromptWarning('\n'.join(errors))]
+ return []
+
+
def _DidYouMeanOSMacro(bad_macro):
try:
return {'A': 'OS_ANDROID',