diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index b95248a7380fa..52e46bf169bf1 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -144,6 +144,9 @@ class BanRule:
     # 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
+    # If True, surfaces any violation as a Gerrit comment on the CL after
+    # running the CQ.
+    surface_as_gerrit_lint: Optional[bool] = None
 
 
 _BANNED_JAVA_IMPORTS : Sequence[BanRule] = (
@@ -316,6 +319,7 @@ _BANNED_JAVA_FUNCTIONS : Sequence[BanRule] = (
           r'^infra/', # This is permitted in infra/ folder.
           r'^tools/', # This is permitted in tools/ folder.
         ],
+        surface_as_gerrit_lint=True,
     ),
 )
 
@@ -2227,6 +2231,7 @@ _BANNED_CPP_FUNCTIONS: Sequence[BanRule] = (
           r'^infra/', # This is permitted in infra/ folder.
           r'^tools/', # This is permitted in tools/ folder.
         ],
+        surface_as_gerrit_lint=True,
     ),
 )
 
@@ -2996,8 +3001,7 @@ def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
 
 def CheckNoBannedFunctions(input_api, output_api):
     """Make sure that banned functions are not used."""
-    warnings = []
-    errors = []
+    results= []
 
     def IsExcludedFile(affected_file, excluded_paths):
         if not excluded_paths:
@@ -3027,13 +3031,27 @@ def CheckNoBannedFunctions(input_api, output_api):
         if IsExcludedFile(affected_file, ban_rule.excluded_paths):
             return
 
-        problems = _GetMessageForMatchingType(input_api, f, line_num, line,
-                                              ban_rule)
-        if problems:
+        message = _GetMessageForMatchingType(input_api, f, line_num, line,
+                                             ban_rule)
+        if message:
+            result_loc = []
+            if ban_rule.surface_as_gerrit_lint:
+                result_loc.append(output_api.PresubmitResultLocation(
+                    file_path=affected_file.LocalPath(),
+                    start_line=line_num,
+                    end_line=line_num,
+                ))
             if ban_rule.treat_as_error is not None and ban_rule.treat_as_error:
-                errors.extend(problems)
+                results.append(
+                    output_api.PresubmitError('A banned function was used.\n' +
+                                              '\n'.join(message),
+                                              locations=result_loc))
+
             else:
-                warnings.extend(problems)
+                results.append(
+                    output_api.PresubmitPromptWarning('A banned function was used.\n' +
+                                                      '\n'.join(message),
+                                                      locations=result_loc))
 
     file_filter = lambda f: f.LocalPath().endswith(('.java'))
     for f in input_api.AffectedFiles(file_filter=file_filter):
@@ -3096,17 +3114,8 @@ def CheckNoBannedFunctions(input_api, output_api):
             for ban_rule in _BANNED_MOJOM_PATTERNS:
                 CheckForMatch(f, line_num, line, ban_rule)
 
+    return results
 
-    result = []
-    if (warnings):
-        result.append(
-            output_api.PresubmitPromptWarning('Banned functions were used.\n' +
-                                              '\n'.join(warnings)))
-    if (errors):
-        result.append(
-            output_api.PresubmitError('Banned functions were used.\n' +
-                                      '\n'.join(errors)))
-    return result
 
 def _CheckAndroidNoBannedImports(input_api, output_api):
     """Make sure that banned java imports are not used."""
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 364fdba1c33a1..6fa5eb342765e 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -2979,35 +2979,34 @@ class BannedTypeCheckTest(unittest.TestCase):
         ]
 
         errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
