Skip to content

Commit 53ea5ff

Browse files
authored
[mutation analyzer] support mutation analysis for pointee (#118593)
This patch wants to add mutation analyzer for pointee object.
1 parent 4c6242e commit 53ea5ff

File tree

3 files changed

+543
-3
lines changed

3 files changed

+543
-3
lines changed

clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ class ExprMutationAnalyzer {
7171
const Stmt *findReferenceMutation(const Expr *Exp);
7272
const Stmt *findFunctionArgMutation(const Expr *Exp);
7373

74+
const Stmt *findPointeeValueMutation(const Expr *Exp);
75+
const Stmt *findPointeeMemberMutation(const Expr *Exp);
76+
const Stmt *findPointeeToNonConst(const Expr *Exp);
77+
7478
const Stmt &Stm;
7579
ASTContext &Context;
7680
Memoized &Memorized;

clang/lib/Analysis/ExprMutationAnalyzer.cpp

Lines changed: 144 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
99
#include "clang/AST/Expr.h"
1010
#include "clang/AST/OperationKinds.h"
11+
#include "clang/AST/Stmt.h"
1112
#include "clang/ASTMatchers/ASTMatchFinder.h"
1213
#include "clang/ASTMatchers/ASTMatchers.h"
14+
#include "clang/ASTMatchers/ASTMatchersMacros.h"
1315
#include "llvm/ADT/STLExtras.h"
1416

1517
namespace clang {
@@ -22,7 +24,6 @@ using namespace ast_matchers;
2224
// - ConditionalOperator
2325
// - BinaryConditionalOperator
2426
static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
25-
2627
const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
2728
if (Matcher(E))
2829
return true;
@@ -79,6 +80,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
7980

8081
namespace {
8182

83+
AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
84+
8285
AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
8386
return llvm::is_contained(Node.capture_inits(), E);
8487
}
@@ -99,6 +102,59 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
99102
return canExprResolveTo(Exp, Target);
100103
}
101104

105+
// use class member to store data can reduce stack usage to avoid stack overflow
106+
// when recursive call.
107+
class ExprPointeeResolve {
108+
const Expr *T;
109+
110+
bool resolveExpr(const Expr *E) {
111+
if (E == nullptr)
112+
return false;
113+
if (E == T)
114+
return true;
115+
116+
if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
117+
if (BO->isAdditiveOp())
118+
return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS()));
119+
if (BO->isCommaOp())
120+
return resolveExpr(BO->getRHS());
121+
return false;
122+
}
123+
124+
if (const auto *PE = dyn_cast<ParenExpr>(E))
125+
return resolveExpr(PE->getSubExpr());
126+
127+
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
128+
// only implicit cast needs to be treated as resolvable.
129+
// explicit cast will be checked in `findPointeeToNonConst`
130+
const CastKind kind = ICE->getCastKind();
131+
if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
132+
kind == CK_UncheckedDerivedToBase)
133+
return resolveExpr(ICE->getSubExpr());
134+
return false;
135+
}
136+
137+
if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
138+
return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr());
139+
140+
return false;
141+
}
142+
143+
public:
144+
ExprPointeeResolve(const Expr *T) : T(T) {}
145+
bool resolve(const Expr *S) { return resolveExpr(S); }
146+
};
147+
148+
AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) {
149+
auto *Exp = dyn_cast<Expr>(&Node);
150+
if (!Exp)
151+
return true;
152+
auto *Target = dyn_cast<Expr>(T);
153+
if (!Target)
154+
return false;
155+
return ExprPointeeResolve{Target}.resolve(Exp);
156+
}
157+
102158
// Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
103159
// not have the 'arguments()' method.
104160
AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -208,7 +264,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
208264

209265
const Stmt *
210266
ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
211-
return findMutationMemoized(Exp, {/*TODO*/}, Memorized.PointeeResults);
267+
return findMutationMemoized(
268+
Exp,
269+
{
270+
&ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
271+
&ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
272+
&ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
273+
},
274+
Memorized.PointeeResults);
212275
}
213276

214277
const Stmt *
@@ -377,7 +440,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
377440
// references.
378441
const auto NonConstRefParam = forEachArgumentWithParamType(
379442
anyOf(canResolveToExpr(Exp),
380-
memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
443+
memberExpr(
444+
hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))),
381445
nonConstReferenceType());
382446
const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
383447

