Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(948)

Unified Diff: PRESUBMIT.py

Issue 2598173002: Add a Presubmit to check if a file must be included or imported.
Patch Set: feedback Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | PRESUBMIT_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: PRESUBMIT.py
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 686ae511773be4835973f9e2dac4ab7d9776bd2b..a87d017275ec7c8cf0e74943e58c4af2bcb26eed 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -58,6 +58,11 @@ _TEST_ONLY_WARNING = (
'not perfect. The commit queue will not block on this warning.')
+_INCLUDE_IMPORT_WARNING = (
+ 'You are including ObjC headers or importing C++ headers. Remember to use '
+ 'the right #include/#import directive.')
+
+
_INCLUDE_ORDER_WARNING = (
'Your #include order seems to be broken. Remember to use the right '
'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
@@ -790,6 +795,133 @@ def _CheckNoAuraWindowPropertyHInHeaders(input_api, output_api):
return results
+def _CheckHeaderIsObjectiveC(input_api, included_file, included_file_content):
+ """Checks if f is a C++ or an Objective-C header.
+
+ An Objective-C header is a header with a line starting with
+ - #import
+ - @class
+ - @interface
+ - @implementation
+ - @protocol
+ - @end
+
+ or with the two lines
+ #if !defined(__OBJC__)
+ #error ...
+
+ Returns a pair of booleans.
+ - First is true if the determination of Objective-C could be done without
+ error
+ - Second is true if the file has been determined as Objective-C header.
+ """
+ file_path_ios_mac_pattern = input_api.re.compile(
+ r'(?:.*[_./])?(?:ios|mac)[_./].*')
+ if not file_path_ios_mac_pattern.match(included_file):
+ return (False, False)
+ guard_noobjc_pattern = input_api.re.compile(
+ r'\s*#if !defined\(__OBJC__\).*')
+ guard_objc_pattern = input_api.re.compile(
+ r'\s*#if defined\(__OBJC__\).*')
+ error_pattern = input_api.re.compile(
+ r'\s*#error .*')
+ block_pattern = input_api.re.compile(
+ r'\s*typedef.*\(\^.*')
+ objc_pattern = input_api.re.compile(
+ r'\s*(@class|@interface|@end|@implementation|@protocol|#import).*')
+ lines = included_file_content.NewContents()
+ previous_was_noobjc_define = False
+ for included_line in lines:
+ if guard_noobjc_pattern.match(included_line):
+ previous_was_noobjc_define = True
+ continue
+ if previous_was_noobjc_define:
+ # Found No Objc Guard. The next line must be check to see if it is
+ # throwing an error.
+ if error_pattern.match(included_line):
+ # There is an error thrown if the file is compiled without ObjC.
+ # Header is Objective-C.
+ return (True, True)
+ else:
+ # There are ifdefs about Objective-C, file can likely be imported or
+ # included.
+ return (False, False)
+ previous_was_noobjc_define = False
+ if guard_objc_pattern.match(included_line):
+ # Found ObjcGuard. This file can be included or imported.
+ return (False, False)
+ if objc_pattern.match(included_line) or block_pattern.match(included_line):
+ # Found an Objective-C keyword. File is an Objective-C header.
+ return (True, True)
+ # No Objective-C clue, file must be C++.
+ return (True, False)
+
+
+def _CheckIncludeImportInFile(input_api, f):
+ """Checks the include/import coherence in file f."""
+ custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
+ custom_import_pattern = input_api.re.compile(r'\s*#import "(?P<FILE>.*)"')
+ contents = f.NewContents()
+ warnings = []
+ for (line_num, line) in enumerate(contents):
+ is_imported = False
+ included_file = ""
+ match_cpp = custom_include_pattern.match(line)
+ if match_cpp:
+ match_dict = match_cpp.groupdict()
+ included_file = match_dict['FILE']
+ match_objc = custom_import_pattern.match(line)
+ if match_objc:
+ match_dict = match_objc.groupdict()
+ included_file = match_dict['FILE']
+ is_imported = True
+ if included_file == "":
+ continue
+ included_absolute_path = input_api.os_path.join(
+ input_api.change.RepositoryRoot(), included_file)
+ if not input_api.os_path.isfile(included_absolute_path):
+ # Included file may need a custom -I flag.
+ # Ignore it.
+ continue
+ included_file_content = input_api.ReadFile(included_absolute_path)
+ (detection_ok, has_objc) = \
+ _CheckHeaderIsObjectiveC(input_api,
+ included_file,
+ included_file_content)
+ if not detection_ok:
+ # There is an Objective-C guard, the file is internded to be included or
+ # imported.
+ continue
+ if is_imported and f.LocalPath().endswith('.cc'):
+ warnings.append('%s:%d %s' % (f.LocalPath(), line_num, line))
+ warnings.append('Header is imported in a C++ file.')
+ if has_objc and not is_imported:
+ warnings.append('%s:%d %s' % (f.LocalPath(), line_num, line))
+ warnings.append('Header is included but has Objective-C instructions.')
+ if not has_objc and is_imported:
+ warnings.append('%s:%d %s' % (f.LocalPath(), line_num, line))
+ warnings.append('Header is imported but has no Objective-C instructions.')
+ return warnings
+
+
+def _CheckIncludeImport(input_api, output_api):
+ """Checks that C++ headers are included, Objective-C headers imported.
+ """
+ def FileFilterIncludeImport(affected_file):
+ black_list = (_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST)
+ return input_api.FilterSourceFile(affected_file, black_list=black_list)
+
+ warnings = []
+ for f in input_api.AffectedFiles(file_filter=FileFilterIncludeImport):
+ warnings.extend(_CheckIncludeImportInFile(input_api, f))
+
+ results = []
+ if warnings:
+ results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_IMPORT_WARNING,
+ warnings))
+ return results
+
+
def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
"""Checks that the lines in scope occur in the right order.
@@ -2088,6 +2220,7 @@ def _CommonChecks(input_api, output_api):
results.extend(_CheckFilePermissions(input_api, output_api))
results.extend(_CheckTeamTags(input_api, output_api))
results.extend(_CheckNoAuraWindowPropertyHInHeaders(input_api, output_api))
+ results.extend(_CheckIncludeImport(input_api, output_api))
results.extend(_CheckIncludeOrder(input_api, output_api))
results.extend(_CheckForVersionControlConflicts(input_api, output_api))
results.extend(_CheckPatchFiles(input_api, output_api))
« no previous file with comments | « no previous file | PRESUBMIT_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698