-        self.assertEqual(2, len(errors))
+        self.assertEqual(12, len(errors))
         self.assertTrue(
             'some/java/problematic/diskread.java' in errors[0].message)
         self.assertTrue(
-            'some/java/problematic/diskwrite.java' in errors[0].message)
-        self.assertFalse('some/java/ok/diskwrite.java' in errors[0].message)
-        self.assertFalse('some/java/ok/diskwrite.java' in errors[1].message)
+            'some/java/problematic/diskwrite.java' in errors[1].message)
+        self.assertTrue(all('some/java/ok/diskwrite.java' not in e.message for e in errors))
         self.assertTrue(
-            'some/java/problematic/waitidleforsync.java' in errors[0].message)
+            'some/java/problematic/waitidleforsync.java' in errors[2].message)
         self.assertTrue(
-            'some/java/problematic/registerreceiver.java' in errors[1].message)
+            'some/java/problematic/registerreceiver.java' in errors[3].message)
         self.assertTrue(
-            'some/java/problematic/property.java' in errors[0].message)
+            'some/java/problematic/property.java' in errors[4].message)
         self.assertTrue(
-            'some/java/problematic/requestlayout.java' in errors[0].message)
+            'some/java/problematic/requestlayout.java' in errors[5].message)
         self.assertTrue(
-            'some/java/problematic/lastprofile.java' in errors[0].message)
+            'some/java/problematic/lastprofile.java' in errors[6].message)
         self.assertTrue(
-            'some/java/problematic/getdrawable1.java' in errors[0].message)
+            'some/java/problematic/getdrawable1.java' in errors[7].message)
         self.assertTrue(
-            'some/java/problematic/getdrawable2.java' in errors[0].message)
+            'some/java/problematic/getdrawable2.java' in errors[8].message)
         self.assertTrue('some/java/problematic/announceForAccessibility.java'
-                        in errors[0].message)
+                        in errors[9].message)
         self.assertTrue(
             'some/java/problematic/accessibilityTypeAnnouncement.java' in
-            errors[0].message)
+            errors[10].message)
         self.assertTrue(
             'content/java/problematic/desktopandroid.java' in
-            errors[0].message)
+            errors[11].message)
 
 
     def testBannedCppFunctions(self):
@@ -3035,29 +3034,36 @@ class BannedTypeCheckTest(unittest.TestCase):
                      ['std::ranges::subrange(first, last)']),
             MockFile('views_usage.cc', ['std::views::all(vec)']),
             MockFile('content/desktop_android.cc',
-                     ['#if BUILDFLAG(IS_DESKTOP_ANDROID)']),
+                     [
+                         '// some first line',
+                         '#if BUILDFLAG(IS_DESKTOP_ANDROID)',
+                         '// some third line',
+                     ]),
         ]
 
         results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
 
-        # warnings are results[0], errors are results[1]
-        self.assertEqual(2, len(results))
-        self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
+        # Each entry in results corresponds to a BanRule with a violation, in
+        # the order they were encountered.
+        self.assertEqual(8, len(results))
+        self.assertTrue('some/cpp/problematic/file.cc' in results[0].message)
         self.assertTrue(
-            'third_party/blink/problematic/file.cc' in results[0].message)
-        self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
-        self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
-        self.assertTrue('some/cpp/problematic/file3.cc' in results[0].message)
-        self.assertTrue('some/cpp/problematic/file4.cc' in results[0].message)
-        self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
-        self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
-        self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
-        self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
-        self.assertFalse('allowed_ranges_usage.cc' in results[0].message)
-        self.assertFalse('allowed_ranges_usage.cc' in results[1].message)
-        self.assertTrue('banned_ranges_usage.cc' in results[1].message)
-        self.assertTrue('views_usage.cc' in results[1].message)
-        self.assertTrue('content/desktop_android.cc' in results[0].message)
+            'third_party/blink/problematic/file.cc' in results[1].message)
+        self.assertTrue(all('some/cpp/ok/file.cc' not in r.message for r in results))
+        self.assertTrue('some/cpp/problematic/file2.cc' in results[2].message)
+        self.assertTrue('some/cpp/problematic/file3.cc' in results[3].message)
+        self.assertTrue('some/cpp/problematic/file4.cc' in results[4].message)
+        self.assertTrue(all('some/cpp/nocheck/file.cc' not in r.message for r in results))
+        self.assertTrue(all('some/cpp/comment/file.cc' not in r.message for r in results))
+        self.assertTrue(all('allowed_ranges_usage.cc' not in r.message for r in results))
+        self.assertTrue('banned_ranges_usage.cc' in results[5].message)
+        self.assertTrue('views_usage.cc' in results[6].message)
+        self.assertTrue('content/desktop_android.cc' in results[7].message)
+
+        # Check ResultLocation data. Line nums start at 1.
+        self.assertEqual(results[7].locations[0].file_path, 'content/desktop_android.cc')
+        self.assertEqual(results[7].locations[0].start_line, 2)
+        self.assertEqual(results[7].locations[0].end_line, 2)
 
     def testBannedCppRandomFunctions(self):
         banned_rngs = [
@@ -3092,15 +3098,14 @@ class BannedTypeCheckTest(unittest.TestCase):
             ]
             results = PRESUBMIT.CheckNoBannedFunctions(input_api,
                                                        MockOutputApi())
