-
Notifications
You must be signed in to change notification settings - Fork 12.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[mutation analyzer] support mutation analysis for pointee #118593
Open
HerrCai0907
wants to merge
2
commits into
llvm:main
Choose a base branch
from
HerrCai0907:support-check-mutation-pointee
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+492
−3
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
04e220e to
019ee3d
Compare
019ee3d to
0165704
Compare
9c38149 to
58b6e46
Compare
|
@llvm/pr-subscribers-clang-analysis @llvm/pr-subscribers-clang Author: Congcong Cai (HerrCai0907) ChangesPatch is 21.85 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/118593.diff 3 Files Affected:
diff --git a/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h b/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
index 7442f4aad531b7..3344959072c221 100644
--- a/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
+++ b/clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
@@ -71,6 +71,10 @@ class ExprMutationAnalyzer {
const Stmt *findReferenceMutation(const Expr *Exp);
const Stmt *findFunctionArgMutation(const Expr *Exp);
+ const Stmt *findPointeeValueMutation(const Expr *Exp);
+ const Stmt *findPointeeMemberMutation(const Expr *Exp);
+ const Stmt *findPointeeToNonConst(const Expr *Exp);
+
const Stmt &Stm;
ASTContext &Context;
Memoized &Memorized;
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index be0e8aa5743dd9..490bb8cfb71b46 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -8,8 +8,10 @@
#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OperationKinds.h"
+#include "clang/AST/Stmt.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "llvm/ADT/STLExtras.h"
namespace clang {
@@ -22,7 +24,6 @@ using namespace ast_matchers;
// - ConditionalOperator
// - BinaryConditionalOperator
static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
-
const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
if (Matcher(E))
return true;
@@ -92,6 +93,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
namespace {
+AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
+
AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
return llvm::is_contained(Node.capture_inits(), E);
}
@@ -112,6 +115,57 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
return canExprResolveTo(Exp, Target);
}
+// use class member to store data can reduce stack usage to avoid stack overflow
+// when recursive call.
+class ExprPointeeResolve {
+ const Expr *T;
+
+ bool resolveExpr(const Expr *E) {
+ if (E == nullptr)
+ return false;
+ if (E == T)
+ return true;
+
+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+ if (BO->isAdditiveOp())
+ return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS()));
+ if (BO->isCommaOp())
+ return resolveExpr(BO->getRHS());
+ return false;
+ }
+
+ if (const auto *PE = dyn_cast<ParenExpr>(E))
+ return resolveExpr(PE->getSubExpr());
+
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ const CastKind kind = ICE->getCastKind();
+ if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
+ kind == CK_UncheckedDerivedToBase)
+ return resolveExpr(ICE->getSubExpr());
+ return false;
+ }
+
+ if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
+ return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr());
+
+ return false;
+ }
+
+public:
+ ExprPointeeResolve(const Expr *T) : T(T) {}
+ bool resolve(const Expr *S) { return resolveExpr(S); }
+};
+
+AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) {
+ auto *Exp = dyn_cast<Expr>(&Node);
+ if (!Exp)
+ return true;
+ auto *Target = dyn_cast<Expr>(T);
+ if (!Target)
+ return false;
+ return ExprPointeeResolve{Target}.resolve(Exp);
+}
+
// Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
// not have the 'arguments()' method.
AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -219,7 +273,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
const Stmt *
ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
- return findMutationMemoized(Exp, {/*TODO*/}, Memorized.PointeeResults);
+ return findMutationMemoized(
+ Exp,
+ {
+ &ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
+ &ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
+ &ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
+ },
+ Memorized.PointeeResults);
}
const Stmt *
@@ -388,7 +449,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
// references.
const auto NonConstRefParam = forEachArgumentWithParamType(
anyOf(canResolveToExpr(Exp),
- memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
+ memberExpr(
+ hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))),
nonConstReferenceType());
const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
@@ -654,6 +716,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
return nullptr;
}
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
+ const auto Matches = match(
+ stmt(forEachDescendant(
+ expr(anyOf(
+ // deref by *
+ unaryOperator(hasOperatorName("*"),
+ hasUnaryOperand(canResolveToExprPointee(Exp))),
+ // deref by []
+ arraySubscriptExpr(hasBase(canResolveToExprPointee(Exp)))))
+ .bind(NodeID<Expr>::value))),
+ Stm, Context);
+ return findExprMutation(Matches);
+}
+
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {
+ const Stmt *MemberCallExpr = selectFirst<Stmt>(
+ "stmt", match(stmt(forEachDescendant(
+ cxxMemberCallExpr(on(canResolveToExprPointee(Exp)),
+ unless(isConstCallee()))
+ .bind("stmt"))),
+ Stm, Context));
+ if (MemberCallExpr)
+ return MemberCallExpr;
+ const auto Matches =
+ match(stmt(forEachDescendant(
+ memberExpr(hasObjectExpression(canResolveToExprPointee(Exp)))
+ .bind(NodeID<Expr>::value))),
+ Stm, Context);
+ return findExprMutation(Matches);
+}
+
+const Stmt *
+ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
+ const auto NonConstPointerOrDependentType =
+ type(anyOf(nonConstPointerType(), isDependentType()));
+
+ // assign
+ const auto InitToNonConst =
+ varDecl(hasType(NonConstPointerOrDependentType),
+ hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt")));
+ const auto AssignToNonConst =
+ binaryOperation(hasOperatorName("="),
+ hasLHS(expr(hasType(NonConstPointerOrDependentType))),
+ hasRHS(canResolveToExprPointee(Exp)));
+ // arguments like
+ const auto ArgOfInstantiationDependent = allOf(
+ hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent());
+ const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
+ canResolveToExprPointee(Exp), NonConstPointerOrDependentType);
+ const auto CallLikeMatcher =
+ anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
+ const auto PassAsNonConstArg =
+ expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent),
+ cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher),
+ parenListExpr(has(canResolveToExprPointee(Exp))),
+ initListExpr(hasAnyInit(canResolveToExprPointee(Exp)))));
+ // cast
+ const auto CastToNonConst =
+ explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)),
+ hasDestinationType(NonConstPointerOrDependentType));
+
+ // capture
+ // FIXME: false positive if the pointee does not change in lambda
+ const auto CaptureNoConst = lambdaExpr(hasCaptureInit(Exp));
+
+ const auto Matches =
+ match(stmt(anyOf(forEachDescendant(
+ stmt(anyOf(AssignToNonConst, PassAsNonConstArg,
+ CastToNonConst, CaptureNoConst))
+ .bind("stmt")),
+ forEachDescendant(decl(InitToNonConst)))),
+ Stm, Context);
+ return selectFirst<Stmt>("stmt", Matches);
+}
+
FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
const FunctionDecl &Func, ASTContext &Context,
ExprMutationAnalyzer::Memoized &Memorized)
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index 137baab53301ae..c632cae266c204 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -14,6 +14,7 @@
#include "clang/Tooling/Tooling.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <cassert>
#include <cctype>
namespace clang {
@@ -1609,4 +1610,348 @@ TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) {
match(withEnclosingCompound(declRefTo("x")), AST11->getASTContext());
EXPECT_FALSE(isMutated(Results11, AST11.get()));
}
+
+static bool isPointeeMutated(const SmallVectorImpl<BoundNodes> &Results,
+ ASTUnit *AST) {
+ const auto *const S = selectFirst<Stmt>("stmt", Results);
+ const auto *const E = selectFirst<Expr>("expr", Results);
+ assert(S && E);
+ TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
+ return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E);
+}
+
+static bool isDeclPointeeMutated(const SmallVectorImpl<BoundNodes> &Results,
+ ASTUnit *AST) {
+ const auto *const S = selectFirst<Stmt>("stmt", Results);
+ const auto *const D = selectFirst<Decl>("decl", Results);
+ assert(S && D);
+ TraversalKindScope RAII(AST->getASTContext(), TK_AsIs);
+ return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(D);
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssign) {
+ {
+ const std::string Code = "void f() { int* x = nullptr; int b = *x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "void f() { int* x = nullptr; *x = 100; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "void f() { int* x = nullptr; (*x)++; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByMember) {
+ {
+ const std::string Code =
+ "struct A { int v; }; void f() { A* x = nullptr; int b = x->v; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code =
+ "struct A { int v; }; void f() { A* x = nullptr; x->v = 1; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code =
+ "struct A { int v; }; void f() { A* x = nullptr; x->v++; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByInitToNonConst) {
+ {
+ const std::string Code = "void f() { int* x = nullptr; int const* b = x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "void f() { int* x = nullptr; int* b = x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "void f() { int* x = nullptr; int* const b = x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToNonConst) {
+ {
+ const std::string Code =
+ "void f() { int* x = nullptr; int const* b; b = x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "void f() { int* x = nullptr; int* b; b = x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgument) {
+ {
+ const std::string Code =
+ "void b(int const*); void f() { int* x = nullptr; b(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code =
+ "void b(int *); void f() { int* x = nullptr; b(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInConstruct) {
+ {
+ const std::string Code = "struct A { A(int const*); };"
+ "void f() { int *x; A a{x}; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "struct A { A(int const*); };"
+ "void f() { int *x; A a(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "struct A { A(int const*); };"
+ "void f() { int *x; A a = x; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "struct A { A(int *); };"
+ "void f() { int *x; A a{x}; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest,
+ PointeeMutatedByPassAsArgumentInTemplateConstruct) {
+ const std::string Code = "template<class T> void f() { int *x; new T(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInInitList) {
+ {
+ const std::string Code =
+ "namespace std {"
+ "template<class T>"
+ "struct initializer_list{ T const* begin; T const* end; };"
+ "}"
+ "void f() { int *x; std::initializer_list<int*> a{x, x, x}; }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByThis) {
+ {
+ const std::string Code =
+ "struct A { void m() const; }; void f() { A* x = nullptr; x->m(); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code =
+ "struct A { void m(); }; void f() { A* x = nullptr; x->m(); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByExplicitCastToNonConst) {
+ {
+ const std::string Code =
+ "void f() { int* x = nullptr; static_cast<int const*>(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code =
+ "void f() { int* x = nullptr; static_cast<int*>(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByConstCastToNonConst) {
+ // const_cast to non-const even treat as mutated.
+ {
+ const std::string Code =
+ "void f() { int* x = nullptr; const_cast<int const*>(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code =
+ "void f() { int* x = nullptr; const_cast<int*>(x); }";
+ auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByUnresolvedCall) {
+ const std::string Code =
+ "template <class T> struct S;"
+ "template <class T> void f() { S<T> s; int* x = nullptr; s.m(x); }";
+ auto AST = buildASTFromCodeWithArgs(
+ Code, {"-fno-delayed-template-parsing", "-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToUnknownType) {
+ {
+ const std::string Code = "template <class T> void f() {"
+ " int* x = nullptr;"
+ " T t = x;"
+ "}";
+ auto AST = buildASTFromCodeWithArgs(
+ Code, {"-fno-delayed-template-parsing", "-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "template <class T> void f() {"
+ " int* x = nullptr;"
+ " typename T::t t = x;"
+ "}";
+ auto AST = buildASTFromCodeWithArgs(
+ Code, {"-fno-delayed-template-parsing", "-Wno-everything"});
+ auto Results =
+ match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByLambdaCapture) {
+ const std::string Code = R"(
+ void f() {
+ i...
[truncated]
|
58b6e46 to
bea4abd
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
No description provided.