Pārlūkot izejas kodu

[cpplint] add nolint region support

Both the internal and community maintained cpplint support regional
nolint annotation NOLINTBEGIN/NOLINTEND.

Bug: 409733462
Change-Id: If738e8f6b8b30e88adec74383fecd8198fe70fbd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/6446042
Auto-Submit: Tommy Chiang <ototot@google.com>
Reviewed-by: Yiwei Zhang <yiwzhang@google.com>
Commit-Queue: Tommy Chiang <ototot@google.com>
Tommy Chiang 4 mēneši atpakaļ
vecāks
revīzija
f088ff9f1b
1 mainītis faili ar 71 papildinājumiem un 22 dzēšanām
  1. 71 22
      cpplint.py

+ 71 - 22
cpplint.py

@@ -70,6 +70,9 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
   'NOLINT(category)' comment to the line.  NOLINT or NOLINT(*)
   suppresses errors of all categories on that line.
 
+  To suppress false-positive errors for a block of lines, enclose the
+  block with 'NOLINTBEGIN(category)' and 'NOLINTEND' comment lines.
+
   The files passed in will be linted; at least one file must be provided.
   Default linted extensions are .cc, .cpp, .cu, .cuh and .h.  Change the
   extensions with the --extensions flag.
@@ -785,7 +788,29 @@ _valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])
 _global_error_suppressions = {}
 
 
-def ParseNolintSuppressions(filename, raw_line, linenum, error):
+def AddSuppression(suppressed_line, category):
+    """Update global error_suppressions for a single line.
+
+  Args:
+    suppressed_line: int, line number where suppression is to be added.
+    category: str, category to suppress, or None to suppress all.
+  """
+    if category:
+        if category.startswith('(') and category.endswith(')'):
+            category = category[1:-1]
+        if category == '*':
+            category = None  # "(*)" -> Suppress all
+
+    if category is None or category in _ERROR_CATEGORIES:
+        _error_suppressions.setdefault(category, set()).add(suppressed_line)
+    else:
+        # Unknown category.  We used to track these in an _LEGACY_ERROR_CATEGORIES
+        # list, but these days we just silently ignore them since they may be
+        # intended for ClangTidy.
+        pass
+
+
+def ParseNolintSuppressions(filename, raw_lines, linenum, error):
     """Updates the global list of line error-suppressions.
 
     Parses any NOLINT comments on the current line, updating the global
@@ -794,28 +819,54 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error):
 
     Args:
         filename: str, the name of the input file.
-        raw_line: str, the line of input text, with comments.
+        raw_lines: List[str], list of input lines, with comments.
         linenum: int, the number of the current line.
         error: function, an error handler.
     """
-    matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line)
+    matched = Search(r'\bNOLINT(NEXTLINE|BEGIN)?\b(\([^)]+\))?',
+                     raw_lines[linenum])
     if matched:
-        if matched.group(1):
-            suppressed_line = linenum + 1
-        else:
-            suppressed_line = linenum
+        annotation_type = matched.group(1)
         category = matched.group(2)
-        if category in (None, '(*)'):  # => "suppress all"
-            _error_suppressions.setdefault(None, set()).add(suppressed_line)
-        else:
-            if category.startswith('(') and category.endswith(')'):
-                category = category[1:-1]
-                if category in _ERROR_CATEGORIES:
-                    _error_suppressions.setdefault(category,
-                                                   set()).add(suppressed_line)
-                elif category not in _LEGACY_ERROR_CATEGORIES:
+        if annotation_type:
+            # Suppressed line(s) are not the current line.
+            if annotation_type == 'NEXTLINE':
+                # Silence next line only.
+                AddSuppression(linenum + 1, category)
+            else:
+                # Silence a block of lines.
+                #
+                # Note that multiple NOLINTBEGIN lines may be terminated by a
+                # single NOLINTEND line:
+                #
+                #   // NOLINTBEGIN(some-category)
+                #   // NOLINTBEGIN(different-category)
+                #   ...
+                #   // NOLINTEND  <- ends both silences
+                #
+                # Also note that because we only consume one single NOLINT
+                # annotation per line, in order to silence multiple categories
+                # of warnings, the above example would be the preferred way to
+                # do it.  This is actually an improvement over historical
+                # handling of single line NOLINT annotations, where the choices
+                # were to silence 0 or 1 or all categories on a single line.
+                lastline = None
+                for i in range(linenum + 1, len(raw_lines)):
+                    if Search(r'\bNOLINTEND\b', raw_lines[i]):
+                        lastline = i
+                        break
+                if not lastline:
+                    # Because multiple NOLINTBEGIN may be terminated by a single
+                    # NOLINTEND, the error message here deliberately avoids
+                    # something like "Unmatched NOLINTEND".
                     error(filename, linenum, 'readability/nolint', 5,
-                          'Unknown NOLINT error category: %s' % category)
+                          'Missing NOLINTEND')
+                else:
+                    for i in range(linenum + 1, lastline):
+                        AddSuppression(i, category)
+        else:
+            # Suppressing errors on current line.
+            AddSuppression(linenum, category)
 
 
 def ProcessGlobalSuppresions(lines):
@@ -2158,14 +2209,12 @@ def CheckForHeaderGuard(filename, clean_lines, error):
         if ifndef != cppvar + '_':
             error_level = 5
 
-        ParseNolintSuppressions(filename, raw_lines[ifndef_linenum],
-                                ifndef_linenum, error)
+        ParseNolintSuppressions(filename, raw_lines, ifndef_linenum, error)
         error(filename, ifndef_linenum, 'build/header_guard', error_level,
               '#ifndef header guard has wrong style, please use: %s' % cppvar)
 
     # Check for "//" comments on endif line.
-    ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum,
-                            error)
+    ParseNolintSuppressions(filename, raw_lines, endif_linenum, error)
     match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif)
     if match:
         if match.group(1) == '_':
@@ -6137,7 +6186,7 @@ def ProcessLine(filename,
             clean_lines, line, error
     """
     raw_lines = clean_lines.raw_lines
-    ParseNolintSuppressions(filename, raw_lines[line], line, error)
+    ParseNolintSuppressions(filename, raw_lines, line, error)
     nesting_state.Update(filename, clean_lines, line, error)
     CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line,
                                  error)