Skip to content

Commit

Permalink
[analyzer] Support C++23 static operator calls (llvm#84972)
Browse files Browse the repository at this point in the history
Made by following:
llvm#83585 (comment)

Thanks for the details Tomek!

CPP-5080
  • Loading branch information
steakhal committed Mar 22, 2024
1 parent db33444 commit e925968
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 1 deletion.
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ Static Analyzer
- Fixed crashing on loops if the loop variable was declared in switch blocks
but not under any case blocks if ``unroll-loops=true`` analyzer config is
set. (#GH68819)
- Support C++23 static operator calls. (#GH84972)

New features
^^^^^^^^^^^^
Expand Down
72 changes: 72 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ namespace ento {

enum CallEventKind {
CE_Function,
CE_CXXStaticOperator,
CE_CXXMember,
CE_CXXMemberOperator,
CE_CXXDestructor,
Expand Down Expand Up @@ -709,6 +710,77 @@ class CXXInstanceCall : public AnyFunctionCall {
}
};

/// Represents a static C++ operator call.
///
/// "A" in this example.
/// However, "B" and "C" are represented by SimpleFunctionCall.
/// \code
/// struct S {
/// int pad;
/// static void operator()(int x, int y);
/// };
/// S s{10};
/// void (*fptr)(int, int) = &S::operator();
///
/// s(1, 2); // A
/// S::operator()(1, 2); // B
/// fptr(1, 2); // C
/// \endcode
class CXXStaticOperatorCall : public SimpleFunctionCall {
friend class CallEventManager;

protected:
CXXStaticOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St,
const LocationContext *LCtx,
CFGBlock::ConstCFGElementRef ElemRef)
: SimpleFunctionCall(CE, St, LCtx, ElemRef) {}
CXXStaticOperatorCall(const CXXStaticOperatorCall &Other) = default;

void cloneTo(void *Dest) const override {
new (Dest) CXXStaticOperatorCall(*this);
}

public:
const CXXOperatorCallExpr *getOriginExpr() const override {
return cast<CXXOperatorCallExpr>(SimpleFunctionCall::getOriginExpr());
}

unsigned getNumArgs() const override {
// Ignore the object parameter that is not used for static member functions.
assert(getOriginExpr()->getNumArgs() > 0);
return getOriginExpr()->getNumArgs() - 1;
}

const Expr *getArgExpr(unsigned Index) const override {
// Ignore the object parameter that is not used for static member functions.
return getOriginExpr()->getArg(Index + 1);
}

std::optional<unsigned>
getAdjustedParameterIndex(unsigned ASTArgumentIndex) const override {
// Ignore the object parameter that is not used for static member functions.
if (ASTArgumentIndex == 0)
return std::nullopt;
return ASTArgumentIndex - 1;
}

unsigned getASTArgumentIndex(unsigned CallArgumentIndex) const override {
// Account for the object parameter for the static member function.
return CallArgumentIndex + 1;
}

OverloadedOperatorKind getOverloadedOperator() const {
return getOriginExpr()->getOperator();
}

Kind getKind() const override { return CE_CXXStaticOperator; }
StringRef getKindAsString() const override { return "CXXStaticOperatorCall"; }

static bool classof(const CallEvent *CA) {
return CA->getKind() == CE_CXXStaticOperator;
}
};

/// Represents a non-static C++ member function call.
///
/// Example: \c obj.fun()
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1408,9 +1408,12 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,

if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) {
if (MD->isImplicitObjectMemberFunction())
return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);
if (MD->isStatic())
return create<CXXStaticOperatorCall>(OpCE, State, LCtx, ElemRef);
}

} else if (CE->getCallee()->getType()->isBlockPointerType()) {
return create<BlockCall>(CE, State, LCtx, ElemRef);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
const StackFrameContext *CallerSFC = CurLC->getStackFrame();
switch (Call.getKind()) {
case CE_Function:
case CE_CXXStaticOperator:
case CE_Block:
break;
case CE_CXXMember:
Expand Down
38 changes: 38 additions & 0 deletions clang/test/Analysis/cxx23-static-operator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \
// RUN: -analyzer-checker=core,debug.ExprInspection

template <typename T> void clang_analyzer_dump(T);

struct Adder {
int data;
static int operator()(int x, int y) {
clang_analyzer_dump(x); // expected-warning {{1}}
clang_analyzer_dump(y); // expected-warning {{2}}
return x + y;
}
};

void static_operator_call_inlines() {
Adder s{10};
clang_analyzer_dump(s(1, 2)); // expected-warning {{3}}
}

struct DataWithCtor {
int x;
int y;
DataWithCtor(int parm) : x(parm + 10), y(parm + 20) {
clang_analyzer_dump(this); // expected-warning {{&v}}
}
};

struct StaticSubscript {
static void operator[](DataWithCtor v) {
clang_analyzer_dump(v.x); // expected-warning {{20}}
clang_analyzer_dump(v.y); // expected-warning {{30}}
}
};

void top() {
StaticSubscript s;
s[DataWithCtor{10}];
}

0 comments on commit e925968

Please sign in to comment.