Skip to content

Commit 0165704

Browse files
committed
[analysis] support mutation analysis for pointee wip
1 parent 4ad4d34 commit 0165704

File tree

3 files changed

+476
-3
lines changed

3 files changed

+476
-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: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
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"
16+
#include "llvm/Support/Casting.h"
1417

1518
namespace clang {
1619
using namespace ast_matchers;
@@ -22,7 +25,6 @@ using namespace ast_matchers;
2225
// - ConditionalOperator
2326
// - BinaryConditionalOperator
2427
static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
25-
2628
const auto IgnoreDerivedToBase = [](const Expr *E, auto Matcher) {
2729
if (Matcher(E))
2830
return true;
@@ -92,6 +94,8 @@ static bool canExprResolveTo(const Expr *Source, const Expr *Target) {
9294

9395
namespace {
9496

97+
AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); }
98+
9599
AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
96100
return llvm::is_contained(Node.capture_inits(), E);
97101
}
@@ -112,6 +116,53 @@ AST_MATCHER_P(Stmt, canResolveToExpr, const Stmt *, Inner) {
112116
return canExprResolveTo(Exp, Target);
113117
}
114118

119+
class ExprPointeeResolve {
120+
const Expr *T;
121+
122+
bool resolveExpr(const Expr *E) {
123+
if (E == T)
124+
return true;
125+
126+
if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
127+
if (BO->isAdditiveOp())
128+
return (resolveExpr(BO->getLHS()) || resolveExpr(BO->getRHS()));
129+
if (BO->isCommaOp())
130+
return resolveExpr(BO->getRHS());
131+
return false;
132+
}
133+
134+
if (const auto *PE = dyn_cast<ParenExpr>(E))
135+
return resolveExpr(PE->getSubExpr());
136+
137+
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
138+
const CastKind kind = ICE->getCastKind();
139+
if (kind == CK_LValueToRValue || kind == CK_DerivedToBase ||
140+
kind == CK_UncheckedDerivedToBase)
141+
return resolveExpr(ICE->getSubExpr());
142+
return false;
143+
}
144+
145+
if (const auto *ACE = dyn_cast<AbstractConditionalOperator>(E))
146+
return resolve(ACE->getTrueExpr()) || resolve(ACE->getFalseExpr());
147+
148+
return false;
149+
}
150+
151+
public:
152+
ExprPointeeResolve(const Expr *T) : T(T) {}
153+
bool resolve(const Expr *S) { return resolveExpr(S); }
154+
};
155+
156+
AST_MATCHER_P(Stmt, canResolveToExprPointee, const Stmt *, T) {
157+
auto *Exp = dyn_cast<Expr>(&Node);
158+
if (!Exp)
159+
return true;
160+
auto *Target = dyn_cast<Expr>(T);
161+
if (!Target)
162+
return false;
163+
return ExprPointeeResolve{Target}.resolve(Exp);
164+
}
165+
115166
// Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does
116167
// not have the 'arguments()' method.
117168
AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>,
@@ -219,7 +270,14 @@ const Stmt *ExprMutationAnalyzer::Analyzer::findMutation(const Decl *Dec) {
219270

220271
const Stmt *
221272
ExprMutationAnalyzer::Analyzer::findPointeeMutation(const Expr *Exp) {
222-
return findMutationMemoized(Exp, {/*TODO*/}, Memorized.PointeeResults);
273+
return findMutationMemoized(
274+
Exp,
275+
{
276+
&ExprMutationAnalyzer::Analyzer::findPointeeValueMutation,
277+
&ExprMutationAnalyzer::Analyzer::findPointeeMemberMutation,
278+
&ExprMutationAnalyzer::Analyzer::findPointeeToNonConst,
279+
},
280+
Memorized.PointeeResults);
223281
}
224282

225283
const Stmt *
@@ -388,7 +446,8 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
388446
// references.
389447
const auto NonConstRefParam = forEachArgumentWithParamType(
390448
anyOf(canResolveToExpr(Exp),
391-
memberExpr(hasObjectExpression(canResolveToExpr(Exp)))),
449+
memberExpr(
450+
hasObjectExpression(ignoringImpCasts(canResolveToExpr(Exp))))),
392451
nonConstReferenceType());
393452
const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
394453

@@ -654,6 +713,83 @@ ExprMutationAnalyzer::Analyzer::findFunctionArgMutation(const Expr *Exp) {
654713
return nullptr;
655714
}
656715

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

0 commit comments

Comments
 (0)