summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/lib/Analysis/ExprMutationAnalyzer.cpp15
-rw-r--r--clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp138
2 files changed, 128 insertions, 25 deletions
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index 50d4d0b8890..db8c259a52a 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -304,7 +304,16 @@ const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
nonConstReferenceType()))))
.bind(NodeID<Expr>::value)),
Stm, Context);
- return findExprMutation(Casts);
+ if (const Stmt *S = findExprMutation(Casts))
+ return S;
+ // Treat std::{move,forward} as cast.
+ const auto Calls =
+ match(findAll(callExpr(callee(namedDecl(
+ hasAnyName("::std::move", "::std::forward"))),
+ hasArgument(0, equalsNode(Exp)))
+ .bind("expr")),
+ Stm, Context);
+ return findExprMutation(Calls);
}
const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
@@ -360,7 +369,9 @@ const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
const auto IsInstantiated = hasDeclaration(isInstantiated());
const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
const auto Matches = match(
- findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
+ findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
+ unless(callee(namedDecl(hasAnyName(
+ "::std::move", "::std::forward"))))),
cxxConstructExpr(NonConstRefParam, IsInstantiated,
FuncDecl)))
.bind(NodeID<Expr>::value)),
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index 318fb0d82ae..77cc702056e 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -66,6 +66,25 @@ std::string removeSpace(std::string s) {
return s;
}
+const std::string StdRemoveReference =
+ "namespace std {"
+ "template<class T> struct remove_reference { typedef T type; };"
+ "template<class T> struct remove_reference<T&> { typedef T type; };"
+ "template<class T> struct remove_reference<T&&> { typedef T type; }; }";
+
+const std::string StdMove =
+ "namespace std {"
+ "template<class T> typename remove_reference<T>::type&& "
+ "move(T&& t) noexcept {"
+ "return static_cast<typename remove_reference<T>::type&&>(t); } }";
+
+const std::string StdForward =
+ "namespace std {"
+ "template<class T> T&& "
+ "forward(typename remove_reference<T>::type& t) noexcept { return t; }"
+ "template<class T> T&& "
+ "forward(typename remove_reference<T>::type&&) noexcept { return t; } }";
+
} // namespace
TEST(ExprMutationAnalyzerTest, Trivial) {
@@ -373,36 +392,87 @@ TEST(ExprMutationAnalyzerTest, ByConstRRefArgument) {
}
TEST(ExprMutationAnalyzerTest, Move) {
- // Technically almost the same as ByNonConstRRefArgument, just double checking
- const auto AST = tooling::buildASTFromCode(
- "namespace std {"
- "template<class T> struct remove_reference { typedef T type; };"
- "template<class T> struct remove_reference<T&> { typedef T type; };"
- "template<class T> struct remove_reference<T&&> { typedef T type; };"
- "template<class T> typename std::remove_reference<T>::type&& "
- "move(T&& t) noexcept; }"
- "void f() { struct A {}; A x; std::move(x); }");
- const auto Results =
+ auto AST =
+ tooling::buildASTFromCode(StdRemoveReference + StdMove +
+ "void f() { struct A {}; A x; std::move(x); }");
+ auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
- EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdMove +
+ "void f() { struct A {}; A x, y; std::move(x) = y; }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
+
+ AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+ "void f() { int x, y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdMove +
+ "struct S { S(); S(const S&); S& operator=(const S&); };"
+ "void f() { S x, y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST =
+ tooling::buildASTFromCode(StdRemoveReference + StdMove +
+ "struct S { S(); S(S&&); S& operator=(S&&); };"
+ "void f() { S x, y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+ AST =
+ tooling::buildASTFromCode(StdRemoveReference + StdMove +
+ "struct S { S(); S(const S&); S(S&&);"
+ "S& operator=(const S&); S& operator=(S&&); };"
+ "void f() { S x, y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdMove +
+ "struct S { S(); S(const S&); S(S&&);"
+ "S& operator=(const S&); S& operator=(S&&); };"
+ "void f() { const S x; S y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+ "struct S { S(); S(S); S& operator=(S); };"
+ "void f() { S x, y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdMove +
+ "struct S{}; void f() { S x, y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdMove +
+ "struct S{}; void f() { const S x; S y; y = std::move(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
}
TEST(ExprMutationAnalyzerTest, Forward) {
- // Technically almost the same as ByNonConstRefArgument, just double checking
- const auto AST = tooling::buildASTFromCode(
- "namespace std {"
- "template<class T> struct remove_reference { typedef T type; };"
- "template<class T> struct remove_reference<T&> { typedef T type; };"
- "template<class T> struct remove_reference<T&&> { typedef T type; };"
- "template<class T> T&& "
- "forward(typename std::remove_reference<T>::type&) noexcept;"
- "template<class T> T&& "
- "forward(typename std::remove_reference<T>::type&&) noexcept;"
+ auto AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdForward +
"void f() { struct A {}; A x; std::forward<A &>(x); }");
- const auto Results =
+ auto Results =
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdForward +
+ "void f() { struct A {}; A x, y; std::forward<A &>(x) = y; }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_THAT(mutatedBy(Results, AST.get()),
- ElementsAre("std::forward<A &>(x)"));
+ ElementsAre("std::forward<A &>(x) = y"));
}
TEST(ExprMutationAnalyzerTest, CallUnresolved) {
@@ -639,6 +709,17 @@ TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) {
"void f() { int x; S s(x); }");
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdForward +
+ "template <class... Args> void u(Args&...);"
+ "template <class... Args> void h(Args&&... args)"
+ "{ u(std::forward<Args>(args)...); }"
+ "template <class... Args> void g(Args&&... args)"
+ "{ h(std::forward<Args>(args)...); }"
+ "void f() { int x; g(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
}
TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
@@ -683,6 +764,17 @@ TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
"void f() { int x; S s(x); }");
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
EXPECT_FALSE(isMutated(Results, AST.get()));
+
+ AST = tooling::buildASTFromCode(
+ StdRemoveReference + StdForward +
+ "template <class... Args> void u(Args...);"
+ "template <class... Args> void h(Args&&... args)"
+ "{ u(std::forward<Args>(args)...); }"
+ "template <class... Args> void g(Args&&... args)"
+ "{ h(std::forward<Args>(args)...); }"
+ "void f() { int x; g(x); }");
+ Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
}
TEST(ExprMutationAnalyzerTest, ArrayElementModified) {
OpenPOWER on IntegriCloud