2018-09-11 21:13:20 +00:00
|
|
|
//===---------- ExprMutationAnalyzerTest.cpp ------------------------------===//
|
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-09-11 21:13:20 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
|
2020-10-09 13:17:59 +02:00
|
|
|
#include "clang/AST/TypeLoc.h"
|
2018-09-11 21:13:20 +00:00
|
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
2024-05-02 00:11:32 +08:00
|
|
|
#include "clang/Frontend/ASTUnit.h"
|
2018-09-11 21:13:20 +00:00
|
|
|
#include "clang/Tooling/Tooling.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <cctype>
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
using ::testing::ElementsAre;
|
|
|
|
using ::testing::ResultOf;
|
|
|
|
using ::testing::Values;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using ExprMatcher = internal::Matcher<Expr>;
|
|
|
|
using StmtMatcher = internal::Matcher<Stmt>;
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
std::unique_ptr<ASTUnit>
|
|
|
|
buildASTFromCodeWithArgs(const Twine &Code,
|
|
|
|
const std::vector<std::string> &Args) {
|
2019-01-08 16:55:13 +00:00
|
|
|
SmallString<1024> CodeStorage;
|
|
|
|
auto AST =
|
|
|
|
tooling::buildASTFromCodeWithArgs(Code.toStringRef(CodeStorage), Args);
|
2018-09-19 03:50:03 +00:00
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
return AST;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<ASTUnit> buildASTFromCode(const Twine &Code) {
|
|
|
|
return buildASTFromCodeWithArgs(Code, {});
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
ExprMatcher declRefTo(StringRef Name) {
|
2024-05-02 00:11:32 +08:00
|
|
|
return declRefExpr(to(namedDecl(hasName(Name)).bind("decl")));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
|
|
|
|
return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isMutated(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) {
|
|
|
|
const auto *const S = selectFirst<Stmt>("stmt", Results);
|
|
|
|
const auto *const E = selectFirst<Expr>("expr", Results);
|
2020-12-11 00:52:35 +01:00
|
|
|
TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
|
2018-09-11 21:13:20 +00:00
|
|
|
return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
|
|
|
|
}
|
|
|
|
|
2024-05-02 00:11:32 +08:00
|
|
|
bool isDeclMutated(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) {
|
|
|
|
const auto *const S = selectFirst<Stmt>("stmt", Results);
|
|
|
|
const auto *const D = selectFirst<Decl>("decl", Results);
|
|
|
|
TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
|
|
|
|
return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(D);
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
SmallVector<std::string, 1>
|
|
|
|
mutatedBy(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) {
|
|
|
|
const auto *const S = selectFirst<Stmt>("stmt", Results);
|
|
|
|
SmallVector<std::string, 1> Chain;
|
|
|
|
ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
for (const auto *E = selectFirst<Expr>("expr", Results); E != nullptr;) {
|
|
|
|
const Stmt *By = Analyzer.findMutation(E);
|
2020-10-09 13:17:59 +02:00
|
|
|
if (!By)
|
|
|
|
break;
|
|
|
|
|
|
|
|
std::string Buffer;
|
|
|
|
llvm::raw_string_ostream Stream(Buffer);
|
|
|
|
By->printPretty(Stream, nullptr, AST->getASTContext().getPrintingPolicy());
|
2024-09-14 04:37:40 -04:00
|
|
|
Chain.emplace_back(StringRef(Buffer).trim().str());
|
2018-09-11 21:13:20 +00:00
|
|
|
E = dyn_cast<DeclRefExpr>(By);
|
|
|
|
}
|
|
|
|
return Chain;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string removeSpace(std::string s) {
|
|
|
|
s.erase(std::remove_if(s.begin(), s.end(),
|
2020-05-02 15:34:53 +02:00
|
|
|
[](char c) { return llvm::isSpace(c); }),
|
2018-09-11 21:13:20 +00:00
|
|
|
s.end());
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2018-09-17 20:10:56 +00:00
|
|
|
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&& "
|
2018-09-19 03:50:03 +00:00
|
|
|
"forward(typename remove_reference<T>::type&& t) noexcept { return t; } }";
|
2018-09-17 20:10:56 +00:00
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, Trivial) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x; x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
class AssignmentTest : public ::testing::TestWithParam<std::string> {};
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// This test is for the most basic and direct modification of a variable,
|
|
|
|
// assignment to it (e.g. `x = 10;`).
|
|
|
|
// It additionally tests that references to a variable are not only captured
|
|
|
|
// directly but expressions that result in the variable are handled, too.
|
|
|
|
// This includes the comma operator, parens and the ternary operator.
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST_P(AssignmentTest, AssignmentModifies) {
|
2020-10-09 13:17:59 +02:00
|
|
|
// Test the detection of the raw expression modifications.
|
2020-06-09 19:43:48 +01:00
|
|
|
{
|
|
|
|
const std::string ModExpr = "x " + GetParam() + " 10";
|
|
|
|
const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// Test the detection if the expression is surrounded by parens.
|
2020-06-09 19:43:48 +01:00
|
|
|
{
|
|
|
|
const std::string ModExpr = "(x) " + GetParam() + " 10";
|
|
|
|
const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
2020-10-09 13:17:59 +02:00
|
|
|
|
|
|
|
// Test the detection if the comma operator yields the expression as result.
|
|
|
|
{
|
|
|
|
const std::string ModExpr = "x " + GetParam() + " 10";
|
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"void f() { int x, y; y, " + ModExpr + "; }", {"-Wno-unused-value"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure no detection if the comma operator does not yield the expression as
|
|
|
|
// result.
|
|
|
|
{
|
|
|
|
const std::string ModExpr = "y, x, y " + GetParam() + " 10";
|
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"void f() { int x, y; " + ModExpr + "; }", {"-Wno-unused-value"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test the detection if the a ternary operator can result in the expression.
|
|
|
|
{
|
|
|
|
const std::string ModExpr = "(y != 0 ? y : x) " + GetParam() + " 10";
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test the detection if the a ternary operator can result in the expression
|
|
|
|
// through multiple nesting of ternary operators.
|
|
|
|
{
|
|
|
|
const std::string ModExpr =
|
|
|
|
"(y != 0 ? (y > 5 ? y : x) : (y)) " + GetParam() + " 10";
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test the detection if the a ternary operator can result in the expression
|
|
|
|
// with additional parens.
|
|
|
|
{
|
|
|
|
const std::string ModExpr = "(y != 0 ? (y) : ((x))) " + GetParam() + " 10";
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test the detection for the binary conditional operator.
|
|
|
|
{
|
|
|
|
const std::string ModExpr = "(y ?: x) " + GetParam() + " 10";
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 19:15:20 +02:00
|
|
|
INSTANTIATE_TEST_SUITE_P(AllAssignmentOperators, AssignmentTest,
|
2018-09-11 21:13:20 +00:00
|
|
|
Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
|
2021-05-14 19:15:20 +02:00
|
|
|
"^=", "<<=", ">>=") );
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, AssignmentConditionalWithInheritance) {
|
|
|
|
const auto AST = buildASTFromCode("struct Base {void nonconst(); };"
|
|
|
|
"struct Derived : Base {};"
|
|
|
|
"static void f() {"
|
|
|
|
" Derived x, y;"
|
|
|
|
" Base &b = true ? x : y;"
|
|
|
|
" b.nonconst();"
|
|
|
|
"}");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("b", "b.nonconst()"));
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
class IncDecTest : public ::testing::TestWithParam<std::string> {};
|
|
|
|
|
|
|
|
TEST_P(IncDecTest, IncDecModifies) {
|
|
|
|
const std::string ModExpr = GetParam();
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
|
|
|
|
}
|
|
|
|
|
2021-05-14 19:15:20 +02:00
|
|
|
INSTANTIATE_TEST_SUITE_P(AllIncDecOperators, IncDecTest,
|
2020-06-09 19:43:48 +01:00
|
|
|
Values("++x", "--x", "x++", "x--", "++(x)", "--(x)",
|
2021-05-14 19:15:20 +02:00
|
|
|
"(x)++", "(x)--") );
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// Section: member functions
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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>()"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs("template <class T> void f() { T x; x.mf(); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, TypeDependentMemberCall) {
|
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"template <class T> class vector { void push_back(T); }; "
|
|
|
|
"template <class T> void f() { vector<T> x; x.push_back(T()); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.push_back(T())"));
|
|
|
|
}
|
|
|
|
|
2023-09-26 07:40:15 +08:00
|
|
|
TEST(ExprMutationAnalyzerTest, MemberPointerMemberCall) {
|
|
|
|
{
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("struct X {};"
|
|
|
|
"using T = int (X::*)();"
|
|
|
|
"void f(X &x, T m) { X &ref = x; (ref.*m)(); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()"));
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("struct X {};"
|
|
|
|
"using T = int (X::*)();"
|
|
|
|
"void f(X &x, T const m) { X &ref = x; (ref.*m)(); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()"));
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("struct X {};"
|
|
|
|
"using T = int (X::*)() const;"
|
|
|
|
"void f(X &x, T m) { X &ref = x; (ref.*m)(); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// Section: overloaded operators
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, NonConstOperator) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct Foo { Foo& operator=(int); }; Foo x; x = 10; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x = 10"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ConstOperator) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct Foo { int operator()() const; }; Foo x; x(); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, UnresolvedOperator) {
|
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"template <typename Stream> void input_operator_template() {"
|
|
|
|
"Stream x; unsigned y = 42;"
|
|
|
|
"x >> y; }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2023-11-14 23:00:18 +01:00
|
|
|
TEST(ExprMutationAnalyzerTest, DependentOperatorWithNonDependentOperand) {
|
|
|
|
// gh57297
|
|
|
|
// The expression to check may not be the dependent operand in a dependent
|
|
|
|
// operator.
|
|
|
|
|
|
|
|
// Explicitly not declaring a (templated) stream operator
|
|
|
|
// so the `<<` is a `binaryOperator` with a dependent type.
|
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"struct Stream { };"
|
|
|
|
"template <typename T> void f() { T t; Stream x; x << t; }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << t"));
|
|
|
|
}
|
|
|
|
|
2024-01-22 21:51:39 +01:00
|
|
|
TEST(ExprMutationAnalyzerTest, FoldExpression) {
|
|
|
|
// gh70323
|
|
|
|
// A fold expression may contain `Exp` as it's initializer.
|
|
|
|
// We don't know if the operator modifies `Exp` because the
|
|
|
|
// operator is type dependent due to the parameter pack.
|
|
|
|
auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"struct Stream {};"
|
|
|
|
"template <typename... Args> void concatenate(Args... args) "
|
|
|
|
"{ Stream x; (x << ... << args); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x << ... << args)"));
|
|
|
|
|
|
|
|
AST = buildASTFromCodeWithArgs(
|
|
|
|
"struct Stream {};"
|
|
|
|
"template <typename... Args> void concatenate(Args... args) "
|
|
|
|
"{ Stream x; (args << ... << x); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(args << ... << x)"));
|
|
|
|
|
|
|
|
AST = buildASTFromCodeWithArgs(
|
|
|
|
"struct Stream {};"
|
|
|
|
"template <typename... Args> void concatenate(Args... args) "
|
|
|
|
"{ Stream x; (..., (x << args)); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << args"));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// Section: expression as call argument
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, ByValueArgument) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void g(int); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void g(int*); void f() { int* x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int* IntPtr;"
|
|
|
|
"void g(IntPtr); void f() { int* x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-19 20:27:25 +00:00
|
|
|
"struct A {}; A operator+(A, int); void f() { A x; x + 1; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(int); }; int x; A y(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("struct A { A(); A& operator=(A); };"
|
|
|
|
"void f() { A x, y; y = x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
2018-09-19 18:00:55 +00:00
|
|
|
|
|
|
|
AST = buildASTFromCode(
|
|
|
|
"template <int> struct A { A(); A(const A&); static void mf(A) {} };"
|
|
|
|
"void f() { A<0> x; A<0>::mf(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ByConstValueArgument) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void g(const int); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void g(int* const); void f() { int* x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int* const CIntPtr;"
|
|
|
|
"void g(CIntPtr); void f() { int* x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-19 20:27:25 +00:00
|
|
|
"struct A {}; A operator+(const A, int); void f() { A x; x + 1; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct A { A(const int); }; int x; A y(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
2018-09-19 18:00:55 +00:00
|
|
|
|
|
|
|
AST = buildASTFromCode("template <int> struct A { A(); A(const A&);"
|
|
|
|
"static void mf(const A&) {} };"
|
|
|
|
"void f() { A<0> x; A<0>::mf(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void g(int&); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int& IntRef;"
|
|
|
|
"void g(IntRef); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("template <class T> using TRef = T&;"
|
|
|
|
"void g(TRef<int>); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"template <class T> struct identity { using type = T; };"
|
|
|
|
"template <class T, class U = T&> void g(typename identity<U>::type);"
|
|
|
|
"void f() { int x; g<int>(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g<int>(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int* IntPtr;"
|
|
|
|
"void g(IntPtr&); void f() { int* x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int* IntPtr; typedef IntPtr& IntPtrRef;"
|
|
|
|
"void g(IntPtrRef); void f() { int* x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-19 20:27:25 +00:00
|
|
|
"struct A {}; A operator+(A&, int); void f() { A x; x + 1; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x + 1"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(int&); }; int x; A y(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(); A(A&); }; A x; A y(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
2018-09-19 18:00:55 +00:00
|
|
|
|
|
|
|
AST = buildASTFromCode(
|
|
|
|
"template <int> struct A { A(); A(const A&); static void mf(A&) {} };"
|
|
|
|
"void f() { A<0> x; A<0>::mf(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("A<0>::mf(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, ByNonConstRefArgumentFunctionTypeDependent) {
|
|
|
|
auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"enum MyEnum { foo, bar };"
|
|
|
|
"void tryParser(unsigned& first, MyEnum Type) { first++, (void)Type; }"
|
|
|
|
"template <MyEnum Type> void parse() {"
|
|
|
|
" auto parser = [](unsigned& first) { first++; tryParser(first, Type); "
|
|
|
|
"};"
|
|
|
|
" unsigned x = 42;"
|
|
|
|
" parser(x);"
|
|
|
|
"}",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("parser(x)"));
|
|
|
|
}
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, ByConstRefArgument) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void g(const int&); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef const int& CIntRef;"
|
|
|
|
"void g(CIntRef); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("template <class T> using CTRef = const T&;"
|
|
|
|
"void g(CTRef<int>); void f() { int x; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST =
|
|
|
|
buildASTFromCode("template <class T> struct identity { using type = T; };"
|
|
|
|
"template <class T, class U = const T&>"
|
|
|
|
"void g(typename identity<U>::type);"
|
|
|
|
"void f() { int x; g<int>(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-19 20:27:25 +00:00
|
|
|
"struct A {}; A operator+(const A&, int); void f() { A x; x + 1; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct A { A(const int&); }; int x; A y(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { struct A { A(); A(const A&); }; A x; A y(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ByNonConstRRefArgument) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void g(int&&); void f() { int x; g(static_cast<int &&>(x)); }");
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("g(static_cast<int &&>(x))"));
|
|
|
|
|
2018-09-19 20:27:25 +00:00
|
|
|
AST = buildASTFromCode("struct A {}; A operator+(A&&, int);"
|
2018-09-19 03:50:03 +00:00
|
|
|
"void f() { A x; static_cast<A &&>(x) + 1; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<A &&>(x) + 1"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(int&&); }; "
|
|
|
|
"int x; A y(static_cast<int &&>(x)); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<int &&>(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(); A(A&&); }; "
|
|
|
|
"A x; A y(static_cast<A &&>(x)); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<A &&>(x)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ByConstRRefArgument) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void g(const int&&); void f() { int x; g(static_cast<int&&>(x)); }");
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<int &&>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2018-09-19 20:27:25 +00:00
|
|
|
AST = buildASTFromCode("struct A {}; A operator+(const A&&, int);"
|
2018-09-19 03:50:03 +00:00
|
|
|
"void f() { A x; static_cast<A&&>(x) + 1; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<A &&>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(const int&&); }; "
|
|
|
|
"int x; A y(static_cast<int&&>(x)); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<int &&>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { struct A { A(); A(const A&&); }; "
|
|
|
|
"A x; A y(static_cast<A&&>(x)); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<A &&>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
2024-01-16 10:03:19 +01:00
|
|
|
// Section: bindings.
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, BindingModifies) {
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("struct Point { int x; int y; };"
|
|
|
|
"void mod(int&);"
|
|
|
|
"void f(Point p) { auto& [x, y] = p; mod(x); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x", "mod(x)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, BindingDoesNotModify) {
|
|
|
|
const auto AST = buildASTFromCode("struct Point { int x; int y; };"
|
|
|
|
"void f(Point p) { auto& [x, y] = p; x; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: explicit std::move and std::forward testing
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, Move) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"void f() { struct A {}; A x; std::move(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
auto Results =
|
2018-09-11 21:13:20 +00:00
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2018-09-17 20:10:56 +00:00
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"void f() { struct A {}; A x, y; std::move(x) = y; }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"void f() { int x, y; y = std::move(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST =
|
|
|
|
buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"struct S { S(); S(const S&); S& operator=(const S&); };"
|
|
|
|
"void f() { S x, y; y = std::move(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"struct S { S(); S(S&&); S& operator=(S&&); };"
|
|
|
|
"void f() { S x, y; y = std::move(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = 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); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = 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); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"struct S { S(); S& operator=(S); };"
|
|
|
|
"void f() { S x, y; y = std::move(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(StdRemoveReference + StdMove +
|
|
|
|
"struct S{}; void f() { S x, y; y = std::move(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-17 20:10:56 +00:00
|
|
|
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()));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, Forward) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode(StdRemoveReference + StdForward +
|
|
|
|
"void f() { struct A {}; A x; std::forward<A &>(x); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
auto Results =
|
2018-09-11 21:13:20 +00:00
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2018-09-17 20:10:56 +00:00
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-17 20:10:56 +00:00
|
|
|
StdRemoveReference + StdForward +
|
|
|
|
"void f() { struct A {}; A x, y; std::forward<A &>(x) = y; }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2018-09-11 21:13:20 +00:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
2018-09-17 20:10:56 +00:00
|
|
|
ElementsAre("std::forward<A &>(x) = y"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: template constellations that prohibit reasoning about modifications
|
|
|
|
// as it depends on instantiations.
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, CallUnresolved) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCodeWithArgs("template <class T> void f() { T x; g(x); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST =
|
|
|
|
buildASTFromCodeWithArgs("template <int N> void f() { char x[N]; g(x); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs("template <class F>"
|
|
|
|
"void g(F f) { int x; f(x); } ",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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)"));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: return values
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("int f() { int x; return x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("int* f() { int* x; return x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int* IntPtr;"
|
|
|
|
"IntPtr f() { int* x; return x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("int& f() { int x; return x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("return x;"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ReturnAsConstRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("const int& f() { int x; return x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("int&& f() { int x; return static_cast<int &&>(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
2020-10-09 13:17:59 +02:00
|
|
|
ElementsAre("static_cast<int &&>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ReturnAsConstRRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"const int&& f() { int x; return static_cast<int&&>(x); }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<int &&>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: taking the address of a variable and pointers
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, TakeAddress) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void g(int*); void f() { int x; g(&x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("&x"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) {
|
|
|
|
const auto AST =
|
2018-09-19 03:50:03 +00:00
|
|
|
buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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"));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: special case: all created references are non-mutating themself
|
|
|
|
// and therefore all become 'const'/the value is not modified!
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, FollowRefModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
|
|
|
|
"int& r3 = r2; r3 = 10; }");
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("r0", "r1", "r2", "r3", "r3 = 10"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int& IntRefX;"
|
|
|
|
"using IntRefY = int&;"
|
|
|
|
"void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;"
|
|
|
|
"decltype((x)) r2 = r1; r2 = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("r0", "r1", "r2", "r2 = 10"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, FollowRefNotModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
|
|
|
|
"int& r3 = r2; int& r4 = r3; int& r5 = r4;}");
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { int x; int& r0 = x; const int& r1 = r0;}");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef const int& CIntRefX;"
|
|
|
|
"using CIntRefY = const int&;"
|
|
|
|
"void f() { int x; int& r0 = x; CIntRefX r1 = r0;"
|
|
|
|
"CIntRefY r2 = r1; decltype((r1)) r3 = r2;}");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, FollowConditionalRefModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { int x, y; bool b; int &r = b ? x : y; r = 10; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("r", "r = 10"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, FollowConditionalRefNotModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST =
|
|
|
|
buildASTFromCode("void f() { int x, y; bool b; int& r = b ? x : y; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2018-09-14 20:07:18 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("template <class T> void g(T&& t) { t = 10; }"
|
|
|
|
"void f() { int x; g(x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-14 20:07:18 +00:00
|
|
|
"void h(int&);"
|
|
|
|
"template <class... Args> void g(Args&&... args) { h(args...); }"
|
|
|
|
"void f() { int x; g(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-14 20:07:18 +00:00
|
|
|
"void h(int&, int);"
|
|
|
|
"template <class... Args> void g(Args&&... args) { h(args...); }"
|
|
|
|
"void f() { int x, y; g(x, y); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x, y)"));
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-14 20:07:18 +00:00
|
|
|
"void h(int, int&);"
|
|
|
|
"template <class... Args> void g(Args&&... args) { h(args...); }"
|
|
|
|
"void f() { int x, y; g(y, x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(y, x)"));
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("struct S { template <class T> S(T&& t) { t = 10; } };"
|
|
|
|
"void f() { int x; S s(x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-14 20:07:18 +00:00
|
|
|
"struct S { template <class T> S(T&& t) : m(++t) { } int m; };"
|
|
|
|
"void f() { int x; S s(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
2018-09-17 20:10:56 +00:00
|
|
|
|
2018-09-19 18:00:55 +00:00
|
|
|
AST = buildASTFromCode("template <class U> struct S {"
|
|
|
|
"template <class T> S(T&& t) : m(++t) { } U m; };"
|
|
|
|
"void f() { int x; S<int> s(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = 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); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
|
2024-04-17 09:57:30 +08:00
|
|
|
|
|
|
|
AST = buildASTFromCode(
|
|
|
|
StdRemoveReference + StdForward +
|
|
|
|
"template <class T> void f1(T &&a);"
|
|
|
|
"template <class T> void f2(T &&a);"
|
|
|
|
"template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }"
|
|
|
|
"template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }"
|
|
|
|
"void f() { int x; f1(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
|
|
|
AST = buildASTFromCode(
|
|
|
|
StdRemoveReference + StdForward +
|
|
|
|
"template <class T> void f1(T &&a);"
|
|
|
|
"template <class T> void f2(T &&a);"
|
|
|
|
"template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }"
|
|
|
|
"template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); a++; }"
|
|
|
|
"void f() { int x; f1(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f1(x)"));
|
|
|
|
|
|
|
|
AST = buildASTFromCode(
|
|
|
|
StdRemoveReference + StdForward +
|
|
|
|
"template <class T> void f1(T &&a);"
|
|
|
|
"template <class T> void f2(T &&a);"
|
|
|
|
"template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); a++; }"
|
|
|
|
"template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }"
|
|
|
|
"void f() { int x; f1(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f1(x)"));
|
2018-09-14 20:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("template <class T> void g(T&&) {}"
|
|
|
|
"void f() { int x; g(x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("template <class T> void g(T&& t) { t; }"
|
|
|
|
"void f() { int x; g(x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("template <class... Args> void g(Args&&...) {}"
|
|
|
|
"void f() { int x; g(x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("template <class... Args> void g(Args&&...) {}"
|
|
|
|
"void f() { int y, x; g(y, x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-14 20:07:18 +00:00
|
|
|
"void h(int, int&);"
|
|
|
|
"template <class... Args> void g(Args&&... args) { h(args...); }"
|
|
|
|
"void f() { int x, y; g(x, y); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("struct S { template <class T> S(T&& t) { t; } };"
|
|
|
|
"void f() { int x; S s(x); }");
|
2018-09-14 20:07:18 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-14 20:07:18 +00:00
|
|
|
"struct S { template <class T> S(T&& t) : m(t) { } int m; };"
|
|
|
|
"void f() { int x; S s(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
2018-09-17 20:10:56 +00:00
|
|
|
|
2018-09-19 18:00:55 +00:00
|
|
|
AST = buildASTFromCode("template <class U> struct S {"
|
|
|
|
"template <class T> S(T&& t) : m(t) { } U m; };"
|
|
|
|
"void f() { int x; S<int> s(x); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = 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); }");
|
2018-09-17 20:10:56 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
2018-09-14 20:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: builtin arrays
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, ArrayElementModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x[2]; x[0] = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x[0] = 10"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x[2]; x[0]; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: member modifications
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, NestedMemberModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; "
|
|
|
|
"struct C { B vb; }; C x; x.vb.va.vi = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"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) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; "
|
|
|
|
"struct C { B vb; }; C x; x.vb.va.vi; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs("template <class T> void f() { T x; x.y.z; }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST =
|
|
|
|
buildASTFromCodeWithArgs("template <class T> struct S;"
|
|
|
|
"template <class T> void f() { S<T> x; x.y.z; }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: casts
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, CastToValue) {
|
|
|
|
const auto AST =
|
2018-09-19 03:50:03 +00:00
|
|
|
buildASTFromCode("void f() { int x; static_cast<double>(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CastToRefModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode("void f() { int x; static_cast<int &>(x) = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
2020-10-09 13:17:59 +02:00
|
|
|
ElementsAre("static_cast<int &>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int& IntRef;"
|
|
|
|
"void f() { int x; static_cast<IntRef>(x) = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
2020-10-09 13:17:59 +02:00
|
|
|
ElementsAre("static_cast<IntRef>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CastToRefNotModified) {
|
|
|
|
const auto AST =
|
2018-09-19 03:50:03 +00:00
|
|
|
buildASTFromCode("void f() { int x; static_cast<int&>(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("static_cast<int &>(x)"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CastToConstRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode("void f() { int x; static_cast<const int&>(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef const int& CIntRef;"
|
|
|
|
"void f() { int x; static_cast<CIntRef>(x); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: comma expressions
|
|
|
|
|
2024-04-30 21:30:55 +08:00
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithAnAssignment) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"void f() { int x; int y; (x, y) = 5; }", {"-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithDecOp) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"void f() { int x; int y; (x, y)++; }", {"-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithNonConstMemberCall) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"class A { public: int mem; void f() { mem ++; } };"
|
|
|
|
"void fn() { A o1, o2; (o1, o2).f(); }",
|
|
|
|
{"-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("o2")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithConstMemberCall) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"class A { public: int mem; void f() const { } };"
|
|
|
|
"void fn() { A o1, o2; (o1, o2).f(); }",
|
|
|
|
{"-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("o2")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithCallExpr) {
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCodeWithArgs("class A { public: int mem; void f(A &O1) {} };"
|
|
|
|
"void fn() { A o1, o2; o2.f((o2, o1)); }",
|
|
|
|
{"-Wno-unused-value"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithCallUnresolved) {
|
|
|
|
auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"template <class T> struct S;"
|
|
|
|
"template <class T> void f() { S<T> s; int x, y; s.mf((y, x)); }",
|
2019-03-07 18:57:04 +00:00
|
|
|
{"-fno-delayed-template-parsing", "-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
|
|
|
|
AST = buildASTFromCodeWithArgs(
|
|
|
|
"template <class T> void f(T t) { int x, y; g(t, (y, x)); }",
|
2019-03-07 18:57:04 +00:00
|
|
|
{"-fno-delayed-template-parsing", "-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprParmRef) {
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCodeWithArgs("class A { public: int mem;};"
|
|
|
|
"extern void fn(A &o1);"
|
|
|
|
"void fn2 () { A o1, o2; fn((o2, o1)); } ",
|
|
|
|
{"-Wno-unused-value"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprWithAmpersandOp) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const auto AST = buildASTFromCodeWithArgs("class A { public: int mem;};"
|
|
|
|
"void fn () { A o1, o2;"
|
|
|
|
"void *addr = &(o2, o1); } ",
|
|
|
|
{"-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprAsReturnAsValue) {
|
|
|
|
auto AST = buildASTFromCodeWithArgs("int f() { int x, y; return (x, y); }",
|
|
|
|
{"-Wno-unused-value"});
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2024-04-30 21:30:55 +08:00
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprAsReturnAsNonConstRef) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
"int& f() { int x, y; return (y, x); }", {"-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprAsArrayToPointerDecay) {
|
|
|
|
const auto AST =
|
|
|
|
buildASTFromCodeWithArgs("void g(int*); "
|
|
|
|
"void f() { int x[2], y[2]; g((y, x)); }",
|
|
|
|
{"-Wno-unused-value"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, CommaExprAsUniquePtr) {
|
2020-01-10 16:10:55 +01:00
|
|
|
const std::string UniquePtrDef = "template <class T> struct UniquePtr {"
|
|
|
|
" UniquePtr();"
|
|
|
|
" UniquePtr(const UniquePtr&) = delete;"
|
|
|
|
" T& operator*() const;"
|
|
|
|
" T* operator->() const;"
|
|
|
|
"};";
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto AST = buildASTFromCodeWithArgs(
|
|
|
|
UniquePtrDef + "template <class T> void f() "
|
|
|
|
"{ UniquePtr<T> x; UniquePtr<T> y;"
|
|
|
|
" (y, x)->mf(); }",
|
2019-03-07 18:57:04 +00:00
|
|
|
{"-fno-delayed-template-parsing", "-Wno-unused-value"});
|
2019-03-07 15:50:52 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, CommaNestedConditional) {
|
|
|
|
const std::string Code = "void f() { int x, y = 42;"
|
|
|
|
" y, (true ? x : y) = 42; }";
|
|
|
|
const auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("(true ? x : y) = 42"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// section: lambda captures
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x; [=]() { x; }; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByValue) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x; [x]() { x; }; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x; [&]() { x = 10; }; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre(ResultOf(removeSpace, "[&](){x=10;}")));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("void f() { int x; [&x]() { x = 10; }; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre(ResultOf(removeSpace, "[&x](){x=10;}")));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: range-for loops
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode("void f() { int x[2]; for (int& e : x) e = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("for (int &e : x)\n e = 10;"));
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef int& IntRef;"
|
|
|
|
"void f() { int x[2]; for (IntRef e : x) e = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("for (IntRef e : x)\n e = 10;"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModifiedByImplicitInit) {
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto AST =
|
2018-09-19 03:50:03 +00:00
|
|
|
buildASTFromCode("void f() { int x[2]; for (int& e : x) e; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForArrayByValue) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void f() { int x[2]; for (int e : x) e = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST =
|
|
|
|
buildASTFromCode("void f() { int* x[2]; for (int* e : x) e = nullptr; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2022-07-24 19:35:52 +02:00
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
2018-09-11 21:13:20 +00:00
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"typedef int* IntPtr;"
|
|
|
|
"void f() { int* x[2]; for (IntPtr e : x) e = nullptr; }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2022-07-24 19:35:52 +02:00
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForArrayByConstRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST =
|
|
|
|
buildASTFromCode("void f() { int x[2]; for (const int& e : x) e; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("typedef const int& CIntRef;"
|
|
|
|
"void f() { int x[2]; for (CIntRef e : x) e; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefModified) {
|
|
|
|
const auto AST =
|
2018-09-19 03:50:03 +00:00
|
|
|
buildASTFromCode("struct V { int* begin(); int* end(); };"
|
|
|
|
"void f() { V x; for (int& e : x) e = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()),
|
|
|
|
ElementsAre("for (int &e : x)\n e = 10;"));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefNotModified) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode("struct V { int* begin(); int* end(); };"
|
|
|
|
"void f() { V x; for (int& e : x) e; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
2020-10-09 13:17:59 +02:00
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
2018-09-11 21:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForNonArrayByValue) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"struct V { const int* begin() const; const int* end() const; };"
|
|
|
|
"void f() { V x; for (int e : x) e; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, RangeForNonArrayByConstRef) {
|
2018-09-19 03:50:03 +00:00
|
|
|
const auto AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"struct V { const int* begin() const; const int* end() const; };"
|
|
|
|
"void f() { V x; for (const int& e : x) e; }");
|
|
|
|
const auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: unevaluated expressions
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, UnevaluatedExpressions) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void f() { int x, y; decltype(x = 10) z = y; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { int x, y; __typeof(x = 10) z = y; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { int x, y; __typeof__(x = 10) z = y; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { int x; sizeof(x = 10); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { int x; alignof(x = 10); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode("void f() { int x; noexcept(x = 10); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs("namespace std { class type_info; }"
|
|
|
|
"void f() { int x; typeid(x = 10); }",
|
|
|
|
{"-frtti"});
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(
|
2018-09-11 21:13:20 +00:00
|
|
|
"void f() { int x; _Generic(x = 10, int: 0, default: 1); }");
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExprMutationAnalyzerTest, NotUnevaluatedExpressions) {
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode("void f() { int x; sizeof(int[x++]); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x++"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
"namespace std { class type_info; }"
|
|
|
|
"struct A { virtual ~A(); }; struct B : A {};"
|
|
|
|
"struct X { A& f(); }; void f() { X x; typeid(x.f()); }",
|
|
|
|
{"-frtti"});
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()"));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: special case: smartpointers
|
|
|
|
|
2018-09-11 21:13:20 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, UniquePtr) {
|
|
|
|
const std::string UniquePtrDef =
|
|
|
|
"template <class T> struct UniquePtr {"
|
|
|
|
" UniquePtr();"
|
|
|
|
" UniquePtr(const UniquePtr&) = delete;"
|
|
|
|
" UniquePtr(UniquePtr&&);"
|
|
|
|
" UniquePtr& operator=(const UniquePtr&) = delete;"
|
|
|
|
" UniquePtr& operator=(UniquePtr&&);"
|
|
|
|
" T& operator*() const;"
|
|
|
|
" T* operator->() const;"
|
|
|
|
"};";
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
auto AST = buildASTFromCode(UniquePtrDef +
|
|
|
|
"void f() { UniquePtr<int> x; *x = 10; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(UniquePtrDef + "void f() { UniquePtr<int> x; *x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(UniquePtrDef +
|
|
|
|
"void f() { UniquePtr<const int> x; *x; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(UniquePtrDef + "struct S { int v; };"
|
|
|
|
"void f() { UniquePtr<S> x; x->v; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(UniquePtrDef +
|
|
|
|
"struct S { int v; };"
|
|
|
|
"void f() { UniquePtr<const S> x; x->v; }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST =
|
|
|
|
buildASTFromCode(UniquePtrDef + "struct S { void mf(); };"
|
|
|
|
"void f() { UniquePtr<S> x; x->mf(); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCode(UniquePtrDef +
|
|
|
|
"struct S { void mf() const; };"
|
|
|
|
"void f() { UniquePtr<const S> x; x->mf(); }");
|
2018-09-11 21:13:20 +00:00
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results, AST.get()));
|
|
|
|
|
2018-09-19 03:50:03 +00:00
|
|
|
AST = buildASTFromCodeWithArgs(
|
2018-09-11 21:13:20 +00:00
|
|
|
UniquePtrDef + "template <class T> void f() { UniquePtr<T> x; x->mf(); }",
|
|
|
|
{"-fno-delayed-template-parsing"});
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()"));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
// section: complex problems detected on real code
|
|
|
|
|
2024-05-02 00:11:32 +08:00
|
|
|
TEST(ExprMutationAnalyzerTest, SelfRef) {
|
|
|
|
std::unique_ptr<ASTUnit> AST{};
|
|
|
|
SmallVector<BoundNodes, 1> Results{};
|
|
|
|
|
|
|
|
AST = buildASTFromCodeWithArgs("void f() { int &x = x; }",
|
|
|
|
{"-Wno-unused-value", "-Wno-uninitialized"});
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_FALSE(isDeclMutated(Results, AST.get()));
|
|
|
|
|
|
|
|
AST = buildASTFromCodeWithArgs("void f() { int &x = x; x = 1; }",
|
|
|
|
{"-Wno-unused-value", "-Wno-uninitialized"});
|
|
|
|
Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isDeclMutated(Results, AST.get()));
|
|
|
|
}
|
|
|
|
|
2020-10-09 13:17:59 +02:00
|
|
|
TEST(ExprMutationAnalyzerTest, UnevaluatedContext) {
|
|
|
|
const std::string Example =
|
|
|
|
"template <typename T>"
|
|
|
|
"struct to_construct : T { to_construct(int &j) {} };"
|
|
|
|
"template <typename T>"
|
|
|
|
"void placement_new_in_unique_ptr() { int x = 0;"
|
|
|
|
" new to_construct<T>(x);"
|
|
|
|
"}";
|
|
|
|
auto AST =
|
|
|
|
buildASTFromCodeWithArgs(Example, {"-fno-delayed-template-parsing"});
|
|
|
|
auto Results =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
|
|
|
|
EXPECT_TRUE(isMutated(Results, AST.get()));
|
|
|
|
EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x)"));
|
|
|
|
}
|
|
|
|
|
2019-01-21 13:26:18 +00:00
|
|
|
TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) {
|
|
|
|
const std::string Reproducer =
|
|
|
|
"namespace std {"
|
Treat `std::move`, `forward`, etc. as builtins.
This is extended to all `std::` functions that take a reference to a
value and return a reference (or pointer) to that same value: `move`,
`forward`, `move_if_noexcept`, `as_const`, `addressof`, and the
libstdc++-specific function `__addressof`.
We still require these functions to be declared before they can be used,
but don't instantiate their definitions unless their addresses are
taken. Instead, code generation, constant evaluation, and static
analysis are given direct knowledge of their effect.
This change aims to reduce various costs associated with these functions
-- per-instantiation memory costs, compile time and memory costs due to
creating out-of-line copies and inlining them, code size at -O0, and so
on -- so that they are not substantially more expensive than a cast.
Most of these improvements are very small, but I measured a 3% decrease
in -O0 object file size for a simple C++ source file using the standard
library after this change.
We now automatically infer the `const` and `nothrow` attributes on these
now-builtin functions, in particular meaning that we get a warning for
an unused call to one of these functions.
In C++20 onwards, we disallow taking the addresses of these functions,
per the C++20 "addressable function" rule. In earlier language modes, a
compatibility warning is produced but the address can still be taken.
The same infrastructure is extended to the existing MSVC builtin
`__GetExceptionInfo`, which is now only recognized in namespace `std`
like it always should have been.
This is a re-commit of
fc3090109643af8d2da9822d0f99c84742b9c877,
a571f82a50416b767fd3cce0fb5027bb5dfec58c,
64c045e25b8471bbb572bd29159c294a82a86a2, and
de6ddaeef3aaa8a9ae3663c12cdb57d9afc0f906,
and reverts aa643f455a5362de7189eac630050d2c8aefe8f2.
This change also includes a workaround for users using libc++ 3.1 and
earlier (!!), as apparently happens on AIX, where std::move sometimes
returns by value.
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D123345
Revert "Fixup D123950 to address revert of D123345"
This reverts commit aa643f455a5362de7189eac630050d2c8aefe8f2.
2022-04-20 17:13:56 -07:00
|
|
|
"template <class T> T &forward(T &A) { return static_cast<T&&>(A); }"
|
2019-01-21 13:26:18 +00:00
|
|
|
"template <class T> struct __bind {"
|
|
|
|
" T f;"
|
|
|
|
" template <class V> __bind(T v, V &&) : f(forward(v)) {}"
|
|
|
|
"};"
|
|
|
|
"}"
|
|
|
|
"void f() {"
|
|
|
|
" int x = 42;"
|
|
|
|
" auto Lambda = [] {};"
|
|
|
|
" std::__bind<decltype(Lambda)>(Lambda, x);"
|
|
|
|
"}";
|
|
|
|
auto AST11 = buildASTFromCodeWithArgs(Reproducer, {"-std=c++11"});
|
|
|
|
auto Results11 =
|
|
|
|
match(withEnclosingCompound(declRefTo("x")), AST11->getASTContext());
|
|
|
|
EXPECT_FALSE(isMutated(Results11, AST11.get()));
|
|
|
|
}
|
2018-09-11 21:13:20 +00:00
|
|
|
} // namespace clang
|