-            self.assertEqual(1, len(results), banned_rng)
+            self.assertEqual(2, len(results), banned_rng)
             self.assertTrue(
                 'some/cpp/problematic/file.cc' in results[0].message,
                 banned_rng)
             self.assertTrue(
-                'third_party/blink/problematic/file.cc' in results[0].message,
+                'third_party/blink/problematic/file.cc' in results[1].message,
                 banned_rng)
-            self.assertFalse('third_party/ok/file.cc' in results[0].message,
-                             banned_rng)
+            self.assertTrue(all('third_party/ok/file.cc' not in r.message for r in results))
 
     def testBannedIosObjcFunctions(self):
         input_api = MockInputApi()
@@ -3120,12 +3125,12 @@ class BannedTypeCheckTest(unittest.TestCase):
         ]
 
         errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
-        self.assertEqual(1, len(errors))
+        self.assertEqual(3, len(errors))
         self.assertTrue('some/ios/file.mm' in errors[0].message)
-        self.assertTrue('another/ios_file.mm' in errors[0].message)
-        self.assertTrue('some/mac/file.mm' not in errors[0].message)
-        self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
-        self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
+        self.assertTrue('another/ios_file.mm' in errors[1].message)
+        self.assertTrue(all('some/mac/file.mm' not in e.message for e in errors))
+        self.assertTrue('some/ios/file_egtest.mm' in errors[2].message)
+        self.assertTrue(all('some/ios/file_unittest.mm' not in e.message for e in errors))
 
     def testBannedMojoFunctions(self):
         input_api = MockInputApi()
@@ -3137,7 +3142,8 @@ class BannedTypeCheckTest(unittest.TestCase):
 
         results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
 
-        # warnings are results[0], errors are results[1]
+        # Each entry in results corresponds to a BanRule with a violation, in
+        # the order they were encountered.
         self.assertEqual(1, len(results))
         self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
         self.assertTrue(
@@ -3161,7 +3167,8 @@ class BannedTypeCheckTest(unittest.TestCase):
 
         results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
 
-        # warnings are results[0], errors are results[1]
+        # Each entry in results corresponds to a BanRule with a violation, in
+        # the order they were encountered.
         self.assertEqual(1, len(results))
         self.assertTrue('bad.mojom' in results[0].message)
         self.assertTrue('good.mojom' not in results[0].message)
@@ -3184,23 +3191,23 @@ class BannedTypeCheckTest(unittest.TestCase):
                      ['string extension_id']),
         ]
 
-        # warnings are results[0], errors are results[1]
+        # Each entry in results corresponds to a BanRule with a violation, in
+        # the order they were encountered.
         results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
 
-        # Only warnings and no errors.
-        self.assertEqual(1, len(results))
+        self.assertEqual(3, len(results))
 
         # Pattern test assertions.
         self.assertTrue('bad.mojom' in results[0].message)
-        self.assertTrue('bad_struct.mojom' in results[0].message)
-        self.assertTrue('good.mojom' not in results[0].message)
-        self.assertTrue('good_struct.mojom' not in results[0].message)
+        self.assertTrue('bad_struct.mojom' in results[1].message)
+        self.assertTrue(all('good.mojom' not in r.message for r in results))
+        self.assertTrue(all('good_struct.mojom' not in r.message for r in results))
 
         # Path exclusion assertions.
         self.assertTrue('some/included/extensions/path/bad_extension_id.mojom'
-                        in results[0].message)
-        self.assertTrue('some/excluded/path/bad_extension_id.mojom' not in
-                        results[0].message)
+                        in results[2].message)
+        self.assertTrue(all('some/excluded/path/bad_extension_id.mojom' not in r.message for r in results))
+
 
 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
 
@@ -5732,20 +5739,19 @@ class CheckDeprecatedSyncConsentFunctionsTest(unittest.TestCase):
 
         results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
 
-        self.assertEqual(1, len(results))
-        self.assertFalse(
-            'chrome/browser/android/file.cc' in results[0].message),
+        self.assertEqual(7, len(results))
+        self.assertTrue(all('chrome/browser/android/file.cc' not in r.message for r in results))
         self.assertTrue('chrome/android/file.cc' in results[0].message),
