Clean up the banned function checker in PRESUBMIT.py.

Use a named tuple and add some typing hints that, in theory, should help
catch some mistakes. Fix all the mistakes in the ban rules that pytype
found as well. :)

Also deletes:
- `_INCLUDE_ORDER_WARNING` which is no longer referenced anywhere
- `base::size()`/`base::empty()`/`base::data()` ban, since those
  functions have been removed

Change-Id: I6b26e065e4db51dc20301ed0ee16ab8ef0384a9f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3524396
Reviewed-by: Michael Thiessen <[email protected]>
Commit-Queue: Daniel Cheng <[email protected]>
Cr-Commit-Position: refs/heads/main@{#981274}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 2ebda3e3..89a8ccd 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -7,6 +7,11 @@
 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
 for more details about the presubmit API built into depot_tools.
 """
+
+from typing import Optional
+from typing import Sequence
+from dataclasses import dataclass
+
 PRESUBMIT_VERSION = '2.0.0'
 
 # This line is 'magic' in that git-cl looks for it to decide whether to
@@ -104,51 +109,75 @@
     'release apk.')
 
 
-_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/'
-    'cppguide.html#Names_and_Order_of_Includes')
+@dataclass
+class BanRule:
+  # String pattern. If the pattern begins with a slash, the pattern will be
+  # treated as a regular expression instead.
+  pattern: str
+  # Explanation as a sequence of strings. Each string in the sequence will be
+  # printed on its own line.
+  explanation: Sequence[str]
+  # Whether or not to treat this ban as a fatal error. If unspecified, defaults
+  # to true.
+  treat_as_error: Optional[bool] = None
+  # Paths that should be excluded from the ban check. Each string is a regular
+  # expression that will be matched against the path of the file being checked
+  # relative to the root of the source tree.
+  excluded_paths: Optional[Sequence[str]] = None
 
-# Format: Sequence of tuples containing:
-# * Full import path.
-# * Sequence of strings to show when the pattern matches.
-# * Sequence of path or filename exceptions to this rule
-_BANNED_JAVA_IMPORTS = ((
-    'java.net.URI;',
-    ('Use org.chromium.url.GURL instead of java.net.URI, where possible.', ),
-    (
-        'net/android/javatests/src/org/chromium/net/'
-        'AndroidProxySelectorTest.java',
-        'components/cronet/',
-        'third_party/robolectric/local/',
+
+# Format: Sequence of BanRule:
+_BANNED_JAVA_IMPORTS = (
+    BanRule(
+      'import java.net.URI;',
+      (
+       'Use org.chromium.url.GURL instead of java.net.URI, where possible.',
+      ),
+      excluded_paths=(
+        (r'net/android/javatests/src/org/chromium/net/'
+         'AndroidProxySelectorTest\.java'),
+        r'components/cronet/',
+        r'third_party/robolectric/local/',
+      ),
     ),
-), (
-    'android.annotation.TargetApi;',
-    ('Do not use TargetApi, use @androidx.annotation.RequiresApi instead. '
-     'RequiresApi ensures that any calls are guarded by the appropriate '
-     'SDK_INT check. See https://crbug.com/1116486.', ),
-    (),
-), (
-    'android.support.test.rule.UiThreadTestRule;',
-    ('Do not use UiThreadTestRule, just use '
-     '@org.chromium.base.test.UiThreadTest on test methods that should run '
-     'on the UI thread. See https://crbug.com/1111893.', ),
-    (),
-), ('android.support.test.annotation.UiThreadTest;',
-    ('Do not use android.support.test.annotation.UiThreadTest, use '
-     'org.chromium.base.test.UiThreadTest instead. See '
-     'https://crbug.com/1111893.', ),
-    ()), ('android.support.test.rule.ActivityTestRule;',
-          ('Do not use ActivityTestRule, use '
-           'org.chromium.base.test.BaseActivityTestRule instead.', ),
-          ('components/cronet/', )))
+    BanRule(
+      'import android.annotation.TargetApi;',
+      (
+       'Do not use TargetApi, use @androidx.annotation.RequiresApi instead. '
+       'RequiresApi ensures that any calls are guarded by the appropriate '
+       'SDK_INT check. See https://crbug.com/1116486.',
+      ),
+    ),
+    BanRule(
+      'import android.support.test.rule.UiThreadTestRule;',
+      (
+       'Do not use UiThreadTestRule, just use '
+       '@org.chromium.base.test.UiThreadTest on test methods that should run '
+       'on the UI thread. See https://crbug.com/1111893.',
+      ),
+    ),
+    BanRule(
+      'import android.support.test.annotation.UiThreadTest;',
+      ('Do not use android.support.test.annotation.UiThreadTest, use '
+       'org.chromium.base.test.UiThreadTest instead. See '
+       'https://crbug.com/1111893.',
+      ),
+    ),
+    BanRule(
+      'import android.support.test.rule.ActivityTestRule;',
+      (
+       'Do not use ActivityTestRule, use '
+       'org.chromium.base.test.BaseActivityTestRule instead.',
+      ),
+      excluded_paths=(
+        'components/cronet/',
+      ),
+    ),
+)
 
-# Format: Sequence of tuples containing:
-# * String pattern or, if starting with a slash, a regular expression.
-# * Sequence of strings to show when the pattern matches.
-# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
+# Format: Sequence of BanRule:
 _BANNED_JAVA_FUNCTIONS = (
-    (
+    BanRule(
       'StrictMode.allowThreadDiskReads()',
       (
        'Prefer using StrictModeContext.allowDiskReads() to using StrictMode '
@@ -156,7 +185,7 @@
       ),
       False,
     ),
-    (
+    BanRule(
       'StrictMode.allowThreadDiskWrites()',
       (
        'Prefer using StrictModeContext.allowDiskWrites() to using StrictMode '
@@ -174,12 +203,9 @@
     ),
 )
 
-# Format: Sequence of tuples containing:
-# * String pattern or, if starting with a slash, a regular expression.
-# * Sequence of strings to show when the pattern matches.
-# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
+# Format: Sequence of BanRule:
 _BANNED_OBJC_FUNCTIONS = (
-    (
+    BanRule(
       'addTrackingRect:',
       (
        'The use of -[NSView addTrackingRect:owner:userData:assumeInside:] is'
@@ -188,7 +214,7 @@
       ),
       False,
     ),
-    (
+    BanRule(
       r'/NSTrackingArea\W',
       (
        'The use of NSTrackingAreas is prohibited. Please use CrTrackingArea',
@@ -197,7 +223,7 @@
       ),
       False,
     ),
-    (
+    BanRule(
       'convertPointFromBase:',
       (
        'The use of -[NSView convertPointFromBase:] is almost certainly wrong.',
@@ -206,7 +232,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       'convertPointToBase:',
       (
        'The use of -[NSView convertPointToBase:] is almost certainly wrong.',
@@ -215,7 +241,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       'convertRectFromBase:',
       (
        'The use of -[NSView convertRectFromBase:] is almost certainly wrong.',
@@ -224,7 +250,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       'convertRectToBase:',
       (
        'The use of -[NSView convertRectToBase:] is almost certainly wrong.',
@@ -233,7 +259,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       'convertSizeFromBase:',
       (
        'The use of -[NSView convertSizeFromBase:] is almost certainly wrong.',
@@ -242,7 +268,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       'convertSizeToBase:',
       (
        'The use of -[NSView convertSizeToBase:] is almost certainly wrong.',
@@ -251,7 +277,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       r"/\s+UTF8String\s*]",
       (
        'The use of -[NSString UTF8String] is dangerous as it can return null',
@@ -260,7 +286,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       r'__unsafe_unretained',
       (
         'The use of __unsafe_unretained is almost certainly wrong, unless',
@@ -269,7 +295,7 @@
       ),
       False,
     ),
-    (
+    BanRule(
       'freeWhenDone:NO',
       (
         'The use of "freeWhenDone:NO" with the NoCopy creation of ',
@@ -279,12 +305,9 @@
     ),
 )
 
-# Format: Sequence of tuples containing:
-# * String pattern or, if starting with a slash, a regular expression.
-# * Sequence of strings to show when the pattern matches.
-# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
+# Format: Sequence of BanRule:
 _BANNED_IOS_OBJC_FUNCTIONS = (
-    (
+    BanRule(
       r'/\bTEST[(]',
       (
         'TEST() macro should not be used in Objective-C++ code as it does not ',
@@ -294,7 +317,7 @@
       ),
       True,
     ),
-    (
+    BanRule(
       r'/\btesting::Test\b',
       (
         'testing::Test should not be used in Objective-C++ code as it does ',
@@ -305,12 +328,9 @@
     ),
 )
 
-# Format: Sequence of tuples containing:
-# * String pattern or, if starting with a slash, a regular expression.
-# * Sequence of strings to show when the pattern matches.
-# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
+# Format: Sequence of BanRule:
 _BANNED_IOS_EGTEST_FUNCTIONS = (
-    (
+    BanRule(
       r'/\bEXPECT_OCMOCK_VERIFY\b',
       (
         'EXPECT_OCMOCK_VERIFY should not be used in EarlGrey tests because ',
@@ -320,13 +340,9 @@
     ),
 )
 
-# Format: Sequence of tuples containing:
-# * String pattern or, if starting with a slash, a regular expression.
-# * Sequence of strings to show when the pattern matches.
-# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
-# * Sequence of paths to *not* check (regexps).
+# Format: Sequence of BanRule:
 _BANNED_CPP_FUNCTIONS = (
-    (
+    BanRule(
       r'/\busing namespace ',
       (
        'Using directives ("using namespace x") are banned by the Google Style',
@@ -339,7 +355,7 @@
     # Make sure that gtest's FRIEND_TEST() macro is not used; the
     # FRIEND_TEST_ALL_PREFIXES() macro from base/gtest_prod_util.h should be
     # used instead since that allows for FLAKY_ and DISABLED_ prefixes.
-    (
+    BanRule(
       'FRIEND_TEST(',
       (
        'Chromium code should not use gtest\'s FRIEND_TEST() macro. Include',
@@ -348,7 +364,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'setMatrixClip',
       (
         'Overriding setMatrixClip() is prohibited; ',
@@ -357,7 +373,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'SkRefPtr',
       (
         'The use of SkRefPtr is prohibited. ',
@@ -366,7 +382,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'SkAutoRef',
       (
         'The indirect use of SkRefPtr via SkAutoRef is prohibited. ',
@@ -375,7 +391,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'SkAutoTUnref',
       (
         'The use of SkAutoTUnref is dangerous because it implicitly ',
@@ -384,7 +400,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'SkAutoUnref',
       (
         'The indirect use of SkAutoTUnref through SkAutoUnref is dangerous ',
@@ -394,7 +410,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       r'/HANDLE_EINTR\(.*close',
       (
        'HANDLE_EINTR(close) is invalid. If close fails with EINTR, the file',
@@ -405,7 +421,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       r'/IGNORE_EINTR\((?!.*close)',
       (
        'IGNORE_EINTR is only valid when wrapping close. To wrap other system',
@@ -418,7 +434,7 @@
         r'^ppapi[\\/]tests[\\/]test_broker\.cc$',
       ),
     ),
-    (
+    BanRule(
       r'/v8::Extension\(',
       (
         'Do not introduce new v8::Extensions into the code base, use',
@@ -429,7 +445,7 @@
         r'extensions[\\/]renderer[\\/]safe_builtins\.*',
       ),
     ),
-    (
+    BanRule(
       '#pragma comment(lib,',
       (
         'Specify libraries to link with in build files and not in the source.',
@@ -440,7 +456,7 @@
           r'^third_party[\\/]abseil-cpp[\\/].*',
       ),
     ),
-    (
+    BanRule(
       r'/base::SequenceChecker\b',
       (
         'Consider using SEQUENCE_CHECKER macros instead of the class directly.',
@@ -448,7 +464,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       r'/base::ThreadChecker\b',
       (
         'Consider using THREAD_CHECKER macros instead of the class directly.',
@@ -456,7 +472,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
       (
         'base::TimeXXX::FromInternalValue() and ToInternalValue() are',
@@ -471,7 +487,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'CallJavascriptFunctionUnsafe',
       (
         "Don't use CallJavascriptFunctionUnsafe() in new code. Instead, use",
@@ -485,7 +501,7 @@
         r'^content[\\/]public[\\/]test[\\/]test_web_ui\.(cc|h)$',
       ),
     ),
-    (
+    BanRule(
       'leveldb::DB::Open',
       (
         'Instead of leveldb::DB::Open() use leveldb_env::OpenDB() from',
@@ -497,7 +513,7 @@
         r'^third_party/leveldatabase/.*\.(cc|h)$',
       ),
     ),
-    (
+    BanRule(
       'leveldb::NewMemEnv',
       (
         'Instead of leveldb::NewMemEnv() use leveldb_chrome::NewMemEnv() from',
@@ -509,7 +525,7 @@
         r'^third_party/leveldatabase/.*\.(cc|h)$',
       ),
     ),
-    (
+    BanRule(
       'RunLoop::QuitCurrent',
       (
         'Please migrate away from RunLoop::QuitCurrent*() methods. Use member',
@@ -518,7 +534,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'base::ScopedMockTimeMessageLoopTaskRunner',
       (
         'ScopedMockTimeMessageLoopTaskRunner is deprecated. Prefer',
@@ -530,7 +546,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'std::regex',
       (
         'Using std::regex adds unnecessary binary size to Chrome. Please use',
@@ -540,7 +556,7 @@
       # Abseil's benchmarks never linked into chrome.
       ['third_party/abseil-cpp/.*_benchmark.cc'],
     ),
-    (
+    BanRule(
       r'/\bstd::stoi\b',
       (
         'std::stoi uses exceptions to communicate results. ',
@@ -549,7 +565,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stol\b',
       (
         'std::stol uses exceptions to communicate results. ',
@@ -558,7 +574,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stoul\b',
       (
         'std::stoul uses exceptions to communicate results. ',
@@ -567,7 +583,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stoll\b',
       (
         'std::stoll uses exceptions to communicate results. ',
@@ -576,7 +592,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stoull\b',
       (
         'std::stoull uses exceptions to communicate results. ',
@@ -585,7 +601,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stof\b',
       (
         'std::stof uses exceptions to communicate results. ',
@@ -596,7 +612,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stod\b',
       (
         'std::stod uses exceptions to communicate results. ',
@@ -607,7 +623,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::stold\b',
       (
         'std::stold uses exceptions to communicate results. ',
@@ -618,7 +634,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::to_string\b',
       (
         'std::to_string is locale dependent and slower than alternatives.',
@@ -630,7 +646,7 @@
       False,  # Only a warning since it is already used.
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::shared_ptr\b',
       (
         'std::shared_ptr should not be used. Use scoped_refptr instead.',
@@ -654,7 +670,7 @@
        '^tools/clang/plugins/tests/',
        _THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::weak_ptr\b',
       (
         'std::weak_ptr should not be used. Use base::WeakPtr instead.',
@@ -662,7 +678,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\blong long\b',
       (
         'long long is banned. Use stdint.h if you need a 64 bit number.',
@@ -670,16 +686,16 @@
       False,  # Only a warning since it is already used.
       [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
     ),
-    (
+    BanRule(
       r'\b(absl|std)::any\b',
       (
-        'absl::any / std::any are not safe to use in a component build.'
+        'absl::any / std::any are not safe to use in a component build.',
       ),
       True,
       # Not an error in third party folders, though it probably should be :)
       [_THIRD_PARTY_EXCEPT_BLINK],
     ),
-    (
+    BanRule(
       r'/\bstd::bind\b',
       (
         'std::bind is banned because of lifetime risks.',
@@ -688,7 +704,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::optional\b',
       (
         'std::optional is banned. Use absl::optional instead.',
@@ -696,7 +712,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\b#include <chrono>\b',
       (
         '<chrono> overlaps with Time APIs in base. Keep using',
@@ -705,7 +721,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\b#include <exception>\b',
       (
         'Exceptions are banned and disabled in Chromium.',
@@ -713,7 +729,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::function\b',
       (
         'std::function is banned. Instead use base::OnceCallback or ',
@@ -723,7 +739,7 @@
       False,  # Only a warning since it is already used.
       [_THIRD_PARTY_EXCEPT_BLINK],  # Do not warn in third_party folders.
     ),
-    (
+    BanRule(
       r'/\b#include <random>\b',
       (
         'Do not use any random number engines from <random>. Instead',
@@ -732,7 +748,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\b#include <X11/',
       (
         'Do not use Xlib. Use xproto (from //ui/gfx/x:xproto) instead.',
@@ -740,7 +756,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       r'/\bstd::ratio\b',
       (
         'std::ratio is banned by the Google Style Guide.',
@@ -748,7 +764,7 @@
       True,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       ('base::ThreadRestrictions::ScopedAllowIO'),
       (
         'ScopedAllowIO is deprecated, use ScopedAllowBlocking instead.',
@@ -756,7 +772,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       r'/\bRunMessageLoop\b',
       (
           'RunMessageLoop is deprecated, use RunLoop instead.',
@@ -764,7 +780,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'RunThisRunLoop',
       (
           'RunThisRunLoop is deprecated, use RunLoop directly instead.',
@@ -772,7 +788,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'RunAllPendingInMessageLoop()',
       (
           "Prefer RunLoop over RunAllPendingInMessageLoop, please contact gab@",
@@ -781,7 +797,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'RunAllPendingInMessageLoop(BrowserThread',
       (
           'RunAllPendingInMessageLoop is deprecated. Use RunLoop for',
@@ -792,7 +808,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       r'MessageLoopRunner',
       (
           'MessageLoopRunner is deprecated, use RunLoop instead.',
@@ -800,7 +816,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'GetDeferredQuitTaskForRunLoop',
       (
           "GetDeferredQuitTaskForRunLoop shouldn't be needed, please contact",
@@ -809,7 +825,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'sqlite3_initialize(',
       (
         'Instead of calling sqlite3_initialize(), depend on //sql, ',
@@ -821,7 +837,7 @@
         r'^third_party/sqlite/.*\.(c|cc|h)$',
       ),
     ),
-    (
+    BanRule(
       'std::random_shuffle',
       (
         'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
@@ -830,7 +846,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'ios/web/public/test/http_server',
       (
         'web::HTTPserver is deprecated use net::EmbeddedTestServer instead.',
@@ -838,7 +854,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'GetAddressOf',
       (
         'Improper use of Microsoft::WRL::ComPtr<T>::GetAddressOf() has been ',
@@ -849,7 +865,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'SHFileOperation',
       (
         'SHFileOperation was deprecated in Windows Vista, and there are less ',
@@ -859,7 +875,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'StringFromGUID2',
       (
         'StringFromGUID2 introduces an unnecessary dependency on ole32.dll.',
@@ -867,10 +883,10 @@
       ),
       True,
       (
-        r'/base/win/win_util_unittest.cc'
+        r'/base/win/win_util_unittest.cc',
       ),
     ),
-    (
+    BanRule(
       'StringFromCLSID',
       (
         'StringFromCLSID introduces an unnecessary dependency on ole32.dll.',
@@ -878,10 +894,10 @@
       ),
       True,
       (
-        r'/base/win/win_util_unittest.cc'
+        r'/base/win/win_util_unittest.cc',
       ),
     ),
-    (
+    BanRule(
       'kCFAllocatorNull',
       (
         'The use of kCFAllocatorNull with the NoCopy creation of ',
@@ -890,7 +906,7 @@
       True,
       (),
     ),
-    (
+    BanRule(
       'mojo::ConvertTo',
       (
         'mojo::ConvertTo and TypeConverter are deprecated. Please consider',
@@ -906,7 +922,7 @@
         r'^content/renderer/.*\.(cc|h)$',
       ),
     ),
-    (
+    BanRule(
       'GetInterfaceProvider',
       (
         'InterfaceProvider is deprecated.',
@@ -916,7 +932,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'CComPtr',
       (
         'New code should use Microsoft::WRL::ComPtr from wrl/client.h as a ',
@@ -926,7 +942,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       r'/\b(IFACE|STD)METHOD_?\(',
       (
         'IFACEMETHOD() and STDMETHOD() make code harder to format and read.',
@@ -935,7 +951,7 @@
       False,
       [_THIRD_PARTY_EXCEPT_BLINK],  # Not an error in third_party folders.
     ),
-    (
+    BanRule(
       'set_owned_by_client',
       (
         'set_owned_by_client is deprecated.',
@@ -946,7 +962,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'RemoveAllChildViewsWithoutDeleting',
       (
         'RemoveAllChildViewsWithoutDeleting is deprecated.',
@@ -956,7 +972,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       r'/\bTRACE_EVENT_ASYNC_',
       (
           'Please use TRACE_EVENT_NESTABLE_ASYNC_.. macros instead',
@@ -968,7 +984,7 @@
         r'^base/tracing/.*',
       ),
     ),
-    (
+    BanRule(
       r'/\bbase::debug::DumpWithoutCrashingUnthrottled[(][)]',
       (
           'base::debug::DumpWithoutCrashingUnthrottled() does not throttle',
@@ -978,7 +994,7 @@
       False,
       (),
     ),
-    (
+    BanRule(
       'RoInitialize',
       (
         'Improper use of [base::win]::RoInitialize() has been implicated in a ',
@@ -987,27 +1003,16 @@
       ),
       True,
       (
-          r'^base[\\/]win[\\/]scoped_winrt_initializer\.cc$'
+          r'^base[\\/]win[\\/]scoped_winrt_initializer\.cc$',
       ),
     ),
-    (
-        r'/base::(size|empty|data)',
-        (
-            'Please use the STL equivalent (std::size/std::empty/std::data) ',
-            'instead. The base versions are being removed: ',
-            'https://crbug.com/1299695'
-
-        ),
-        False,
-        [_THIRD_PARTY_EXCEPT_BLINK],  # Don't warn in third_party folders.
-    ),
 )
 
 # Format: Sequence of tuples containing:
 # * String pattern or, if starting with a slash, a regular expression.
 # * Sequence of strings to show when the pattern matches.
 _DEPRECATED_MOJO_TYPES = (
-    (
+    BanRule(
       r'/\bmojo::AssociatedInterfacePtrInfo\b',
       (
         'mojo::AssociatedInterfacePtrInfo<Interface> is deprecated.',
@@ -1576,7 +1581,7 @@
 
 
 def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
-                               type_name, message):
+                               ban_rule):
     """Helper method for CheckNoBannedFunctions and CheckNoDeprecatedMojoTypes.
 
     Returns an string composed of the name of the file, the line number where the
@@ -1585,25 +1590,25 @@
     """
     result = []
 
-    if input_api.re.search(r"^ *//",
-                           line):  # Ignore comments about banned types.
+    # Ignore comments about banned types.
+    if input_api.re.search(r"^ *//", line):
         return result
-    if line.endswith(
-            " nocheck"):  # A // nocheck comment will bypass this error.
+    # A // nocheck comment will bypass this error.
+    if line.endswith(" nocheck"):
         return result
 
     matched = False
-    if type_name[0:1] == '/':
-        regex = type_name[1:]
+    if ban_rule.pattern[0:1] == '/':
+        regex = ban_rule.pattern[1:]
         if input_api.re.search(regex, line):
             matched = True
-    elif type_name in line:
+    elif ban_rule.pattern in line:
         matched = True
 
     if matched:
         result.append('    %s:%d:' % (affected_file.LocalPath(), line_number))
-        for message_line in message:
-            result.append('      %s' % message_line)
+        for line in ban_rule.explanation:
+            result.append('      %s' % line)
 
     return result
 
@@ -1614,6 +1619,9 @@
     errors = []
 
     def IsExcludedFile(affected_file, excluded_paths):
+        if not excluded_paths:
+            return False
+
         local_path = affected_file.LocalPath()
         for item in excluded_paths:
             if input_api.re.match(item, local_path):
@@ -1633,12 +1641,15 @@
                 return True
         return False
 
-    def CheckForMatch(affected_file, line_num, line, func_name, message,
-                      error):
+    def CheckForMatch(affected_file, line_num: int, line: str,
+                      ban_rule: BanRule):
+        if IsExcludedFile(affected_file, ban_rule.excluded_paths):
+            return
+
         problems = _GetMessageForMatchingType(input_api, f, line_num, line,
-                                              func_name, message)
+                                              ban_rule)
         if problems:
-            if error:
+            if ban_rule.treat_as_error is not None and ban_rule.treat_as_error:
                 errors.extend(problems)
             else:
                 warnings.extend(problems)
@@ -1646,33 +1657,31 @@
     file_filter = lambda f: f.LocalPath().endswith(('.java'))
     for f in input_api.AffectedFiles(file_filter=file_filter):
         for line_num, line in f.ChangedContents():
-            for func_name, message, error in _BANNED_JAVA_FUNCTIONS:
-                CheckForMatch(f, line_num, line, func_name, message, error)
+            for ban_rule in _BANNED_JAVA_FUNCTIONS:
+                CheckForMatch(f, line_num, line, ban_rule)
 
     file_filter = lambda f: f.LocalPath().endswith(('.mm', '.m', '.h'))
     for f in input_api.AffectedFiles(file_filter=file_filter):
         for line_num, line in f.ChangedContents():
-            for func_name, message, error in _BANNED_OBJC_FUNCTIONS:
-                CheckForMatch(f, line_num, line, func_name, message, error)
+            for ban_rule in _BANNED_OBJC_FUNCTIONS:
+                CheckForMatch(f, line_num, line, ban_rule)
 
     for f in input_api.AffectedFiles(file_filter=IsIosObjcFile):
         for line_num, line in f.ChangedContents():
-            for func_name, message, error in _BANNED_IOS_OBJC_FUNCTIONS:
-                CheckForMatch(f, line_num, line, func_name, message, error)
+            for ban_rule in _BANNED_IOS_OBJC_FUNCTIONS:
+                CheckForMatch(f, line_num, line, ban_rule)
 
     egtest_filter = lambda f: f.LocalPath().endswith(('_egtest.mm'))
     for f in input_api.AffectedFiles(file_filter=egtest_filter):
         for line_num, line in f.ChangedContents():
-            for func_name, message, error in _BANNED_IOS_EGTEST_FUNCTIONS:
-                CheckForMatch(f, line_num, line, func_name, message, error)
+            for ban_rule in _BANNED_IOS_EGTEST_FUNCTIONS:
+                CheckForMatch(f, line_num, line, ban_rule)
 
     file_filter = lambda f: f.LocalPath().endswith(('.cc', '.mm', '.h'))
     for f in input_api.AffectedFiles(file_filter=file_filter):
         for line_num, line in f.ChangedContents():
-            for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
-                if IsExcludedFile(f, excluded_paths):
-                    continue
-                CheckForMatch(f, line_num, line, func_name, message, error)
+            for ban_rule in _BANNED_CPP_FUNCTIONS:
+                CheckForMatch(f, line_num, line, ban_rule)
 
     result = []
     if (warnings):
@@ -1690,22 +1699,16 @@
     """Make sure that banned java imports are not used."""
     errors = []
 
-    def IsException(path, exceptions):
-        for exception in exceptions:
-            if (path.startswith(exception)):
-                return True
-        return False
-
     file_filter = lambda f: f.LocalPath().endswith(('.java'))
     for f in input_api.AffectedFiles(file_filter=file_filter):
         for line_num, line in f.ChangedContents():
-            for import_name, message, exceptions in _BANNED_JAVA_IMPORTS:
-                if IsException(f.LocalPath(), exceptions):
-                    continue
+            for ban_rule in _BANNED_JAVA_IMPORTS:
+                # Consider merging this into the above function. There is no
+                # real difference anymore other than helping with a little
+                # bit of boilerplate text. Doing so means things like
+                # `treat_as_error` will also be uniformly handled.
                 problems = _GetMessageForMatchingType(input_api, f, line_num,
-                                                      line,
-                                                      'import ' + import_name,
-                                                      message)
+                                                      line, ban_rule)
                 if problems:
                     errors.extend(problems)
     result = []
@@ -1733,9 +1736,9 @@
             continue
 
         for line_num, line in f.ChangedContents():
-            for func_name, message in _DEPRECATED_MOJO_TYPES:
+            for ban_rule in _DEPRECATED_MOJO_TYPES:
                 problems = _GetMessageForMatchingType(input_api, f, line_num,
-                                                      line, func_name, message)
+                                                      line, ban_rule)
 
                 if problems:
                     # Raise errors inside |error_paths| and warnings everywhere else.