[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / win / new_analyze_warnings / warning_diff.py
blob1eeb9234a6fb86414d8630bfa8c208fb91c59d4f
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """
5 This script takes two warning summary files and reports on the new warnings
6 and the fixed warnings. The warning summaries are created by
7 warnings_by_type.py.
9 A warning is identified by the source file and the warning text, without the
10 Lines component or any line 'xxx' references within the warning.
11 All warnings with the same signature are grouped together (duplicates are
12 assumed to have been removed already).
14 If a file contains multiple warnings with the same signature then a report
15 will be generated for each warning when the warning count changes.
16 """
18 import sys
19 import re
21 # Some sample warnings:
22 # sctp_bsd_addr.c(182) : warning C28125: The function
23 # 'InitializeCriticalSection' must be called from within a try/except block:
24 # The requirement might be conditional.
25 # exception_handler.cc(813) : warning C6387: 'child_thread_handle' could be '0':
26 # this does not adhere to the specification for the function 'CloseHandle'. :
27 # Lines: 773, 774, 775, 776, 777, 784, 802, 804, 809, 813
28 # unistr.cpp(1823) : warning C28193: 'temporary value' holds a value that must
29 # be examined.: Lines: 1823, 1824
30 # Note "line '1428'" in this warning, and 'count went from 3 to 2':
31 # scheduler.cc(1452) : warning C6246: Local declaration of 'node' hides
32 # declaration of the same name in outer scope. For additional information, see
33 # previous declaration at line '1428' of 'scheduler.cc'.: count went from 3 to 2
34 # Note "line 454" in this warning:
35 # gurl.cc(449) : warning C28112: A variable (empty_gurl) which is accessed via
36 # an Interlocked function must always be accessed via an Interlocked function.
37 # See line 454: It is not always safe to access a variable which is accessed
38 # via the Interlocked* family of functions in any other way.
41 warningsToIgnore = [
42 # We assume that memory allocations never fail
43 "C6255", # _alloca indicates failure by raising a stack overflow exception.
44 "C6308", # 'realloc' might return null pointer, leaking memory
45 "C6387", # Param could be '0': this does not adhere to the specification for
46 # the function
47 # I have yet to see errors caused by passing 'char' to isspace and friends
48 "C6330", # 'char' passed as _Param_(1) when 'unsigned char' is required
49 # This warning needs to be in clang to make it effective
50 "C6262", # Function uses too much stack
51 # Triggers on isnan, isinf, and template metaprogramming.
52 "C6334", # sizeof operator applied to an expression with an operator might
53 # yield unexpected results:
56 warningRe = re.compile(r"(.*)\(\d+\) : warning (C\d{4,5}): (.*)")
57 warningRefLine = re.compile(r"(.*line ')\d+('.*)")
58 warningRefLine2 = re.compile(r"(.*line )\d+(:.*)")
60 def RemoveExtraneous(line):
61 """
62 Remove extraneous data such as the optional 'Lines:' block at the end of some
63 warnings, and line ending characters.
64 This ensures better matching and makes for less cluttered results.
65 """
66 linesOffset = line.find(": Lines:")
67 if linesOffset >= 0:
68 line = line[:linesOffset]
69 return line.strip()
73 def SummarizeWarnings(filename):
74 """
75 This function reads the file and looks for warning messages. It creates a
76 dictionary with the keys being the filename, warning number, and warning text,
77 and returns this.
78 The warning summary at the end is ignored because it doesn't match the regex
79 due to the 'C' being stripped from the warnings, for just this purpose.
80 """
81 warnings = {}
82 for line in open(filename).readlines():
83 line = line.replace(r"\chromium\src", r"\analyze_chromium\src")
84 line = line.replace(r"\chromium2\src", r"\analyze_chromium\src")
85 line = RemoveExtraneous(line)
86 match = warningRe.match(line)
87 if match:
88 file, warningNumber, description = match.groups()
89 ignore = False
90 if warningNumber in warningsToIgnore:
91 ignore = True
92 glesTest = "gles2_implementation_unittest"
93 if warningNumber == "C6001" and line.count(glesTest) > 0:
94 ignore = True # Many spurious warnings of this form
95 if not ignore:
96 # See if the description contains line numbers, so that we can
97 # remove them.
98 matchLine = warningRefLine.match(description)
99 if not matchLine:
100 matchLine = warningRefLine2.match(description)
101 if matchLine:
102 # Replace referenced line numbers with #undef so that they don't cause
103 # mismatches.
104 description = "#undef".join(matchLine.groups())
105 # Look for "the readable size is " and "the writable size is " because
106 # these are often followed by sizes that vary in uninteresting ways,
107 # especially between 32-bit and 64-bit builds.
108 readableText = "the readable size is "
109 writableText = "the writable size is "
110 if description.find(readableText) >= 0:
111 description = description[:description.find(readableText)]
112 if description.find(writableText) >= 0:
113 description = description[:description.find(writableText)]
115 key = (file, warningNumber, description)
116 if not key in warnings:
117 warnings[key] = []
118 warnings[key].append(line.strip())
119 return warnings
123 def PrintAdditions(oldResults, newResults, message, invert):
124 results = []
125 for key in newResults.keys():
126 if oldResults.has_key(key):
127 # Check to see if the warning count has changed
128 old = oldResults[key]
129 new = newResults[key]
130 if len(new) > len(old):
131 # If the warning count has increased then we don't know which ones are
132 # new. Sigh... Report the new ones, up to some maximum:
133 for warning in newResults[key]:
134 if invert:
135 results.append(warning + ": count went from %d to %d" % \
136 (len(newResults[key]), len(oldResults[key])))
137 else:
138 results.append(warning + ": count went from %d to %d" % \
139 (len(oldResults[key]), len(newResults[key])))
140 else:
141 # Totally new (or fixed) warning.
142 results += newResults[key]
143 # This sort is not perfect because it is alphabetic and it needs to switch to
144 # numeric when it encounters digits. Later.
145 results.sort()
146 print "%s (%d total)" % (message, len(results))
147 for line in results:
148 print line
152 if len(sys.argv) < 3:
153 print "Usage: %s oldsummary.txt newsummary.txt" % sys.argv[0]
154 print "Prints the changes in warnings between two /analyze runs."
155 sys.exit(0)
157 oldFilename = sys.argv[1]
158 newFilename = sys.argv[2]
159 oldResults = SummarizeWarnings(oldFilename)
160 newResults = SummarizeWarnings(newFilename)
162 PrintAdditions(oldResults, newResults, "New warnings", False)
163 print
164 print
165 PrintAdditions(newResults, oldResults, "Fixed warnings", True)