[ASTMatchers] extract public matchers from const-analysis into own patch

The analysis for const-ness of local variables required a view generally useful
matchers that are extracted into its own patch.

They are decompositionDecl and forEachArgumentWithParamType, that works
for calls through function pointers as well.

This is a reupload of https://reviews.llvm.org/D72505, that already landed,
but had to be reverted due to a GCC crash on powerpc
(https://reviews.llvm.org/rG4c48ea68e491cb42f1b5d43ffba89f6a7f0dadc4)

Because this took a long time to adress, i decided to redo this patch and
have a clean workflow.
I try to coordinate with someone that has a PPC to apply this patch and
test for the crash. If everything is fine, I intend to just commit.
If the crash is still happening, i hope to at least find the cause.

Differential Revision: https://reviews.llvm.org/D87588
This commit is contained in:
Jonas Toth 2020-09-13 19:30:56 +02:00
parent 583c8ce30c
commit 69f98311ca
4 changed files with 402 additions and 0 deletions

View File

@ -649,6 +649,30 @@ Example matches a
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>&gt;</td><td class="name" onclick="toggle('decompositionDecl0')"><a name="decompositionDecl0Anchor">decompositionDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="decompositionDecl0"><pre>Matches decomposition-declarations.
Examples matches the declaration node with foo and bar, but not
number.
(matcher = declStmt(has(decompositionDecl())))
int number = 42;
auto [foo, bar] = std::make_pair{42, 42};
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>&gt;</td><td class="name" onclick="toggle('decompositionDecl0')"><a name="decompositionDecl0Anchor">decompositionDecl</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="decompositionDecl0"><pre>Matches decomposition-declarations.
Examples matches the declaration node with foo and bar, but not
number.
(matcher = declStmt(has(decompositionDecl())))
int number = 42;
auto [foo, bar] = std::make_pair{42, 42};
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>&gt;</td><td class="name" onclick="toggle('nestedNameSpecifierLoc0')"><a name="nestedNameSpecifierLoc0Anchor">nestedNameSpecifierLoc</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="nestedNameSpecifierLoc0"><pre>Same as nestedNameSpecifier but matches NestedNameSpecifierLoc.
</pre></td></tr>
@ -5322,6 +5346,60 @@ and parmVarDecl(...)
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>&gt;</td><td class="name" onclick="toggle('forEachArgumentWithParamType1')"><a name="forEachArgumentWithParamType1Anchor">forEachArgumentWithParamType</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; ArgMatcher, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt; ParamMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType1"><pre>Matches all arguments and their respective types for a CallExpr or
CXXConstructExpr. It is very similar to forEachArgumentWithParam but
it works on calls through function pointers as well.
The difference is, that function pointers do not provide access to a
ParmVarDecl, but only the QualType for each argument.
Given
void f(int i);
int y;
f(y);
void (*f_ptr)(int) = f;
f_ptr(y);
callExpr(
forEachArgumentWithParamType(
declRefExpr(to(varDecl(hasName("y")))),
qualType(isInteger()).bind("type)
))
matches f(y) and f_ptr(y)
with declRefExpr(...)
matching int y
and qualType(...)
matching int
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>&gt;</td><td class="name" onclick="toggle('forEachArgumentWithParamType1')"><a name="forEachArgumentWithParamType1Anchor">forEachArgumentWithParamType</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; ArgMatcher, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt; ParamMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType1"><pre>Matches all arguments and their respective types for a CallExpr or
CXXConstructExpr. It is very similar to forEachArgumentWithParam but
it works on calls through function pointers as well.
The difference is, that function pointers do not provide access to a
ParmVarDecl, but only the QualType for each argument.
Given
void f(int i);
int y;
f(y);
void (*f_ptr)(int) = f;
f_ptr(y);
callExpr(
forEachArgumentWithParamType(
declRefExpr(to(varDecl(hasName("y")))),
qualType(isInteger()).bind("type)
))
matches f(y) and f_ptr(y)
with declRefExpr(...)
matching int y
and qualType(...)
matching int
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>&gt;</td><td class="name" onclick="toggle('hasAnyArgument1')"><a name="hasAnyArgument1Anchor">hasAnyArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyArgument1"><pre>Matches any argument of a call expression or a constructor call
expression, or an ObjC-message-send expression.
@ -5850,6 +5928,60 @@ and parmVarDecl(...)
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>&gt;</td><td class="name" onclick="toggle('forEachArgumentWithParamType0')"><a name="forEachArgumentWithParamType0Anchor">forEachArgumentWithParamType</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; ArgMatcher, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt; ParamMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType0"><pre>Matches all arguments and their respective types for a CallExpr or
CXXConstructExpr. It is very similar to forEachArgumentWithParam but
it works on calls through function pointers as well.
The difference is, that function pointers do not provide access to a
ParmVarDecl, but only the QualType for each argument.
Given
void f(int i);
int y;
f(y);
void (*f_ptr)(int) = f;
f_ptr(y);
callExpr(
forEachArgumentWithParamType(
declRefExpr(to(varDecl(hasName("y")))),
qualType(isInteger()).bind("type)
))
matches f(y) and f_ptr(y)
with declRefExpr(...)
matching int y
and qualType(...)
matching int
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>&gt;</td><td class="name" onclick="toggle('forEachArgumentWithParamType0')"><a name="forEachArgumentWithParamType0Anchor">forEachArgumentWithParamType</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; ArgMatcher, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt; ParamMatcher</td></tr>
<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType0"><pre>Matches all arguments and their respective types for a CallExpr or
CXXConstructExpr. It is very similar to forEachArgumentWithParam but
it works on calls through function pointers as well.
The difference is, that function pointers do not provide access to a
ParmVarDecl, but only the QualType for each argument.
Given
void f(int i);
int y;
f(y);
void (*f_ptr)(int) = f;
f_ptr(y);
callExpr(
forEachArgumentWithParamType(
declRefExpr(to(varDecl(hasName("y")))),
qualType(isInteger()).bind("type)
))
matches f(y) and f_ptr(y)
with declRefExpr(...)
matching int y
and qualType(...)
matching int
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>&gt;</td><td class="name" onclick="toggle('hasAnyArgument0')"><a name="hasAnyArgument0Anchor">hasAnyArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyArgument0"><pre>Matches any argument of a call expression or a constructor call
expression, or an ObjC-message-send expression.

View File

@ -334,6 +334,19 @@ AST_MATCHER_P(Stmt, isExpandedFromMacro, llvm::StringRef, MacroName) {
/// \endcode
extern const internal::VariadicAllOfMatcher<Decl> decl;
/// Matches decomposition-declarations.
///
/// Examples matches the declaration node with \c foo and \c bar, but not
/// \c number.
/// (matcher = declStmt(has(decompositionDecl())))
///
/// \code
/// int number = 42;
/// auto [foo, bar] = std::make_pair{42, 42};
/// \endcode
extern const internal::VariadicAllOfMatcher<DecompositionDecl>
decompositionDecl;
/// Matches a declaration of a linkage specification.
///
/// Given
@ -4349,6 +4362,103 @@ AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParam,
return Matched;
}
/// Matches all arguments and their respective types for a \c CallExpr or
/// \c CXXConstructExpr. It is very similar to \c forEachArgumentWithParam but
/// it works on calls through function pointers as well.
///
/// The difference is, that function pointers do not provide access to a
/// \c ParmVarDecl, but only the \c QualType for each argument.
///
/// Given
/// \code
/// void f(int i);
/// int y;
/// f(y);
/// void (*f_ptr)(int) = f;
/// f_ptr(y);
/// \endcode
/// callExpr(
/// forEachArgumentWithParamType(
/// declRefExpr(to(varDecl(hasName("y")))),
/// qualType(isInteger()).bind("type)
/// ))
/// matches f(y) and f_ptr(y)
/// with declRefExpr(...)
/// matching int y
/// and qualType(...)
/// matching int
AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParamType,
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
CXXConstructExpr),
internal::Matcher<Expr>, ArgMatcher,
internal::Matcher<QualType>, ParamMatcher) {
BoundNodesTreeBuilder Result;
// The first argument of an overloaded member operator is the implicit object
// argument of the method which should not be matched against a parameter, so
// we skip over it here.
BoundNodesTreeBuilder Matches;
unsigned ArgIndex = cxxOperatorCallExpr(callee(cxxMethodDecl()))
.matches(Node, Finder, &Matches)
? 1
: 0;
const FunctionProtoType *FProto = nullptr;
if (const auto *Call = dyn_cast<CallExpr>(&Node)) {
if (const auto *Value =
dyn_cast_or_null<ValueDecl>(Call->getCalleeDecl())) {
QualType QT = Value->getType().getCanonicalType();
// This does not necessarily lead to a `FunctionProtoType`,
// e.g. K&R functions do not have a function prototype.
if (QT->isFunctionPointerType())
FProto = QT->getPointeeType()->getAs<FunctionProtoType>();
if (QT->isMemberFunctionPointerType()) {
const auto *MP = QT->getAs<MemberPointerType>();
assert(MP && "Must be member-pointer if its a memberfunctionpointer");
FProto = MP->getPointeeType()->getAs<FunctionProtoType>();
assert(FProto &&
"The call must have happened through a member function "
"pointer");
}
}
}
int ParamIndex = 0;
bool Matched = false;
for (; ArgIndex < Node.getNumArgs(); ++ArgIndex, ++ParamIndex) {
BoundNodesTreeBuilder ArgMatches(*Builder);
if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->IgnoreParenCasts()), Finder,
&ArgMatches)) {
BoundNodesTreeBuilder ParamMatches(ArgMatches);
// This test is cheaper compared to the big matcher in the next if.
// Therefore, please keep this order.
if (FProto) {
QualType ParamType = FProto->getParamType(ParamIndex);
if (ParamMatcher.matches(ParamType, Finder, &ParamMatches)) {
Result.addMatch(ParamMatches);
Matched = true;
continue;
}
}
if (expr(anyOf(cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
hasParameter(ParamIndex, hasType(ParamMatcher))))),
callExpr(callee(functionDecl(
hasParameter(ParamIndex, hasType(ParamMatcher)))))))
.matches(Node, Finder, &ParamMatches)) {
Result.addMatch(ParamMatches);
Matched = true;
continue;
}
}
}
*Builder = std::move(Result);
return Matched;
}
/// Matches the ParmVarDecl nodes that are at the N'th position in the parameter
/// list. The parameter list could be that of either a block, function, or
/// objc-method.

