Skip to content

Commit 80ab823

Browse files
NagyDonatsteakhal
andauthored
[analyzer] Accept C library functions from the std namespace (#84469)
Previously, the function `isCLibraryFunction()` and logic relying on it only accepted functions that are declared directly within a TU (i.e. not in a namespace or a class). However C++ headers like <cstdlib> declare many C standard library functions within the namespace `std`, so this commit ensures that functions within the namespace `std` are also accepted. After this commit it will be possible to match functions like `malloc` or `free` with `CallDescription::Mode::CLibrary`. --------- Co-authored-by: Balazs Benics <[email protected]>
1 parent b5a16b6 commit 80ab823

File tree

5 files changed

+98
-9
lines changed

5 files changed

+98
-9
lines changed

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,8 @@ class CallDescription {
4141
/// - We also accept calls where the number of arguments or parameters is
4242
/// greater than the specified value.
4343
/// For the exact heuristics, see CheckerContext::isCLibraryFunction().
44-
/// Note that functions whose declaration context is not a TU (e.g.
45-
/// methods, functions in namespaces) are not accepted as C library
46-
/// functions.
47-
/// FIXME: If I understand it correctly, this discards calls where C++ code
48-
/// refers a C library function through the namespace `std::` via headers
49-
/// like <cstdlib>.
44+
/// (This mode only matches functions that are declared either directly
45+
/// within a TU or in the namespace `std`.)
5046
CLibrary,
5147

5248
/// Matches "simple" functions that are not methods. (Static methods are

clang/lib/StaticAnalyzer/Core/CheckerContext.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
8787
if (!II)
8888
return false;
8989

90-
// Look through 'extern "C"' and anything similar invented in the future.
91-
// If this function is not in TU directly, it is not a C library function.
92-
if (!FD->getDeclContext()->getRedeclContext()->isTranslationUnit())
90+
// C library functions are either declared directly within a TU (the common
91+
// case) or they are accessed through the namespace `std` (when they are used
92+
// in C++ via headers like <cstdlib>).
93+
const DeclContext *DC = FD->getDeclContext()->getRedeclContext();
94+
if (!(DC->isTranslationUnit() || DC->isStdNamespace()))
9395
return false;
9496

9597
// If this function is not externally visible, it is not a C library function.

clang/unittests/StaticAnalyzer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_clang_unittest(StaticAnalysisTests
1111
CallEventTest.cpp
1212
ConflictingEvalCallsTest.cpp
1313
FalsePositiveRefutationBRVisitorTest.cpp
14+
IsCLibraryFunctionTest.cpp
1415
NoStateChangeFuncVisitorTest.cpp
1516
ParamRegionTest.cpp
1617
RangeSetTest.cpp
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "clang/ASTMatchers/ASTMatchFinder.h"
2+
#include "clang/ASTMatchers/ASTMatchers.h"
3+
#include "clang/Analysis/AnalysisDeclContext.h"
4+
#include "clang/Frontend/ASTUnit.h"
5+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
6+
#include "clang/Tooling/Tooling.h"
7+
#include "gtest/gtest.h"
8+
9+
#include <memory>
10+
11+
using namespace clang;
12+
using namespace ento;
13+
using namespace ast_matchers;
14+
15+
testing::AssertionResult extractFunctionDecl(StringRef Code,
16+
const FunctionDecl *&Result) {
17+
auto ASTUnit = tooling::buildASTFromCode(Code);
18+
if (!ASTUnit)
19+
return testing::AssertionFailure() << "AST construction failed";
20+
21+
ASTContext &Context = ASTUnit->getASTContext();
22+
if (Context.getDiagnostics().hasErrorOccurred())
23+
return testing::AssertionFailure() << "Compilation error";
24+
25+
auto Matches = ast_matchers::match(functionDecl().bind("fn"), Context);
26+
if (Matches.empty())
27+
return testing::AssertionFailure() << "No function declaration found";
28+
29+
if (Matches.size() > 1)
30+
return testing::AssertionFailure()
31+
<< "Multiple function declarations found";
32+
33+
Result = Matches[0].getNodeAs<FunctionDecl>("fn");
34+
return testing::AssertionSuccess();
35+
}
36+
37+
TEST(IsCLibraryFunctionTest, AcceptsGlobal) {
38+
const FunctionDecl *Result;
39+
ASSERT_TRUE(extractFunctionDecl(R"cpp(void fun();)cpp", Result));
40+
EXPECT_TRUE(CheckerContext::isCLibraryFunction(Result));
41+
}
42+
43+
TEST(IsCLibraryFunctionTest, AcceptsExternCGlobal) {
44+
const FunctionDecl *Result;
45+
ASSERT_TRUE(
46+
extractFunctionDecl(R"cpp(extern "C" { void fun(); })cpp", Result));
47+
EXPECT_TRUE(CheckerContext::isCLibraryFunction(Result));
48+
}
49+
50+
TEST(IsCLibraryFunctionTest, RejectsNoInlineNoExternalLinkage) {
51+
// Functions that are neither inlined nor externally visible cannot be C library functions.
52+
const FunctionDecl *Result;
53+
ASSERT_TRUE(extractFunctionDecl(R"cpp(static void fun();)cpp", Result));
54+
EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
55+
}
56+
57+
TEST(IsCLibraryFunctionTest, RejectsAnonymousNamespace) {
58+
const FunctionDecl *Result;
59+
ASSERT_TRUE(
60+
extractFunctionDecl(R"cpp(namespace { void fun(); })cpp", Result));
61+
EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
62+
}
63+
64+
TEST(IsCLibraryFunctionTest, AcceptsStdNamespace) {
65+
const FunctionDecl *Result;
66+
ASSERT_TRUE(
67+
extractFunctionDecl(R"cpp(namespace std { void fun(); })cpp", Result));
68+
EXPECT_TRUE(CheckerContext::isCLibraryFunction(Result));
69+
}
70+
71+
TEST(IsCLibraryFunctionTest, RejectsOtherNamespaces) {
72+
const FunctionDecl *Result;
73+
ASSERT_TRUE(
74+
extractFunctionDecl(R"cpp(namespace stdx { void fun(); })cpp", Result));
75+
EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
76+
}
77+
78+
TEST(IsCLibraryFunctionTest, RejectsClassStatic) {
79+
const FunctionDecl *Result;
80+
ASSERT_TRUE(
81+
extractFunctionDecl(R"cpp(class A { static void fun(); };)cpp", Result));
82+
EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
83+
}
84+
85+
TEST(IsCLibraryFunctionTest, RejectsClassMember) {
86+
const FunctionDecl *Result;
87+
ASSERT_TRUE(extractFunctionDecl(R"cpp(class A { void fun(); };)cpp", Result));
88+
EXPECT_FALSE(CheckerContext::isCLibraryFunction(Result));
89+
}

llvm/utils/gn/secondary/clang/unittests/StaticAnalyzer/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ unittest("StaticAnalysisTests") {
1919
"CallEventTest.cpp",
2020
"ConflictingEvalCallsTest.cpp",
2121
"FalsePositiveRefutationBRVisitorTest.cpp",
22+
"IsCLibraryFunctionTest.cpp",
2223
"NoStateChangeFuncVisitorTest.cpp",
2324
"ParamRegionTest.cpp",
2425
"RangeSetTest.cpp",

0 commit comments

Comments
 (0)