[clang] [analyzer] Accept C library functions from the `std` namespace (PR #84469)

Balazs Benics via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 8 08:34:11 PST 2024


=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>,
=?utf-8?q?Donát?= Nagy <donat.nagy at ericsson.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/84469 at github.com>


steakhal wrote:

> I feel that I'm getting bogged down in this area with changes that are more general than what's strictly necessary and only tangentially related to my real goals...
> 
> I'm not familiar with the unittest framework and if unittests were necessary for this change, then I'd sooner abandon it, because it's not worth the effort.
> 
> (Disclaimer: this comment reflects my current annoyance, and is not necessarily aligned with my long-term opinion.)

No worries!
Note the `IsCLibraryFunctionTest.AcceptsStdNamespace` test case which would fail on llvm/main, but would pass with the PR.

Here is what I had in mind:
(I could not push to your branch, neither upload the patch file, so here is the verbatim content instead:
```
commit 1cfbeec724d758b136d36966696df29cf850ca5b
Author: Balazs Benics <benicsbalazs at gmail.com>
Date:   Fri Mar 8 17:26:49 2024 +0100

    Add unittests
    
    FYI IsCLibraryFunctionTest.AcceptsStdNamespace would have been FALSE
    prior this PR.

diff --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt
index 775f0f8486b8..db56e77331b8 100644
--- a/clang/unittests/StaticAnalyzer/CMakeLists.txt
+++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt
@@ -11,6 +11,7 @@ add_clang_unittest(StaticAnalysisTests
   CallEventTest.cpp
   ConflictingEvalCallsTest.cpp
   FalsePositiveRefutationBRVisitorTest.cpp
+  IsCLibraryFunctionTest.cpp
   NoStateChangeFuncVisitorTest.cpp
   ParamRegionTest.cpp
   RangeSetTest.cpp
diff --git a/clang/unittests/StaticAnalyzer/IsCLibraryFunctionTest.cpp b/clang/unittests/StaticAnalyzer/IsCLibraryFunctionTest.cpp
new file mode 100644
index 000000000000..e1dea3458bc1
--- /dev/null
+++ b/clang/unittests/StaticAnalyzer/IsCLibraryFunctionTest.cpp
@@ -0,0 +1,89 @@
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+#include <memory>
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+testing::AssertionResult extractFunctionDecl(StringRef Code,
+                                             const FunctionDecl *&Result) {
+  auto ASTUnit = tooling::buildASTFromCode(Code);
+  if (!ASTUnit)
+    return testing::AssertionFailure() << "AST construction failed";
+
+  ASTContext &Context = ASTUnit->getASTContext();
+  if (Context.getDiagnostics().hasErrorOccurred())
+    return testing::AssertionFailure() << "Compilation error";
+
+  auto Matches = ast_matchers::match(functionDecl().bind("fn"), Context);
+  if (Matches.empty())
+    return testing::AssertionFailure() << "No function declaration found";
+
+  if (Matches.size() > 1)
+    return testing::AssertionFailure()
+           << "Multiple function declarations found";
+
+  Result = Matches[0].getNodeAs<FunctionDecl>("fn");
+  return testing::AssertionSuccess();
+}
+
+TEST(IsCLibraryFunctionTest, AcceptsGlobal) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(extractFunctionDecl(R"cpp(void fun();)cpp", Result));
+  EXPECT_TRUE(CheckerContext::isCLibraryFunction(Result));
+}
+
+TEST(IsCLibraryFunctionTest, AcceptsExternCGlobal) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(
+      extractFunctionDecl(R"cpp(extern "C" { void fun(); })cpp", Result));
+  EXPECT_TRUE(CheckerContext::isCLibraryFunction(Result));
+}
+
+TEST(IsCLibraryFunctionTest, AcceptsStaticGlobal) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(extractFunctionDecl(R"cpp(static void fun();)cpp", Result));
+  EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
+  // FIXME: Shouldn't this be TRUE?
+}
+
+TEST(IsCLibraryFunctionTest, RejectsAnonymousNamespace) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(
+      extractFunctionDecl(R"cpp(namespace { void fun(); })cpp", Result));
+  EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
+}
+
+TEST(IsCLibraryFunctionTest, AcceptsStdNamespace) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(
+      extractFunctionDecl(R"cpp(namespace std { void fun(); })cpp", Result));
+  EXPECT_TRUE(CheckerContext::isCLibraryFunction(Result));
+}
+
+TEST(IsCLibraryFunctionTest, RejectsOtherNamespaces) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(
+      extractFunctionDecl(R"cpp(namespace stdx { void fun(); })cpp", Result));
+  EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
+}
+
+TEST(IsCLibraryFunctionTest, RejectsClassStatic) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(
+      extractFunctionDecl(R"cpp(class A { static void fun(); };)cpp", Result));
+  EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
+}
+
+TEST(IsCLibraryFunctionTest, RejectsClassMember) {
+  const FunctionDecl *Result;
+  ASSERT_TRUE(extractFunctionDecl(R"cpp(class A { void fun(); };)cpp", Result));
+  EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
+}
diff --git a/llvm/utils/gn/secondary/clang/unittests/StaticAnalyzer/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/StaticAnalyzer/BUILD.gn
index 01c2b6ced336..9c240cff1816 100644
--- a/llvm/utils/gn/secondary/clang/unittests/StaticAnalyzer/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/unittests/StaticAnalyzer/BUILD.gn
@@ -19,6 +19,7 @@ unittest("StaticAnalysisTests") {
     "CallEventTest.cpp",
     "ConflictingEvalCallsTest.cpp",
     "FalsePositiveRefutationBRVisitorTest.cpp",
+    "IsCLibraryFunctionTest.cpp",
     "NoStateChangeFuncVisitorTest.cpp",
     "ParamRegionTest.cpp",
     "RangeSetTest.cpp",
``` 


https://github.com/llvm/llvm-project/pull/84469


More information about the cfe-commits mailing list