[clang] [analyzer] Support C++23 static operator calls (PR #84972)
Balazs Benics via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 13 01:06:48 PDT 2024
https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/84972
>From 9860dad609c8a27dfaa178af5b72285e3ad050fd Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Tue, 12 Mar 2024 19:55:29 +0100
Subject: [PATCH 1/3] [analyzer] Support C++23 static operator calls
Made by following:
https://github.com/llvm/llvm-project/pull/83585#issuecomment-1980340866
Thanks for the details Tomek!
---
clang/docs/ReleaseNotes.rst | 2 +
.../Core/PathSensitive/CallEvent.h | 55 +++++++++++++++++++
clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 5 +-
.../Core/ExprEngineCallAndReturn.cpp | 1 +
clang/test/Analysis/cxx23-static-operator.cpp | 16 ++++++
5 files changed, 78 insertions(+), 1 deletion(-)
create mode 100644 clang/test/Analysis/cxx23-static-operator.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e14c92eae0afe1..7629707c122470 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -473,6 +473,8 @@ libclang
Static Analyzer
---------------
+- Support C++23 static operator calls.
+
New features
^^^^^^^^^^^^
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 0d36587484bf9c..be6d713f9f55fb 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -59,6 +59,7 @@ namespace ento {
enum CallEventKind {
CE_Function,
+ CE_CXXStaticOperator,
CE_CXXMember,
CE_CXXMemberOperator,
CE_CXXDestructor,
@@ -709,6 +710,60 @@ 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 CallExpr *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 implicit object parameter.
+ assert(getOriginExpr()->getNumArgs() > 0);
+ return getOriginExpr()->getNumArgs() - 1;
+ }
+
+ const Expr *getArgExpr(unsigned Index) const override {
+ // Ignore the implicit object parameter.
+ return getOriginExpr()->getArg(Index + 1);
+ }
+
+ 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()
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index bc14aea27f6736..0e317ec765ec09 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -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);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 4755b6bfa6dc0a..9d3e4fc944fb7b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -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:
diff --git a/clang/test/Analysis/cxx23-static-operator.cpp b/clang/test/Analysis/cxx23-static-operator.cpp
new file mode 100644
index 00000000000000..3c541c336f417a
--- /dev/null
+++ b/clang/test/Analysis/cxx23-static-operator.cpp
@@ -0,0 +1,16 @@
+// 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) {
+ return x + y;
+ }
+};
+
+void static_operator_call_inlines() {
+ Adder s{10};
+ clang_analyzer_dump(s(1, 2)); // expected-warning {{3 S32b}}
+}
>From b72506d90f0bf06cc50be372b8fc8b88833882ad Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Wed, 13 Mar 2024 08:46:30 +0100
Subject: [PATCH 2/3] Adjust according to review comments
---
.../Core/PathSensitive/CallEvent.h | 21 ++++++++++++++++--
clang/test/Analysis/cxx23-static-operator.cpp | 22 ++++++++++++++++++-
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index be6d713f9f55fb..b88935d2cc2ea4 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -746,16 +746,33 @@ class CXXStaticOperatorCall : public SimpleFunctionCall {
}
unsigned getNumArgs() const override {
- // Ignore the implicit object parameter.
+ // 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 implicit object parameter.
+ // 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"; }
diff --git a/clang/test/Analysis/cxx23-static-operator.cpp b/clang/test/Analysis/cxx23-static-operator.cpp
index 3c541c336f417a..ba28bfb5672046 100644
--- a/clang/test/Analysis/cxx23-static-operator.cpp
+++ b/clang/test/Analysis/cxx23-static-operator.cpp
@@ -6,11 +6,31 @@ 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 S32b}}
+ clang_analyzer_dump(s(1, 2)); // expected-warning {{3}}
+}
+
+struct DataWithCtor {
+ int x;
+ int y;
+ DataWithCtor(int v) : x(v + 10), y(v + 20) {}
+};
+
+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}];
}
>From 7fbf5c6c8c7e6c14cb710d1e9dc427c9f12b85a0 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Wed, 13 Mar 2024 09:06:31 +0100
Subject: [PATCH 3/3] Tighten ctor type
---
.../include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index b88935d2cc2ea4..549c864dc91ef2 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -730,7 +730,7 @@ class CXXStaticOperatorCall : public SimpleFunctionCall {
friend class CallEventManager;
protected:
- CXXStaticOperatorCall(const CallExpr *CE, ProgramStateRef St,
+ CXXStaticOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St,
const LocationContext *LCtx,
CFGBlock::ConstCFGElementRef ElemRef)
: SimpleFunctionCall(CE, St, LCtx, ElemRef) {}
More information about the cfe-commits
mailing list