Skip to content

Commit e925968

Browse files
authored
[analyzer] Support C++23 static operator calls (#84972)
Made by following: #83585 (comment) Thanks for the details Tomek! CPP-5080
1 parent db33444 commit e925968

File tree

5 files changed

+116
-1
lines changed

5 files changed

+116
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ Static Analyzer
576576
- Fixed crashing on loops if the loop variable was declared in switch blocks
577577
but not under any case blocks if ``unroll-loops=true`` analyzer config is
578578
set. (#GH68819)
579+
- Support C++23 static operator calls. (#GH84972)
579580

580581
New features
581582
^^^^^^^^^^^^

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ namespace ento {
5959

6060
enum CallEventKind {
6161
CE_Function,
62+
CE_CXXStaticOperator,
6263
CE_CXXMember,
6364
CE_CXXMemberOperator,
6465
CE_CXXDestructor,
@@ -709,6 +710,77 @@ class CXXInstanceCall : public AnyFunctionCall {
709710
}
710711
};
711712

713+
/// Represents a static C++ operator call.
714+
///
715+
/// "A" in this example.
716+
/// However, "B" and "C" are represented by SimpleFunctionCall.
717+
/// \code
718+
/// struct S {
719+
/// int pad;
720+
/// static void operator()(int x, int y);
721+
/// };
722+
/// S s{10};
723+
/// void (*fptr)(int, int) = &S::operator();
724+
///
725+
/// s(1, 2); // A
726+
/// S::operator()(1, 2); // B
727+
/// fptr(1, 2); // C
728+
/// \endcode
729+
class CXXStaticOperatorCall : public SimpleFunctionCall {
730+
friend class CallEventManager;
731+
732+
protected:
733+
CXXStaticOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St,
734+
const LocationContext *LCtx,
735+
CFGBlock::ConstCFGElementRef ElemRef)
736+
: SimpleFunctionCall(CE, St, LCtx, ElemRef) {}
737+
CXXStaticOperatorCall(const CXXStaticOperatorCall &Other) = default;
738+
739+
void cloneTo(void *Dest) const override {
740+
new (Dest) CXXStaticOperatorCall(*this);
741+
}
742+
743+
public:
744+
const CXXOperatorCallExpr *getOriginExpr() const override {
745+
return cast<CXXOperatorCallExpr>(SimpleFunctionCall::getOriginExpr());
746+
}
747+
748+
unsigned getNumArgs() const override {
749+
// Ignore the object parameter that is not used for static member functions.
750+
assert(getOriginExpr()->getNumArgs() > 0);
751+
return getOriginExpr()->getNumArgs() - 1;
752+
}
753+
754+
const Expr *getArgExpr(unsigned Index) const override {
755+
// Ignore the object parameter that is not used for static member functions.
756+
return getOriginExpr()->getArg(Index + 1);
757+
}
758+
759+
std::optional<unsigned>
760+
getAdjustedParameterIndex(unsigned ASTArgumentIndex) const override {
761+
// Ignore the object parameter that is not used for static member functions.
762+
if (ASTArgumentIndex == 0)
763+
return std::nullopt;
764+
return ASTArgumentIndex - 1;
765+
}
766+
767+
unsigned getASTArgumentIndex(unsigned CallArgumentIndex) const override {
768+
// Account for the object parameter for the static member function.
769+
return CallArgumentIndex + 1;
770+
}
771+
772+
OverloadedOperatorKind getOverloadedOperator() const {
773+
return getOriginExpr()->getOperator();
774+
}
775+
776+
Kind getKind() const override { return CE_CXXStaticOperator; }
777+
StringRef getKindAsString() const override { return "CXXStaticOperatorCall"; }
778+
779+
static bool classof(const CallEvent *CA) {
780+
return CA->getKind() == CE_CXXStaticOperator;
781+
}
782+
};
783+
712784
/// Represents a non-static C++ member function call.
713785
///
714786
/// Example: \c obj.fun()

clang/lib/StaticAnalyzer/Core/CallEvent.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1408,9 +1408,12 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
14081408

14091409
if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
14101410
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
1411-
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
1411+
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) {
14121412
if (MD->isImplicitObjectMemberFunction())
14131413
return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);
1414+
if (MD->isStatic())
1415+
return create<CXXStaticOperatorCall>(OpCE, State, LCtx, ElemRef);
1416+
}
14141417

14151418
} else if (CE->getCallee()->getType()->isBlockPointerType()) {
14161419
return create<BlockCall>(CE, State, LCtx, ElemRef);

clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
846846
const StackFrameContext *CallerSFC = CurLC->getStackFrame();
847847
switch (Call.getKind()) {
848848
case CE_Function:
849+
case CE_CXXStaticOperator:
849850
case CE_Block:
850851
break;
851852
case CE_CXXMember:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \
2+
// RUN: -analyzer-checker=core,debug.ExprInspection
3+
4+
template <typename T> void clang_analyzer_dump(T);
5+
6+
struct Adder {
7+
int data;
8+
static int operator()(int x, int y) {
9+
clang_analyzer_dump(x); // expected-warning {{1}}
10+
clang_analyzer_dump(y); // expected-warning {{2}}
11+
return x + y;
12+
}
13+
};
14+
15+
void static_operator_call_inlines() {
16+
Adder s{10};
17+
clang_analyzer_dump(s(1, 2)); // expected-warning {{3}}
18+
}
19+
20+
struct DataWithCtor {
21+
int x;
22+
int y;
23+
DataWithCtor(int parm) : x(parm + 10), y(parm + 20) {
24+
clang_analyzer_dump(this); // expected-warning {{&v}}
25+
}
26+
};
27+
28+
struct StaticSubscript {
29+
static void operator[](DataWithCtor v) {
30+
clang_analyzer_dump(v.x); // expected-warning {{20}}
31+
clang_analyzer_dump(v.y); // expected-warning {{30}}
32+
}
33+
};
34+
35+
void top() {
36+
StaticSubscript s;
37+
s[DataWithCtor{10}];
38+
}

0 commit comments

Comments
 (0)