forked from OSchip/llvm-project
Revert "Revert "[clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer""
This is the same as D50619 plus fixes for buildbot failures on windows. The test failures on windows are caused by -fdelayed-template-parsing and is fixed by forcing -fno-delayed-template-parsing on test cases that requires AST for uninstantiated templates. llvm-svn: 341891
This commit is contained in:
parent
4ec5a9159b
commit
5066ab369d
|
@ -145,11 +145,16 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
|
|||
hasUnaryOperand(equalsNode(Exp)));
|
||||
|
||||
// Invoking non-const member function.
|
||||
// A member function is assumed to be non-const when it is unresolved.
|
||||
const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
|
||||
const auto AsNonConstThis =
|
||||
expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
|
||||
cxxOperatorCallExpr(callee(NonConstMethod),
|
||||
hasArgument(0, equalsNode(Exp)))));
|
||||
hasArgument(0, equalsNode(Exp))),
|
||||
callExpr(callee(expr(anyOf(
|
||||
unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
|
||||
cxxDependentScopeMemberExpr(
|
||||
hasObjectExpression(equalsNode(Exp)))))))));
|
||||
|
||||
// Taking address of 'Exp'.
|
||||
// We're assuming 'Exp' is mutated as soon as its address is taken, though in
|
||||
|
@ -165,10 +170,16 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
|
|||
unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
|
||||
|
||||
// Used as non-const-ref argument when calling a function.
|
||||
// An argument is assumed to be non-const-ref when the function is unresolved.
|
||||
const auto NonConstRefParam = forEachArgumentWithParam(
|
||||
equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
|
||||
const auto AsNonConstRefArg =
|
||||
anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam));
|
||||
const auto AsNonConstRefArg = anyOf(
|
||||
callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam),
|
||||
callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
|
||||
cxxDependentScopeMemberExpr(),
|
||||
hasType(templateTypeParmType())))),
|
||||
hasAnyArgument(equalsNode(Exp))),
|
||||
cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
|
||||
|
||||
// Captured by a lambda by reference.
|
||||
// If we're initializing a capture with 'Exp' directly then we're initializing
|
||||
|
@ -195,9 +206,12 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
|
|||
|
||||
const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
|
||||
// Check whether any member of 'Exp' is mutated.
|
||||
const auto MemberExprs = match(
|
||||
findAll(memberExpr(hasObjectExpression(equalsNode(Exp))).bind("expr")),
|
||||
Stm, Context);
|
||||
const auto MemberExprs =
|
||||
match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
|
||||
cxxDependentScopeMemberExpr(
|
||||
hasObjectExpression(equalsNode(Exp)))))
|
||||
.bind("expr")),
|
||||
Stm, Context);
|
||||
return findExprMutation(MemberExprs);
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,29 @@ TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
|
|||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
|
||||
auto AST = tooling::buildASTFromCodeWithArgs(
|
||||
"struct X { template <class T> void mf(); };"
|
||||
"template <class T> void f() { X x; x.mf<T>(); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
auto Results =
|
||||
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf<T>()"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f() { T x; x.mf(); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> struct X;"
|
||||
"template <class T> void f() { X<T> x; x.mf(); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
|
||||
const auto AST = tooling::buildASTFromCode(
|
||||
"void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
|
||||
|
@ -292,6 +315,59 @@ TEST(ExprMutationAnalyzerTest, Forward) {
|
|||
ElementsAre("std::forward<A &>(x)"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, CallUnresolved) {
|
||||
auto AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f() { T x; g(x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
auto Results =
|
||||
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <int N> void f() { char x[N]; g(x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f(T t) { int x; g(t, x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f(T t) { int x; t.mf(x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> struct S;"
|
||||
"template <class T> void f() { S<T> s; int x; s.mf(x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"struct S { template <class T> void mf(); };"
|
||||
"template <class T> void f(S s) { int x; s.mf<T>(x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf<T>(x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs("template <class F>"
|
||||
"void g(F f) { int x; f(x); } ",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f() { int x; (void)T(x); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
|
||||
const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
|
||||
const auto Results =
|
||||
|
@ -347,6 +423,22 @@ TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) {
|
|||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
|
||||
const auto AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <typename T> struct S { static constexpr int v = 8; };"
|
||||
"template <> struct S<int> { static constexpr int v = 4; };"
|
||||
"void g(char*);"
|
||||
"template <typename T> void f() { char x[S<T>::v]; g(x); }"
|
||||
"template <> void f<int>() { char y[S<int>::v]; g(y); }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
const auto ResultsX =
|
||||
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
|
||||
const auto ResultsY =
|
||||
match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, FollowRefModified) {
|
||||
const auto AST = tooling::buildASTFromCode(
|
||||
"void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
|
||||
|
@ -398,21 +490,47 @@ TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) {
|
|||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, NestedMemberModified) {
|
||||
const auto AST = tooling::buildASTFromCode(
|
||||
auto AST = tooling::buildASTFromCode(
|
||||
"void f() { struct A { int vi; }; struct B { A va; }; "
|
||||
"struct C { B vb; }; C x; x.vb.va.vi = 10; }");
|
||||
const auto Results =
|
||||
auto Results =
|
||||
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f() { T x; x.y.z = 10; }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10"));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> struct S;"
|
||||
"template <class T> void f() { S<T> x; x.y.z = 10; }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10"));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) {
|
||||
const auto AST = tooling::buildASTFromCode(
|
||||
auto AST = tooling::buildASTFromCode(
|
||||
"void f() { struct A { int vi; }; struct B { A va; }; "
|
||||
"struct C { B vb; }; C x; x.vb.va.vi; }");
|
||||
const auto Results =
|
||||
auto Results =
|
||||
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_FALSE(isMutated(Results, AST.get()));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> void f() { T x; x.y.z; }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_FALSE(isMutated(Results, AST.get()));
|
||||
|
||||
AST = tooling::buildASTFromCodeWithArgs(
|
||||
"template <class T> struct S;"
|
||||
"template <class T> void f() { S<T> x; x.y.z; }",
|
||||
{"-fno-delayed-template-parsing"});
|
||||
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
||||
EXPECT_FALSE(isMutated(Results, AST.get()));
|
||||
}
|
||||
|
||||
TEST(ExprMutationAnalyzerTest, CastToValue) {
|
||||
|
|
Loading…
Reference in New Issue