-        self.assertTrue('ios/file.mm' in results[0].message),
-        self.assertTrue('components/foo/ios/file.cc' in results[0].message),
+        self.assertTrue('ios/file.mm' in results[1].message),
+        self.assertTrue('components/foo/ios/file.cc' in results[2].message),
         self.assertTrue(
-            'components/foo/delegate_android.cc' in results[0].message),
+            'components/foo/delegate_android.cc' in results[3].message),
         self.assertTrue(
-            'components/foo/delegate_ios.cc' in results[0].message),
+            'components/foo/delegate_ios.cc' in results[4].message),
         self.assertTrue(
-            'components/foo/android_delegate.cc' in results[0].message),
+            'components/foo/android_delegate.cc' in results[5].message),
         self.assertTrue(
-            'components/foo/ios_delegate.cc' in results[0].message),
+            'components/foo/ios_delegate.cc' in results[6].message),
 
     def testCppNonMobilePlatformPath(self):
         input_api = MockInputApi()
@@ -5771,12 +5777,12 @@ class CheckDeprecatedSyncConsentFunctionsTest(unittest.TestCase):
 
         results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
 
-        self.assertEqual(1, len(results))
-        self.assertFalse('components/foo/file1.java' in results[0].message),
+        self.assertEqual(4, len(results))
+        self.assertTrue(all('components/foo/file1.java' not in r.message for r in results))
         self.assertTrue('components/foo/file2.java' in results[0].message),
-        self.assertTrue('chrome/foo/file3.java' in results[0].message),
-        self.assertTrue('chrome/foo/file4.java' in results[0].message),
-        self.assertTrue('chrome/foo/file5.java' in results[0].message),
+        self.assertTrue('chrome/foo/file3.java' in results[1].message),
+        self.assertTrue('chrome/foo/file4.java' in results[2].message),
+        self.assertTrue('chrome/foo/file5.java' in results[3].message),
 
 
 class CheckAnonymousNamespaceTest(unittest.TestCase):
diff --git a/PRESUBMIT_test_mocks.py b/PRESUBMIT_test_mocks.py
index 131a7777979c2..baca10648edcb 100644
--- a/PRESUBMIT_test_mocks.py
+++ b/PRESUBMIT_test_mocks.py
@@ -210,42 +210,46 @@ class MockOutputApi(object):
 
     class PresubmitResult(object):
 
-        def __init__(self, message, items=None, long_text=''):
+        def __init__(self, message, items=None, long_text='', locations=[]):
             self.message = message
             self.items = items
             self.long_text = long_text
+            self.locations = locations
 
         def __repr__(self):
             return self.message
 
     class PresubmitError(PresubmitResult):
 
-        def __init__(self, message, items=None, long_text=''):
-            MockOutputApi.PresubmitResult.__init__(self, message, items,
-                                                   long_text)
+        def __init__(self, *args, **kwargs):
+            MockOutputApi.PresubmitResult.__init__(self, *args, **kwargs)
             self.type = 'error'
 
     class PresubmitPromptWarning(PresubmitResult):
 
-        def __init__(self, message, items=None, long_text=''):
-            MockOutputApi.PresubmitResult.__init__(self, message, items,
-                                                   long_text)
+        def __init__(self, *args, **kwargs):
+            MockOutputApi.PresubmitResult.__init__(self, *args, **kwargs)
             self.type = 'warning'
 
     class PresubmitNotifyResult(PresubmitResult):
 
-        def __init__(self, message, items=None, long_text=''):
-            MockOutputApi.PresubmitResult.__init__(self, message, items,
-                                                   long_text)
+        def __init__(self, *args, **kwargs):
+            MockOutputApi.PresubmitResult.__init__(self, *args, **kwargs)
             self.type = 'notify'
 
     class PresubmitPromptOrNotify(PresubmitResult):
 
-        def __init__(self, message, items=None, long_text=''):
-            MockOutputApi.PresubmitResult.__init__(self, message, items,
-                                                   long_text)
+        def __init__(self, *args, **kwargs):
+            MockOutputApi.PresubmitResult.__init__(self, *args,  **kwargs)
             self.type = 'promptOrNotify'
 
+    class PresubmitResultLocation(object):
+
+        def __init__(self, file_path, start_line, end_line):
+            self.file_path = file_path
+            self.start_line = start_line
+            self.end_line = end_line
+
     def __init__(self):
         self.more_cc = []