View File

@ -202,6 +202,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(cxxUnresolvedConstructExpr);
REGISTER_MATCHER(decayedType);
REGISTER_MATCHER(decl);
REGISTER_MATCHER(decompositionDecl);
REGISTER_MATCHER(declCountIs);
REGISTER_MATCHER(declRefExpr);
REGISTER_MATCHER(declStmt);
@ -227,6 +228,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(floatLiteral);
REGISTER_MATCHER(forEach);
REGISTER_MATCHER(forEachArgumentWithParam);
REGISTER_MATCHER(forEachArgumentWithParamType);
REGISTER_MATCHER(forEachConstructorInitializer);
REGISTER_MATCHER(forEachDescendant);
REGISTER_MATCHER(forEachOverridden);

View File

@ -741,6 +741,164 @@ TEST(ForEachArgumentWithParam, HandlesBoundNodesForNonMatches) {
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("v", 4)));
}
TEST(ForEachArgumentWithParamType, ReportsNoFalsePositives) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
// IntParam does not match.
EXPECT_TRUE(notMatches("void f(int* i) { int* y; f(y); }", CallExpr));
// ArgumentY does not match.
EXPECT_TRUE(notMatches("void f(int i) { int x; f(x); }", CallExpr));
}
TEST(ForEachArgumentWithParamType, MatchesCXXMemberCallExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct S {"
" const S& operator[](int i) { return *this; }"
"};"
"void f(S S1) {"
" int y = 1;"
" S1[y];"
"}",
CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 1)));
StatementMatcher CallExpr2 =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct S {"
" static void g(int i);"
"};"
"void f() {"
" int y = 1;"
" S::g(y);"
"}",
CallExpr2, std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 1)));
}
TEST(ForEachArgumentWithParamType, MatchesCallExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) { int y; f(y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) { int y; f(y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i, int j) { int y; f(y, y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i, int j) { int y; f(y, y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg", 2)));
}
TEST(ForEachArgumentWithParamType, MatchesConstructExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher ConstructExpr =
cxxConstructExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct C {"
" C(int i) {}"
"};"
"int y = 0;"
"C Obj(y);",
ConstructExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct C {"
" C(int i) {}"
"};"
"int y = 0;"
"C Obj(y);",
ConstructExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST(ForEachArgumentWithParamType, HandlesKandRFunctions) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchesC("void f();\n"
"void call_it(void) { int x, y; f(x, y); }\n"
"void f(a, b) int a, b; {}\n"
"void call_it2(void) { int x, y; f(x, y); }",
CallExpr));
}
TEST(ForEachArgumentWithParamType, HandlesBoundNodesForNonMatches) {
EXPECT_TRUE(matchAndVerifyResultTrue(
"void g(int i, int j) {"
" int a;"
" int b;"
" int c;"
" g(a, 0);"
" g(a, b);"
" g(0, b);"
"}",
functionDecl(
forEachDescendant(varDecl().bind("v")),
forEachDescendant(callExpr(forEachArgumentWithParamType(
declRefExpr(to(decl(equalsBoundNode("v")))), qualType())))),
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("v", 4)));
}
TEST(ForEachArgumentWithParamType, MatchesFunctionPtrCalls) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(builtinType()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) {"
"void (*f_ptr)(int) = f; int y; f_ptr(y); }",
CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) {"
"void (*f_ptr)(int) = f; int y; f_ptr(y); }",
CallExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST(ForEachArgumentWithParamType, MatchesMemberFunctionPtrCalls) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(builtinType()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
StringRef S = "struct A {\n"
" int f(int i) { return i + 1; }\n"
" int (A::*x)(int);\n"
"};\n"
"void f() {\n"
" int y = 42;\n"
" A a;\n"
" a.x = &A::f;\n"
" (a.*(a.x))(y);\n"
"}";
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST(QualType, hasCanonicalType) {
EXPECT_TRUE(notMatches("typedef int &int_ref;"
"int a;"