-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mutation analyzer] support mutation analysis for pointee #118593
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
Conversation
✅ 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
ping |
I'll try to find some time for this one, sorry. I'll check it out in the next days. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good in general, could you add a small description to the pr?
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/50/builds/9209 Here is the relevant piece of the build log for the reference
|
This patch wants to add mutation analyzer for pointee object.