[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/steakhal closed https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/84972 >From f66c62bcf3fd1427ad3f5061ec23110c1c3a5b6e Mon Sep 17 00:00:00 2001 From: Balazs Benics Date: Tue, 12 Mar 2024 19:55:29 +0100 Subject: [PATCH] [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 | 1 + .../Core/PathSensitive/CallEvent.h| 72 +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 5 +- .../Core/ExprEngineCallAndReturn.cpp | 1 + clang/test/Analysis/cxx23-static-operator.cpp | 38 ++ 5 files changed, 116 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 fd12bb41be47a3..45b2e01af997c5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 0d36587484bf9c..549c864dc91ef2 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,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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(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 + 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() 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(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); -if (const auto *MD = dyn_cast(DirectCallee)) +if (const auto *MD = dyn_cast(DirectCallee)) { if (MD->isImplicitObjectMemberFunction()) return create(OpCE, State, LCtx, ElemRef); + if (MD->isStatic()) +return create(OpCE,
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/tomasz-kaminski-sonarsource edited https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
@@ -0,0 +1,36 @@ +// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection + +template 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 v) : x(v + 10), y(v + 20) {} tomasz-kaminski-sonarsource wrote: Could we also dump `this` this to confirm that it is param var regions for `operator[]` and we trigger copy elsion? https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/steakhal edited https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
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 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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(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(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); -if (const auto *MD = dyn_cast(DirectCallee)) +if (const auto *MD = dyn_cast(DirectCallee)) { if (MD->isImplicitObjectMemberFunction()) return create(OpCE, State, LCtx, ElemRef); + if (MD->isStatic()) +return create(OpCE, State, LCtx, ElemRef); +} } else if (CE->getCallee()->getType()->isBlockPointerType()) { return create(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 , 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
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/tomasz-kaminski-sonarsource approved this pull request. Only small change. https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
@@ -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) = ::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, tomasz-kaminski-sonarsource wrote: To guaranteee that cast in `getOriginExpr` is correct. ```suggestion CXXStaticOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St, ``` https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
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 Date: Tue, 12 Mar 2024 19:55:29 +0100 Subject: [PATCH 1/2] [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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(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(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); -if (const auto *MD = dyn_cast(DirectCallee)) +if (const auto *MD = dyn_cast(DirectCallee)) { if (MD->isImplicitObjectMemberFunction()) return create(OpCE, State, LCtx, ElemRef); + if (MD->isStatic()) +return create(OpCE, State, LCtx, ElemRef); +} } else if (CE->getCallee()->getType()->isBlockPointerType()) { return create(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 , 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
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
@@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection + +template void clang_analyzer_dump(T); tomasz-kaminski-sonarsource wrote: To validate the argument indexing is correct (the function I have suggested). Could you add example when we construct prvalue of class type as argument: ```c++ struct Clazz { int x; int y; Clazz(int v) : x(v + 10), y(v + 20) {} }; struct StaticOp { static void operator[](Clazz v) { clang_analyzer_dump(v.x); clang_analyzer_dump(v.y); } }; StaticOp s; s[Calzz{10}]; ``` https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/tomasz-kaminski-sonarsource edited https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
@@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \ +// RUN: -analyzer-checker=core,debug.ExprInspection + +template void clang_analyzer_dump(T); + +struct Adder { + int data; + static int operator()(int x, int y) { +return x + y; tomasz-kaminski-sonarsource wrote: Could you dump values of `x` and `y` from inside the body? https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
@@ -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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(SimpleFunctionCall::getOriginExpr()); + } + + unsigned getNumArgs() const override { +// Ignore the implicit object parameter. tomasz-kaminski-sonarsource wrote: ```suggestion // Ignore the object parameter that is not used for static member functions. ``` https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
@@ -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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(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; + } +}; tomasz-kaminski-sonarsource wrote: Could you please add the following function. I believe we should override first two to match the semantic described in `CallEvent`, because arguments include object argument, that do not have corresponding parameter. Last is just for consistency. ``` std::optional getAdjustedParameterIndex(unsigned ASTArgumentIndex) const override { // For member operator calls argument 0 on the expression corresponds // to implicit this-parameter on the declaration. return (ASTArgumentIndex > 0) ? std::optional(ASTArgumentIndex - 1) : std::nullopt; } unsigned getASTArgumentIndex(unsigned CallArgumentIndex) const override { // For member operator calls argument 0 on the expression corresponds // to implicit this-parameter on the declaration. return CallArgumentIndex + 1; } OverloadedOperatorKind getOverloadedOperator() const { return getOriginExpr()->getOperator(); } ``` https://github.com/llvm/llvm-project/pull/84972 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
llvmbot wrote: @llvm/pr-subscribers-clang-static-analyzer-1 Author: Balazs Benics (steakhal) Changes Made by following: https://github.com/llvm/llvm-project/pull/83585#issuecomment-1980340866 Thanks for the details Tomek! --- Full diff: https://github.com/llvm/llvm-project/pull/84972.diff 5 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+2) - (modified) clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h (+55) - (modified) clang/lib/StaticAnalyzer/Core/CallEvent.cpp (+4-1) - (modified) clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (+1) - (added) clang/test/Analysis/cxx23-static-operator.cpp (+16) ``diff 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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(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(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); -if (const auto *MD = dyn_cast(DirectCallee)) +if (const auto *MD = dyn_cast(DirectCallee)) { if (MD->isImplicitObjectMemberFunction()) return create(OpCE, State, LCtx, ElemRef); + if (MD->isStatic()) +return create(OpCE, State, LCtx, ElemRef); +} } else if (CE->getCallee()->getType()->isBlockPointerType()) { return create(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 , 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 00..3c541c336f417a --- /dev/null +++
[clang] [analyzer] Support C++23 static operator calls (PR #84972)
https://github.com/steakhal created https://github.com/llvm/llvm-project/pull/84972 Made by following: https://github.com/llvm/llvm-project/pull/83585#issuecomment-1980340866 Thanks for the details Tomek! >From 9860dad609c8a27dfaa178af5b72285e3ad050fd Mon Sep 17 00:00:00 2001 From: Balazs Benics Date: Tue, 12 Mar 2024 19:55:29 +0100 Subject: [PATCH] [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) = ::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 ) = default; + + void cloneTo(void *Dest) const override { +new (Dest) CXXStaticOperatorCall(*this); + } + +public: + const CXXOperatorCallExpr *getOriginExpr() const override { +return cast(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(CE)) { const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); -if (const auto *MD = dyn_cast(DirectCallee)) +if (const auto *MD = dyn_cast(DirectCallee)) { if (MD->isImplicitObjectMemberFunction()) return create(OpCE, State, LCtx, ElemRef); + if (MD->isStatic()) +return create(OpCE, State, LCtx, ElemRef); +} } else if (CE->getCallee()->getType()->isBlockPointerType()) { return create(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 , const ExplodedNode *Pred, const StackFrameContext *CallerSFC = CurLC->getStackFrame(); switch (Call.getKind()) { case CE_Function: + case CE_CXXStaticOperator: case