@@ -643,6 +707,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
643707
return nullptr;
644708
}
645709

710+
const Stmt *
711+
ExprMutationAnalyzer::Analyzer::findPointeeValueMutation(const Expr *Exp) {
712+
const auto Matches = match(
713+
stmt(forEachDescendant(
714+
expr(anyOf(
715+
// deref by *
716+
unaryOperator(hasOperatorName("*"),
717+
hasUnaryOperand(canResolveToExprPointee(Exp))),
718+
// deref by []
719+
arraySubscriptExpr(hasBase(canResolveToExprPointee(Exp)))))
720+
.bind(NodeID<Expr>::value))),
721+
Stm, Context);
722+
return findExprMutation(Matches);
723+
}
724+
725+
const Stmt *
726+
ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation(const Expr *Exp) {
727+
const Stmt *MemberCallExpr = selectFirst<Stmt>(
728+
"stmt", match(stmt(forEachDescendant(
729+
cxxMemberCallExpr(on(canResolveToExprPointee(Exp)),
730+
unless(isConstCallee()))
731+
.bind("stmt"))),
732+
Stm, Context));
733+
if (MemberCallExpr)
734+
return MemberCallExpr;
735+
const auto Matches =
736+
match(stmt(forEachDescendant(
737+
memberExpr(hasObjectExpression(canResolveToExprPointee(Exp)))
738+
.bind(NodeID<Expr>::value))),
739+
Stm, Context);
740+
return findExprMutation(Matches);
741+
}
742+
743+
const Stmt *
744+
ExprMutationAnalyzer::Analyzer::findPointeeToNonConst(const Expr *Exp) {
745+
const auto NonConstPointerOrDependentType =
746+
type(anyOf(nonConstPointerType(), isDependentType()));
747+
748+
// assign
749+
const auto InitToNonConst =
750+
varDecl(hasType(NonConstPointerOrDependentType),
751+
hasInitializer(expr(canResolveToExprPointee(Exp)).bind("stmt")));
752+
const auto AssignToNonConst =
753+
binaryOperation(hasOperatorName("="),
754+
hasLHS(expr(hasType(NonConstPointerOrDependentType))),
755+
hasRHS(canResolveToExprPointee(Exp)));
756+
// arguments like
757+
const auto ArgOfInstantiationDependent = allOf(
758+
hasAnyArgument(canResolveToExprPointee(Exp)), isInstantiationDependent());
759+
const auto ArgOfNonConstParameter = forEachArgumentWithParamType(
760+
canResolveToExprPointee(Exp), NonConstPointerOrDependentType);
761+
const auto CallLikeMatcher =
762+
anyOf(ArgOfNonConstParameter, ArgOfInstantiationDependent);
763+
const auto PassAsNonConstArg =
764+
expr(anyOf(cxxUnresolvedConstructExpr(ArgOfInstantiationDependent),
765+
cxxConstructExpr(CallLikeMatcher), callExpr(CallLikeMatcher),
766+
parenListExpr(has(canResolveToExprPointee(Exp))),
767+
initListExpr(hasAnyInit(canResolveToExprPointee(Exp)))));
768+
// cast
769+
const auto CastToNonConst =
770+
explicitCastExpr(hasSourceExpression(canResolveToExprPointee(Exp)),
771+
hasDestinationType(NonConstPointerOrDependentType));
772+
773+
// capture
774+
// FIXME: false positive if the pointee does not change in lambda
775+
const auto CaptureNoConst = lambdaExpr(hasCaptureInit(Exp));
776+
777+
const auto Matches =
778+
match(stmt(anyOf(forEachDescendant(
779+
stmt(anyOf(AssignToNonConst, PassAsNonConstArg,
780+
CastToNonConst, CaptureNoConst))
781+
.bind("stmt")),
782+
forEachDescendant(InitToNonConst))),
783+
Stm, Context);
784+
return selectFirst<Stmt>("stmt", Matches);
785+
}
786+
646787
FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
647788
const FunctionDecl &Func, ASTContext &Context,
648789
ExprMutationAnalyzer::Memoized &Memorized)

0 commit comments

Comments
 (0)