1effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# Copyright 2014 The Chromium Authors. All rights reserved. 2effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# Use of this source code is governed by a BSD-style license that can be 3effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch# found in the LICENSE file. 4effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 5effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochclass StrictEnumValueChecker(object): 6effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Verify that changes to enums are valid. 7effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 8effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch This class is used to check enums where reordering or deletion is not allowed, 9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch and additions must be at the end of the enum, just prior to some "boundary" 10effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch entry. See comments at the top of the "extension_function_histogram_value.h" 11effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch file in chrome/browser/extensions for an example what are considered valid 12effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch changes. There are situations where this class gives false positive warnings, 13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch i.e. it warns even though the edit is legitimate. Since the class warns using 14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch prompt warnings, the user can always choose to continue. The main point is to 15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch attract the attention to all (potentially or not) invalid edits. 16effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """ 18effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def __init__(self, input_api, output_api, start_marker, end_marker, path): 19effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.input_api = input_api 20effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.output_api = output_api 21effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.start_marker = start_marker 22effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.end_marker = end_marker 23effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.path = path 24effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.results = [] 25effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 26effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch class EnumRange(object): 27effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Represents a range of line numbers (1-based)""" 28effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def __init__(self, first_line, last_line): 29effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.first_line = first_line 30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.last_line = last_line 31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Count(self): 33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self.last_line - self.first_line + 1 34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Contains(self, line_num): 36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self.first_line <= line_num and line_num <= self.last_line 37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def LogInfo(self, message): 39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.input_api.logging.info(message) 40effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return 41effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def LogDebug(self, message): 43effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.input_api.logging.debug(message) 44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return 45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def ComputeEnumRangeInContents(self, contents): 47effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Returns an |EnumRange| object representing the line extent of the 48effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch enum members in |contents|. The line numbers are 1-based, 49effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch compatible with line numbers returned by AffectedFile.ChangeContents(). 50effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch |contents| is a list of strings reprenting the lines of a text file. 51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 52effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch If either start_marker or end_marker cannot be found in 53effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch |contents|, returns None and emits detailed warnings about the problem. 54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """ 56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch first_enum_line = 0 57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch last_enum_line = 0 58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_num = 1 # Line numbers are 1-based 59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for line in contents: 60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if line.startswith(self.start_marker): 61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch first_enum_line = line_num + 1 62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch elif line.startswith(self.end_marker): 63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch last_enum_line = line_num 64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_num += 1 65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if first_enum_line == 0: 67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.EmitWarning("The presubmit script could not find the start of the " 68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "enum definition (\"%s\"). Did the enum definition " 69effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "change?" % self.start_marker) 70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return None 71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if last_enum_line == 0: 73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.EmitWarning("The presubmit script could not find the end of the " 74effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "enum definition (\"%s\"). Did the enum definition " 75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "change?" % self.end_marker) 76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return None 77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 78effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if first_enum_line >= last_enum_line: 79effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.EmitWarning("The presubmit script located the start of the enum " 80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "definition (\"%s\" at line %d) *after* its end " 81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "(\"%s\" at line %d). Something is not quite right." 82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch % (self.start_marker, first_enum_line, 83effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.end_marker, last_enum_line)) 84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return None 85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.LogInfo("Line extent of (\"%s\") enum definition: " 87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "first_line=%d, last_line=%d." 88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch % (self.start_marker, first_enum_line, last_enum_line)) 89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self.EnumRange(first_enum_line, last_enum_line) 90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def ComputeEnumRangeInNewFile(self, affected_file): 92effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self.ComputeEnumRangeInContents(affected_file.NewContents()) 93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def GetLongMessage(self, local_path): 95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return str("The file \"%s\" contains the definition of the " 96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "(\"%s\") enum which should be edited in specific ways " 97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "only - *** read the comments at the top of the header file ***" 98effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch ". There are changes to the file that may be incorrect and " 99effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "warrant manual confirmation after review. Note that this " 100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "presubmit script can not reliably report the nature of all " 101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "types of invalid changes, especially when the diffs are " 102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "complex. For example, an invalid deletion may be reported " 103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "whereas the change contains a valid rename." 104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch % (local_path, self.start_marker)) 105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def EmitWarning(self, message, line_number=None, line_text=None): 107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Emits a presubmit prompt warning containing the short message 108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch |message|. |item| is |LOCAL_PATH| with optional |line_number| and 109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch |line_text|. 110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """ 112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if line_number is not None and line_text is not None: 113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch item = "%s(%d): %s" % (self.path, line_number, line_text) 114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch elif line_number is not None: 115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch item = "%s(%d)" % (self.path, line_number) 116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch else: 117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch item = self.path 118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch long_message = self.GetLongMessage(self.path) 119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.LogInfo(message) 120effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.results.append( 121effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.output_api.PresubmitPromptWarning(message, [item], long_message)) 122effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def CollectRangesInsideEnumDefinition(self, affected_file, 124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch first_line, last_line): 125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Returns a list of triplet (line_start, line_end, line_text) of ranges of 126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch edits changes. The |line_text| part is the text at line |line_start|. 127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch Since it used only for reporting purposes, we do not need all the text 128effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch lines in the range. 129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """ 131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch results = [] 132effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_line_number = 0 133effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_line_number = 0 134effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_text = "" 135effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 136effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def addRange(): 137effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch tuple = (previous_range_start_line_number, 138effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_line_number, 139effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_text) 140effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch results.append(tuple) 141effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 142effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for line_number, line_text in affected_file.ChangedContents(): 143effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if first_line <= line_number and line_number <= last_line: 144effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.LogDebug("Line change at line number " + str(line_number) + ": " + 145effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_text) 146effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Start a new interval if none started 147effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if previous_range_start_line_number == 0: 148effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_line_number = line_number 149effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_text = line_text 150effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Add new interval if we reached past the previous one 151effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch elif line_number != previous_line_number + 1: 152effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch addRange() 153effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_line_number = line_number 154effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_range_start_text = line_text 155effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch previous_line_number = line_number 156effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 157effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Add a last interval if needed 158effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if previous_range_start_line_number != 0: 159effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch addRange() 160effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return results 161effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 162effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def CheckForFileDeletion(self, affected_file): 163effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Emits a warning notification if file has been deleted """ 164effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not affected_file.NewContents(): 165effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.EmitWarning("The file seems to be deleted in the changelist. If " 166effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "your intent is to really delete the file, the code in " 167effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "PRESUBMIT.py should be updated to remove the " 168effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "|StrictEnumValueChecker| class."); 169effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return False 170effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return True 171effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 172effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def GetDeletedLinesFromScmDiff(self, affected_file): 173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Return a list of of line numbers (1-based) corresponding to lines 174effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch deleted from the new source file (if they had been present in it). Note 175effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch that if multiple contiguous lines have been deleted, the returned list will 176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch contain contiguous line number entries. To prevent false positives, we 177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return deleted line numbers *only* from diff chunks which decrease the size 178effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch of the new file. 179effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch Note: We need this method because we have access to neither the old file 181effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch content nor the list of "delete" changes from the current presubmit script 182effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch API. 183effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 184effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """ 185effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch results = [] 186effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_num = 0 187effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch deleting_lines = False 188effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for line in affected_file.GenerateScmDiff().splitlines(): 189effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Parse the unified diff chunk optional section heading, which looks like 190effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # @@ -l,s +l,s @@ optional section heading 191effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch m = self.input_api.re.match( 192effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch r"^@@ \-([0-9]+)\,([0-9]+) \+([0-9]+)\,([0-9]+) @@", line) 193effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if m: 194effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch old_line_num = int(m.group(1)) 195effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch old_size = int(m.group(2)) 196effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch new_line_num = int(m.group(3)) 197effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch new_size = int(m.group(4)) 198effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_num = new_line_num 199effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Return line numbers only from diff chunks decreasing the size of the 200effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # new file 201effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch deleting_lines = old_size > new_size 202effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch continue 203effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not line.startswith("-"): 204effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_num += 1 205effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if deleting_lines and line.startswith("-") and not line.startswith("--"): 206effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch results.append(line_num) 207effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return results 208effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 209effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def CheckForEnumEntryDeletions(self, affected_file): 210effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """Look for deletions inside the enum definition. We currently use a 211effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch simple heuristics (not 100% accurate): if there are deleted lines inside 212effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch the enum definition, this might be a deletion. 213effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 214effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch """ 215effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch range_new = self.ComputeEnumRangeInNewFile(affected_file) 216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not range_new: 217effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return False 218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch is_ok = True 220effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for line_num in self.GetDeletedLinesFromScmDiff(affected_file): 221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if range_new.Contains(line_num): 222effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.EmitWarning("It looks like you are deleting line(s) from the " 223effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "enum definition. This should never happen.", 224effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_num) 225effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch is_ok = False 226effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return is_ok 227effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 228effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def CheckForEnumEntryInsertions(self, affected_file): 229effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch range = self.ComputeEnumRangeInNewFile(affected_file) 230effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not range: 231effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return False 232effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 233effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch first_line = range.first_line 234effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch last_line = range.last_line 235effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 236effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # Collect the range of changes inside the enum definition range. 237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch is_ok = True 238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for line_start, line_end, line_text in \ 239effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.CollectRangesInsideEnumDefinition(affected_file, 240effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch first_line, 241effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch last_line): 242effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # The only edit we consider valid is adding 1 or more entries *exactly* 243effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # at the end of the enum definition. Every other edit inside the enum 244effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # definition will result in a "warning confirmation" message. 245effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # 246effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # TODO(rpaquay): We currently cannot detect "renames" of existing entries 247effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch # vs invalid insertions, so we sometimes will warn for valid edits. 248effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch is_valid_edit = (line_end == last_line - 1) 249effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 250effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.LogDebug("Edit range in new file at starting at line number %d and " 251effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "ending at line number %d: valid=%s" 252effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch % (line_start, line_end, is_valid_edit)) 253effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 254effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not is_valid_edit: 255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.EmitWarning("The change starting at line %d and ending at line " 256effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "%d is *not* located *exactly* at the end of the " 257effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "enum definition. Unless you are renaming an " 258effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "existing entry, this is not a valid change, as new " 259effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "entries should *always* be added at the end of the " 260effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "enum definition, right before the \"%s\" " 261effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "entry." % (line_start, line_end, self.end_marker), 262effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_start, 263effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch line_text) 264effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch is_ok = False 265effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return is_ok 266effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 267effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def PerformChecks(self, affected_file): 268effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not self.CheckForFileDeletion(affected_file): 269effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return 270effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not self.CheckForEnumEntryDeletions(affected_file): 271effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return 272effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if not self.CheckForEnumEntryInsertions(affected_file): 273effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return 274effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 275effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def ProcessHistogramValueFile(self, affected_file): 276effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.LogInfo("Start processing file \"%s\"" % affected_file.LocalPath()) 277effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.PerformChecks(affected_file) 278effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.LogInfo("Done processing file \"%s\"" % affected_file.LocalPath()) 279effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 280effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch def Run(self): 281effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for file in self.input_api.AffectedFiles(include_deletes=True): 282effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if file.LocalPath() == self.path: 283effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch self.ProcessHistogramValueFile(file) 284effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return self.results 285