Merge remote-tracking branch 'origin/swift-3.1-branch' into stable
* origin/swift-3.1-branch:
[analyzer][scan-build-py] subprocess output handling reviewed in clang module
[www] Add nullability questions to analyzer FAQ.
[www] Add analyzer FAQ about not releasing ivars in -dealloc.
[analyzer] Update alpha and potential checker documentation, esp. alpha.valist
diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py
index 0ed0aef..244c34b 100644
--- a/tools/scan-build-py/libscanbuild/analyze.py
+++ b/tools/scan-build-py/libscanbuild/analyze.py
@@ -269,6 +269,9 @@
""" Validation done by the parser itself, but semantic check still
needs to be done. This method is doing that. """
+ # Make plugins always a list. (It might be None when not specified.)
+ args.plugins = args.plugins if args.plugins else []
+
if args.help_checkers_verbose:
print_checkers(get_checkers(args.clang, args.plugins))
parser.exit()
diff --git a/tools/scan-build-py/libscanbuild/clang.py b/tools/scan-build-py/libscanbuild/clang.py
index 0c3454b..833e77d 100644
--- a/tools/scan-build-py/libscanbuild/clang.py
+++ b/tools/scan-build-py/libscanbuild/clang.py
@@ -15,142 +15,143 @@
__all__ = ['get_version', 'get_arguments', 'get_checkers']
+# regex for activated checker
+ACTIVE_CHECKER_PATTERN = re.compile(r'^-analyzer-checker=(.*)$')
-def get_version(cmd):
- """ Returns the compiler version as string. """
- lines = subprocess.check_output([cmd, '-v'], stderr=subprocess.STDOUT)
- return lines.decode('ascii').splitlines()[0]
+def get_version(clang):
+ """ Returns the compiler version as string.
+
+ :param clang: the compiler we are using
+ :return: the version string printed to stderr """
+
+ output = subprocess.check_output([clang, '-v'], stderr=subprocess.STDOUT)
+ return output.decode('utf-8').splitlines()[0]
def get_arguments(command, cwd):
""" Capture Clang invocation.
- This method returns the front-end invocation that would be executed as
- a result of the given driver invocation. """
-
- def lastline(stream):
- last = None
- for line in stream:
- last = line
- if last is None:
- raise Exception("output not found")
- return last
+ :param command: the compilation command
+ :param cwd: the current working directory
+ :return: the detailed front-end invocation command """
cmd = command[:]
cmd.insert(1, '-###')
logging.debug('exec command in %s: %s', cwd, ' '.join(cmd))
- child = subprocess.Popen(cmd,
- cwd=cwd,
- universal_newlines=True,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- line = lastline(child.stdout)
- child.stdout.close()
- child.wait()
- if child.returncode == 0:
- if re.search(r'clang(.*): error:', line):
- raise Exception(line)
- return decode(line)
- else:
- raise Exception(line)
+
+ output = subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT)
+ # The relevant information is in the last line of the output.
+ # Don't check if finding last line fails, would throw exception anyway.
+ last_line = output.decode('utf-8').splitlines()[-1]
+ if re.search(r'clang(.*): error:', last_line):
+ raise Exception(last_line)
+ return decode(last_line)
def get_active_checkers(clang, plugins):
- """ To get the default plugins we execute Clang to print how this
- compilation would be called.
+ """ Get the active checker list.
- For input file we specify stdin and pass only language information. """
+ :param clang: the compiler we are using
+ :param plugins: list of plugins which was requested by the user
+ :return: list of checker names which are active
- def checkers(language):
+ To get the default checkers we execute Clang to print how this
+ compilation would be called. And take out the enabled checker from the
+ arguments. For input file we specify stdin and pass only language
+ information. """
+
+ def get_active_checkers_for(language):
""" Returns a list of active checkers for the given language. """
- load = [elem
- for plugin in plugins
- for elem in ['-Xclang', '-load', '-Xclang', plugin]]
- cmd = [clang, '--analyze'] + load + ['-x', language, '-']
- pattern = re.compile(r'^-analyzer-checker=(.*)$')
- return [pattern.match(arg).group(1)
- for arg in get_arguments(cmd, '.') if pattern.match(arg)]
+ load_args = [arg
+ for plugin in plugins
+ for arg in ['-Xclang', '-load', '-Xclang', plugin]]
+ cmd = [clang, '--analyze'] + load_args + ['-x', language, '-']
+ return [ACTIVE_CHECKER_PATTERN.match(arg).group(1)
+ for arg in get_arguments(cmd, '.')
+ if ACTIVE_CHECKER_PATTERN.match(arg)]
result = set()
for language in ['c', 'c++', 'objective-c', 'objective-c++']:
- result.update(checkers(language))
- return result
+ result.update(get_active_checkers_for(language))
+ return frozenset(result)
+
+
+def is_active(checkers):
+ """ Returns a method, which classifies the checker active or not,
+ based on the received checker name list. """
+
+ def predicate(checker):
+ """ Returns True if the given checker is active. """
+
+ return any(pattern.match(checker) for pattern in predicate.patterns)
+
+ predicate.patterns = [re.compile(r'^' + a + r'(\.|$)') for a in checkers]
+ return predicate
+
+
+def parse_checkers(stream):
+ """ Parse clang -analyzer-checker-help output.
+
+ Below the line 'CHECKERS:' are there the name description pairs.
+ Many of them are in one line, but some long named checker has the
+ name and the description in separate lines.
+
+ The checker name is always prefixed with two space character. The
+ name contains no whitespaces. Then followed by newline (if it's
+ too long) or other space characters comes the description of the
+ checker. The description ends with a newline character.
+
+ :param stream: list of lines to parse
+ :return: generator of tuples
+
+ (<checker name>, <checker description>) """
+
+ lines = iter(stream)
+ # find checkers header
+ for line in lines:
+ if re.match(r'^CHECKERS:', line):
+ break
+ # find entries
+ state = None
+ for line in lines:
+ if state and not re.match(r'^\s\s\S', line):
+ yield (state, line.strip())
+ state = None
+ elif re.match(r'^\s\s\S+$', line.rstrip()):
+ state = line.strip()
+ else:
+ pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)')
+ match = pattern.match(line.rstrip())
+ if match:
+ current = match.groupdict()
+ yield (current['key'], current['value'])
def get_checkers(clang, plugins):
""" Get all the available checkers from default and from the plugins.
- clang -- the compiler we are using
- plugins -- list of plugins which was requested by the user
+ :param clang: the compiler we are using
+ :param plugins: list of plugins which was requested by the user
+ :return: a dictionary of all available checkers and its status
- This method returns a dictionary of all available checkers and status.
-
- {<plugin name>: (<plugin description>, <is active by default>)} """
-
- plugins = plugins if plugins else []
-
- def parse_checkers(stream):
- """ Parse clang -analyzer-checker-help output.
-
- Below the line 'CHECKERS:' are there the name description pairs.
- Many of them are in one line, but some long named plugins has the
- name and the description in separate lines.
-
- The plugin name is always prefixed with two space character. The
- name contains no whitespaces. Then followed by newline (if it's
- too long) or other space characters comes the description of the
- plugin. The description ends with a newline character. """
-
- # find checkers header
- for line in stream:
- if re.match(r'^CHECKERS:', line):
- break
- # find entries
- state = None
- for line in stream:
- if state and not re.match(r'^\s\s\S', line):
- yield (state, line.strip())
- state = None
- elif re.match(r'^\s\s\S+$', line.rstrip()):
- state = line.strip()
- else:
- pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)')
- match = pattern.match(line.rstrip())
- if match:
- current = match.groupdict()
- yield (current['key'], current['value'])
-
- def is_active(actives, entry):
- """ Returns true if plugin name is matching the active plugin names.
-
- actives -- set of active plugin names (or prefixes).
- entry -- the current plugin name to judge.
-
- The active plugin names are specific plugin names or prefix of some
- names. One example for prefix, when it say 'unix' and it shall match
- on 'unix.API', 'unix.Malloc' and 'unix.MallocSizeof'. """
-
- return any(re.match(r'^' + a + r'(\.|$)', entry) for a in actives)
-
- actives = get_active_checkers(clang, plugins)
+ {<checker name>: (<checker description>, <is active by default>)} """
load = [elem for plugin in plugins for elem in ['-load', plugin]]
cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help']
logging.debug('exec command: %s', ' '.join(cmd))
- child = subprocess.Popen(cmd,
- universal_newlines=True,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
+ output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ lines = output.decode('utf-8').splitlines()
+
+ is_active_checker = is_active(get_active_checkers(clang, plugins))
+
checkers = {
- k: (v, is_active(actives, k))
- for k, v in parse_checkers(child.stdout)
+ name: (description, is_active_checker(name))
+ for name, description in parse_checkers(lines)
}
- child.stdout.close()
- child.wait()
- if child.returncode == 0 and len(checkers):
- return checkers
- else:
+ if not checkers:
raise Exception('Could not query Clang for available checkers.')
+
+ return checkers
diff --git a/tools/scan-build-py/tests/unit/test_clang.py b/tools/scan-build-py/tests/unit/test_clang.py
index 04414a8..eef8c26 100644
--- a/tools/scan-build-py/tests/unit/test_clang.py
+++ b/tools/scan-build-py/tests/unit/test_clang.py
@@ -8,9 +8,19 @@
import libscanbuild.clang as sut
import unittest
import os.path
+import sys
-class GetClangArgumentsTest(unittest.TestCase):
+class ClangGetVersion(unittest.TestCase):
+ def test_get_version_is_not_empty(self):
+ self.assertTrue(sut.get_version('clang'))
+
+ def test_get_version_throws(self):
+ with self.assertRaises(OSError):
+ sut.get_version('notexists')
+
+
+class ClangGetArgumentsTest(unittest.TestCase):
def test_get_clang_arguments(self):
with libear.TemporaryDirectory() as tmpdir:
filename = os.path.join(tmpdir, 'test.c')
@@ -25,18 +35,60 @@
self.assertTrue('var="this is it"' in result)
def test_get_clang_arguments_fails(self):
- self.assertRaises(
- Exception, sut.get_arguments,
- ['clang', '-###', '-fsyntax-only', '-x', 'c', 'notexist.c'], '.')
+ with self.assertRaises(Exception):
+ sut.get_arguments(['clang', '-x', 'c', 'notexist.c'], '.')
+
+ def test_get_clang_arguments_fails_badly(self):
+ with self.assertRaises(OSError):
+ sut.get_arguments(['notexist'], '.')
-class GetCheckersTest(unittest.TestCase):
+class ClangGetCheckersTest(unittest.TestCase):
def test_get_checkers(self):
# this test is only to see is not crashing
result = sut.get_checkers('clang', [])
self.assertTrue(len(result))
+ # do check result types
+ string_type = unicode if sys.version_info < (3,) else str
+ for key, value in result.items():
+ self.assertEqual(string_type, type(key))
+ self.assertEqual(string_type, type(value[0]))
+ self.assertEqual(bool, type(value[1]))
def test_get_active_checkers(self):
# this test is only to see is not crashing
result = sut.get_active_checkers('clang', [])
self.assertTrue(len(result))
+ # do check result types
+ for value in result:
+ self.assertEqual(str, type(value))
+
+ def test_is_active(self):
+ test = sut.is_active(['a', 'b.b', 'c.c.c'])
+
+ self.assertTrue(test('a'))
+ self.assertTrue(test('a.b'))
+ self.assertTrue(test('b.b'))
+ self.assertTrue(test('b.b.c'))
+ self.assertTrue(test('c.c.c.p'))
+
+ self.assertFalse(test('ab'))
+ self.assertFalse(test('ba'))
+ self.assertFalse(test('bb'))
+ self.assertFalse(test('c.c'))
+ self.assertFalse(test('b'))
+ self.assertFalse(test('d'))
+
+ def test_parse_checkers(self):
+ lines = [
+ 'OVERVIEW: Clang Static Analyzer Checkers List',
+ '',
+ 'CHECKERS:',
+ ' checker.one Checker One description',
+ ' checker.two',
+ ' Checker Two description']
+ result = dict(sut.parse_checkers(lines))
+ self.assertTrue('checker.one' in result)
+ self.assertEqual('Checker One description', result.get('checker.one'))
+ self.assertTrue('checker.two' in result)
+ self.assertEqual('Checker Two description', result.get('checker.two'))
diff --git a/www/analyzer/alpha_checks.html b/www/analyzer/alpha_checks.html
index 6f5e4f1..0312d16 100644
--- a/www/analyzer/alpha_checks.html
+++ b/www/analyzer/alpha_checks.html
@@ -26,13 +26,14 @@
<ul>
<li><a href="#core_alpha_checkers">Core Alpha Checkers</a></li>
<li><a href="#cplusplus_alpha_checkers">C++ Alpha Checkers</a></li>
+<li><a href="#valist_alpha_checkers">Variable Argument Alpha Checkers</a></li>
<li><a href="#deadcode_alpha_checkers">Dead Code Alpha Checkers</a></li>
<li><a href="#osx_alpha_checkers">OS X Alpha Checkers</a></li>
<li><a href="#security_alpha_checkers">Security Alpha Checkers</a></li>
<li><a href="#unix_alpha_checkers">Unix Alpha Checkers</a></li>
</ul>
-<!------------------------------ core alpha ----------------------------------->
+<!-- ============================= core alpha ============================= -->
<h3 id="core_alpha_checkers">Core Alpha Checkers</h3>
<table class="checkers">
<colgroup><col class="namedescr"><col class="example"></colgroup>
@@ -179,7 +180,7 @@
</tbody></table>
-<!--------------------------- cplusplus alpha --------------------------------->
+<!-- =========================== cplusplus alpha =========================== -->
<h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3>
<table class="checkers">
<colgroup><col class="namedescr"><col class="example"></colgroup>
@@ -226,7 +227,76 @@
</tbody></table>
-<!--------------------------- dead code alpha --------------------------------->
+
+
+<!-- =============================== va_list =============================== -->
+<h3 id="valist_alpha_checkers">Variable Argument Alpha Checkers</h3>
+<table class="checkers">
+<colgroup><col class="namedescr"><col class="example"></colgroup>
+<thead><tr><td>Name, Description</td><td>Example</td></tr></thead>
+
+<tbody>
+<tr><td><div class="namedescr expandable"><span class="name">
+alpha.valist.CopyToSelf</span><span class="lang">
+(C)</span><div class="descr">
+Calls to the <code>va_copy</code> macro should not copy onto itself.</div></div></td>
+<td><div class="exampleContainer expandable">
+<div class="example"><pre>
+#include <stdarg.h>
+
+void test(int x, ...) {
+ va_list args;
+ va_start(args, x);
+ va_copy(args, args); // warn
+ va_end(args);
+}
+</pre></div></div></td></tr>
+
+<tr><td><div class="namedescr expandable"><span class="name">
+alpha.valist.Uninitialized</span><span class="lang">
+(C)</span><div class="descr">
+Calls to the <code>va_arg</code>, <code>va_copy</code>, or
+<code>va_end</code> macro must happen after calling <code>va_start</code> and
+before calling <code>va_end</code>.</div></div></td>
+<td><div class="exampleContainer expandable">
+<div class="example"><pre>
+#include <stdarg.h>
+
+void test(int x, ...) {
+ va_list args;
+ int y = va_arg(args, int); // warn
+}
+</pre></div>
+<div class="example"><pre>
+#include <stdarg.h>
+
+void test(int x, ...) {
+ va_list args;
+ va_start(args, x);
+ va_end(args);
+ int z = va_arg(args, int); // warn
+}
+</pre></div></div></td></tr>
+
+<tr><td><div class="namedescr expandable"><span class="name">
+alpha.valist.Unterminated</span><span class="lang">
+(C)</span><div class="descr">
+Every <code>va_start</code> must be matched by a <code>va_end</code>. A va_list
+can only be ended once.</div></div></td>
+<td><div class="exampleContainer expandable">
+<div class="example"><pre>
+#include <stdarg.h>
+
+void test(int x, ...) {
+ va_list args;
+ va_start(args, x);
+ int y = x + va_arg(args, int);
+} // warn: missing va_end
+</pre></div></div></td></tr>
+
+</tbody></table>
+
+<!-- =========================== dead code alpha =========================== -->
<h3 id="deadcode_alpha_checkers">Dead Code Alpha Checkers</h3>
<table class="checkers">
<colgroup><col class="namedescr"><col class="example"></colgroup>
@@ -267,7 +337,7 @@
</pre></div></div></td></tr>
</tbody></table>
-<!---------------------------- OS X alpha -------------------------------------->
+<!-- ============================== OS X alpha ============================== -->
<h3 id="osx_alpha_checkers">OS X Alpha Checkers</h3>
<table class="checkers">
<colgroup><col class="namedescr"><col class="example"></colgroup>
@@ -433,7 +503,7 @@
</tbody></table>
-<!------------------------- security alpha ------------------------------------>
+<!-- =========================== security alpha =========================== -->
<h3 id="security_alpha_checkers">Security Alpha Checkers</h3>
<table class="checkers">
<colgroup><col class="namedescr"><col class="example"></colgroup>
@@ -584,7 +654,7 @@
</tbody></table>
-<!--------------------------- unix alpha -------------------------------------->
+<!-- ============================= unix alpha ============================= -->
<h3 id="unix_alpha_checkers">Unix Alpha Checkers</h3>
<table class="checkers">
<colgroup><col class="namedescr"><col class="example"></colgroup>
diff --git a/www/analyzer/faq.html b/www/analyzer/faq.html
index cf3dc70..516233b 100644
--- a/www/analyzer/faq.html
+++ b/www/analyzer/faq.html
@@ -29,6 +29,9 @@
<li><a href="#dead_store">How do I tell the static analyzer that I don't care about a specific dead store?</a></li>
<li><a href="#unused_ivar">How do I tell the static analyzer that I don't care about a specific unused instance variable in Objective C?</a></li>
<li><a href="#unlocalized_string">How do I tell the static analyzer that I don't care about a specific unlocalized string?</a></li>
+ <li><a href="#dealloc_mrr">How do I tell the analyzer that my instance variable does not need to be released in -dealloc under Manual Retain/Release?</a></li>
+ <li><a href="#decide_nullability">How do I decide whether a method's return type should be _Nullable or _Nonnull?</a></li>
+ <li><a href="#nullability_intentional_violation">How do I tell the analyzer that I am intentionally violating nullability?</a></li>
<li><a href="#use_assert">The analyzer assumes that a loop body is never entered. How can I tell it that the loop body will be entered at least once?</a></li>
<li><a href="#suppress_issue">How can I suppress a specific analyzer warning?</a></li>
<li><a href="#exclude_code">How can I selectively exclude code the analyzer examines?</a></li>
@@ -105,6 +108,67 @@
</pre>
</p>
+<h4 id="dealloc_mrr" class="faq">Q: How do I tell the analyzer that my instance variable does not need to be released in -dealloc under Manual Retain/Release?</h4>
+
+<p>If your class only uses an instance variable for part of its lifetime, it may
+maintain an invariant guaranteeing that the instance variable is always released
+before -dealloc. In this case, you can silence a warning about a missing release
+by either adding <tt>assert(_ivar == nil)</tt> or an explicit release
+<tt>[_ivar release]</tt> (which will be a no-op when the variable is nil) in
+-dealloc. </p>
+
+<h4 id="decide_nullability" class="faq">Q: How do I decide whether a method's return type should be _Nullable or _Nonnull?</h4>
+
+<p> Depending on the implementation of the method, this puts you in one of five situations:
+<ol>
+<li>You actually never return nil.</li>
+<li>You do return nil sometimes, and callers are supposed to handle that. This
+includes cases where your method is documented to return nil given certain
+inputs.</li>
+<li>You return nil based on some external condition (such as an out-of-memory
+error), but the client can't do anything about it either.</li>
+<li>You return nil only when the caller passes input documented to be invalid.
+That means it's the client's fault.</li>
+<li>You return nil in some totally undocumented case.</li>
+</ol>
+</p>
+
+<p>In (1) you should annotate the method as returning a <tt>_Nonnull</tt>
+object.</p>
+<p>In (2) the method should be marked <tt>_Nullable.</tt></p>
+<p>In (3) you should probably annotate the method <tt>_Nonnull</tt>. Why?
+Because no callers will actually check for nil, given that they can't do
+anything about the situation and don't know what went wrong. At this point
+things have gone so poorly that there's basically no way to recover.</p>
+<p>The least happy case is (4) because the resulting program will almost
+certainly either crash or just silently do the wrong thing.
+If this is a new method or you control the callers, you can use
+<tt>NSParameterAssert()</tt> (or the equivalent) to check the precondition and
+remove the nil return. But if you don't control the callers and they rely on
+this behavior, you should return mark the method <tt>_Nonnull</tt> and return
+nil <a href="#nullability_intentional_violation">cast to _Nonnull</a> anyway.
+(Note that (4) doesn't apply in cases where the caller can't know they passed
+bad parameters. For example,
+<tt>+[NSData dataWithContentsOfFile:options:error:]</tt> will fail if the file
+doesn't exist, but there's no way to check for that in advance. This means
+you're really in (2).)</p>
+<p>If you're in (5), document it, then figure out if you're now in (2), (3), or
+(4). :-)</p>
+
+<h4 id="nullability_intentional_violation" class="faq">Q: How do I tell the analyzer that I am intentionally violating nullability?</h4>
+
+<p>In some cases, it may make sense for methods to intentionally violate
+nullability. For example, your method may — for reasons of backward
+compatibility — chose to return nil and log an error message in a method
+with a non-null return type when the client violated a documented precondition
+rather than check the precondition with <tt>NSAssert()</tt>. In these cases, you
+can suppress the analyzer warning with a cast:
+<pre class="code_example">
+ return (id _Nonnull)nil;
+</pre>
+Note that this cast does not affect code generation.
+</p>
+
<h4 id="use_assert" class="faq">Q: The analyzer assumes that a loop body is never entered. How can I tell it that the loop body will be entered at least once?</h4>
<img src="images/example_use_assert.png" alt="example use assert">
diff --git a/www/analyzer/potential_checkers.html b/www/analyzer/potential_checkers.html
index 0e5fba3..2e6a201 100644
--- a/www/analyzer/potential_checkers.html
+++ b/www/analyzer/potential_checkers.html
@@ -180,64 +180,6 @@
</table>
-<!-- =============================== va_list =============================== -->
-<h3>va_list</h3>
-<table class="checkers">
-<col class="namedescr"><col class="example"><col class="progress">
-<thead><tr><td>Name, Description</td><td>Example</td><td>Progress</td></tr></thead>
-
-<tr><td><div class="namedescr expandable"><span class="name">
-valist.Uninitialized</span><span class="lang">
-(C)</span><div class="descr">
-Calls to the <code>va_arg</code>, <code>va_copy</code>, or
-<code>va_end</code> macro must happen after calling <code>va_start</code> and
-before calling <code>va_end</code>.</div></div></td>
-<td><div class="exampleContainer expandable">
-<div class="example"><pre>
-#include <stdarg.h>
-
-void test(int x, ...) {
- va_list args;
- int y = va_arg(args, int); // warn
-}
-</pre></div>
-<div class="example"><pre>
-#include <stdarg.h>
-
-void test(int x, ...) {
- va_list args;
- va_start(args, x);
- va_end(args);
- int z = va_arg(args, int); // warn
-}
-</pre></div></div></td>
-<td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=16812">
-PR16811</a></td></tr>
-
-<tr><td><div class="namedescr expandable"><span class="name">
-valist.Unterminated</span><span class="lang">
-(C)</span><div class="descr">
-Every <code>va_start</code> must be matched by a <code>va_end</code>. A va_list
-can only be ended once.
-
-<i>This should be folded into the generalized "ownership checker"
-described on the <a href="open_projects.html">
-Open Projects</a> page.</i></div></div></td>
-<td><div class="exampleContainer expandable">
-<div class="example"><pre>
-#include <stdarg.h>
-
-void test(int x, ...) {
- va_list args;
- va_start(args, x);
- int y = x + va_arg(args, int);
-} // warn: missing va_end
-</pre></div></div></td>
-<td class="aligned"><a href="http://llvm.org/bugs/show_bug.cgi?id=16812">
-PR16812</a></td></tr>
-
-</table>
-
<!-- ============================== exceptions ============================= -->
<h3>exceptions</h3>
<table class="checkers">
@@ -384,7 +326,8 @@
// warn: the right operand to '-' is always 0
}
</pre></div></div></td>
-<td class="aligned">removed from alpha.deadcode.* at r198476</td></tr>
+<td class="aligned">removed from alpha.deadcode.* at
+<a href="https://reviews.llvm.org/rL198476">r198476</a></td></tr>
</table>