[clang] 2033fa2 - Add documentation illustrating use of IgnoreUnlessSpelledInSource
Stephen Kelly via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 20 05:50:11 PST 2020
Author: Stephen Kelly
Date: 2020-11-20T13:49:25Z
New Revision: 2033fa29b09f9402a9f09f9de20414e82f7d174c
URL: https://github.com/llvm/llvm-project/commit/2033fa29b09f9402a9f09f9de20414e82f7d174c
DIFF: https://github.com/llvm/llvm-project/commit/2033fa29b09f9402a9f09f9de20414e82f7d174c.diff
LOG: Add documentation illustrating use of IgnoreUnlessSpelledInSource
Differential Revision: https://reviews.llvm.org/D91639
Added:
Modified:
clang/docs/LibASTMatchersReference.html
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index d9fa3c2830fb..fbc53ed743a2 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -17,6 +17,12 @@
color: blue;
cursor: pointer;
}
+span.mono { font-family: monospace; }
+
+.traverse_compare, .traverse_compare td, .traverse_compare th {
+ border: 1px solid black;
+ border-collapse: collapse;
+}
</style>
<script type="text/javascript">
function toggle(id) {
@@ -69,6 +75,431 @@ <h1>AST Matcher Reference</h1>
</pre>
</p>
+<!-- ======================================================================= -->
+<h2 id="traverse-mode">Traverse Mode</h2>
+<!-- ======================================================================= -->
+
+<p>The default mode of operation of AST Matchers visits all nodes in the AST,
+even if they are not spelled in the source. This is
+<span class="mono">AsIs</span> mode. This mode requires writing AST matchers
+that explicitly traverse or ignore implicit nodes, such as parentheses
+surrounding an expression or expressions with cleanups. These implicit
+nodes are not always obvious from the syntax of the source code, and so this
+mode requires careful consideration and testing to get the desired behavior
+from an AST matcher.
+</p>
+
+<p>In addition, because template instantations are matched in the default mode,
+transformations can be accidentally made to template declarations. Finally,
+because implicit nodes are matched by default, transformations can be made on
+entirely incorrect places in the code.</p>
+
+<p>For these reasons, it is possible to ignore AST nodes which are not spelled
+in the source using the <span class="mono">IgnoreUnlessSpelledInSource</span>
+mode. This is likely to be far less error-prone for users who are not already
+very familiar with where implicit nodes appear in the AST. It is also likely
+to be less error-prone for experienced AST users, as
diff icult cases do not
+need to be encountered and matcher expressions adjusted for these cases.</p>
+
+<p>In clang-query, the mode can be changed with
+<pre>
+set traversal IgnoreUnlessSpelledInSource
+</pre>
+</p>
+This affects both matchers and AST dump output in results.
+
+<p>When using the C++ API such as in clang-tidy checks, the
+<span class="mono">traverse()</span> matcher is used to set the mode:
+<pre>
+Finder->addMatcher(traverse(TK_IgnoreUnlessSpelledInSource,
+ returnStmt(hasReturnArgument(integerLiteral(equals(0))))
+ ), this);
+</pre>
+</p>
+<p>The following table compares the <span class="mono">AsIs</span> mode with
+the <span class="mono">IgnoreUnlessSpelledInSource</span> mode:</p>
+
+<table class="traverse_compare">
+<tr>
+<th></th>
+<th><span class="mono">AsIs</span></th>
+<th><span class="mono">IgnoreUnlessSpelledInSource</span></th>
+</tr>
+<tr>
+ <td>AST dump of <span class="mono">func1</span>:
+<pre>
+struct B {
+ B(int);
+};
+
+B func1() { return 42; }
+</pre>
+
+ </td>
+ <td>
+C++98 dialect:
+<pre>
+FunctionDecl
+`-CompoundStmt
+ `-ReturnStmt
+ `-ExprWithCleanups
+ `-CXXConstructExpr
+ `-MaterializeTemporaryExpr
+ `-ImplicitCastExpr
+ `-ImplicitCastExpr
+ `-CXXConstructExpr
+ `-IntegerLiteral 'int' 42
+</pre>
+C++11, C++14 dialect:
+<pre>
+FunctionDecl
+`-CompoundStmt
+ `-ReturnStmt
+ `-ExprWithCleanups
+ `-CXXConstructExpr
+ `-MaterializeTemporaryExpr
+ `-ImplicitCastExpr
+ `-CXXConstructExpr
+ `-IntegerLiteral 'int' 42
+</pre>
+C++17, C++20 dialect:
+<pre>
+FunctionDecl
+`-CompoundStmt
+ `-ReturnStmt
+ `-ImplicitCastExpr
+ `-CXXConstructExpr
+ `-IntegerLiteral 'int' 42
+</pre>
+</td>
+ <td>
+All dialects:
+ <pre>
+FunctionDecl
+`-CompoundStmt
+ `-ReturnStmt
+ `-IntegerLiteral 'int' 42
+</pre></td>
+</tr>
+
+<tr>
+<td>Matcher for returned <span class="mono">42</span>:
+<pre>
+struct B {
+ B(int);
+};
+
+B func1() { return 42; }
+</pre>
+
+ </td>
+ <td>
+All dialects:
+<pre>
+returnStmt(hasReturnValue(
+ ignoringImplicit(
+ ignoringElidableConstructorCall(
+ ignoringImplicit(
+ cxxConstructExpr(hasArgument(0,
+ ignoringImplicit(
+ integerLiteral().bind("returnVal")
+ )
+ ))
+ )
+ )
+ )
+ ))
+</pre></td>
+ <td>
+All dialects:
+<pre>
+returnStmt(hasReturnValue(
+ integerLiteral().bind("returnVal")
+))
+</pre></td>
+</tr>
+<tr>
+<td>Match result for
+<pre>implicitCastExpr()</pre>
+given:
+<pre>
+struct B {
+ B(int);
+};
+
+B func1() { return 42; }
+</pre>
+
+</td>
+<td>
+Match found.</td>
+ <td>
+No match.</td>
+</tr>
+<tr>
+ <td>Match result for:
+<pre>
+cxxConstructorDecl(
+ isCopyConstructor()
+ ).bind("prepend_explicit")
+</pre>
+given:
+<pre>
+struct Other {};
+struct Copyable {
+ Other m_o;
+ Copyable();
+};
+</pre>
+</td>
+<td>
+Match found. Insertion produces incorrect output:
+<pre>
+struct Other {};
+struct explicit Copyable {
+ Other m_o;
+ Copyable();
+};
+</pre>
+</td>
+<td>
+No match found. Incorrect replacement not possible.
+</td>
+</tr>
+<tr>
+ <td>Replacement of <span class="mono">begin()</span>
+ with <span class="mono">cbegin()</span>:
+<pre>
+cxxMemberCallExpr(
+ on(ConstContainerExpr),
+ callee(cxxMethodDecl(hasName("begin")))
+ ).bind("replace_with_cbegin")
+</pre>
+given:
+<pre>
+void foo() {
+ const Container c;
+ c.begin();
+
+ for (auto i : c) {
+ }
+}
+</pre>
+</td>
+<td>
+2 matches found. Replacement produces incorrect output:
+<pre>
+void foo() {
+ const Container c;
+ c.cbegin();
+
+ for (auto i :.cbegin() c) {
+ }
+}
+</pre>
+</td>
+<td>
+1 match found. Replacement produces correct output:
+<pre>
+void foo() {
+ const Container c;
+ c.cbegin();
+
+ for (auto i : c) {
+ }
+}
+</pre>
+</td>
+</tr>
+<tr>
+ <td>Replacement of <span class="mono">int</span> member
+ with <span class="mono">safe_int</span>:
+<pre>
+fieldDecl(
+ hasType(asString("int"))
+ ).bind("use_safe_int")
+</pre>
+given:
+<pre>
+struct S {
+ int m_i;
+};
+
+template <typename T> struct TemplStruct {
+ TemplStruct() {}
+ ~TemplStruct() {}
+
+private:
+ T m_t;
+};
+
+void instantiate() { TemplStruct<int> ti; }
+</pre>
+</td>
+<td>
+2 matches found. Replacement produces incorrect output:
+<pre>
+struct S {
+ safe_int m_i;
+};
+
+template <typename T> struct TemplStruct {
+ TemplStruct() {}
+ ~TemplStruct() {}
+
+private:
+ safe_int m_t;
+};
+
+void instantiate() { TemplStruct<int> ti; }
+</pre>
+</td>
+<td>
+1 match found. Replacement produces correct output:
+<pre>
+struct S {
+ safe_int m_i;
+};
+
+template <typename T> struct TemplStruct {
+ TemplStruct() {}
+ ~TemplStruct() {}
+
+private:
+ T m_t;
+};
+
+void instantiate() { TemplStruct<int> ti; }
+</pre>
+</td>
+</tr>
+<tr>
+ <td>Add prefix to member initializer
+<pre>
+cxxCtorInitializer(
+ forField(fieldDecl())
+ ).bind("add_prefix")
+</pre>
+given:
+<pre>
+struct Simple {};
+
+struct Record {
+ Record() : i(42) {}
+private:
+ int i;
+ Simple s;
+};
+</pre>
+</td>
+<td>
+2 matches found. Replacement produces incorrect output:
+<pre>
+struct Simple {};
+
+struct Record {
+ m_Record() : m_i(42) {}
+private:
+ int i;
+ Simple s;
+};
+</pre>
+</td>
+<td>
+1 match found. Replacement produces correct output:
+<pre>
+struct Simple {};
+
+struct Record {
+ Record() : m_i(42) {}
+private:
+ int i;
+ Simple s;
+};
+</pre>
+</td>
+</tr>
+<tr>
+ <td>Ignored default arguments
+<pre>
+callExpr(
+ callee(functionDecl(
+ hasName("hasDefaultArg")
+ )),
+ argumentCountIs(1)
+ ).bind("add_prefix")
+</pre>
+given:
+<pre>
+void hasDefaultArg(int i, int j = 0) {}
+void callDefaultArg() { hasDefaultArg(42); }
+</pre>
+</td>
+<td>
+No match.
+</td>
+<td>
+1 match found.
+</td>
+</tr>
+<tr>
+ <td>Lambda fields
+<pre>
+fieldDecl(
+ hasType(asString("int"))
+ ).bind("make_safe")
+</pre>
+given:
+<pre>
+struct S {
+ int m_i;
+};
+
+void func() {
+ int a = 0;
+ int c = 0;
+
+ auto l = [a, b = c](int d) { int e = d; };
+ l(43);
+}
+</pre>
+</td>
+<td>
+2 matches found. Replacement produces incorrect output:
+<pre>
+struct S {
+ safe_int m_i;
+};
+
+void func() {
+ int a = 0;
+ int c = 0;
+
+ auto l = [safe_a, safe_b = c](int d) { int e = d; };
+ l(43);
+}
+</pre>
+</td>
+<td>
+1 match found. Replacement produces correct output:
+<pre>
+struct S {
+ safe_int m_i;
+};
+
+void func() {
+ int a = 0;
+ int c = 0;
+
+ auto l = [a, b = c](int d) { int e = d; };
+ l(43);
+}
+</pre>
+</td>
+
+</tr>
+</table>
+
<!-- ======================================================================= -->
<h2 id="decl-matchers">Node Matchers</h2>
<!-- ======================================================================= -->
More information about the cfe-commits
mailing list