Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
145 changes: 142 additions & 3 deletions clang/lib/Analysis/ExprMutationAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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>,
Expand Down Expand Up @@ -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 *
Expand Down Expand Up @@ -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()));

Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading