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 | class IsCLibraryFunctionTest : public testing::Test { |
16 | std::unique_ptr<ASTUnit> ASTUnitP; |
17 | const FunctionDecl *Result = nullptr; |
18 | |
19 | public: |
20 | const FunctionDecl *getFunctionDecl() const { return Result; } |
21 | |
22 | testing::AssertionResult buildAST(StringRef Code) { |
23 | ASTUnitP = tooling::buildASTFromCode(Code); |
24 | if (!ASTUnitP) |
25 | return testing::AssertionFailure() << "AST construction failed"; |
26 | |
27 | ASTContext &Context = ASTUnitP->getASTContext(); |
28 | if (Context.getDiagnostics().hasErrorOccurred()) |
29 | return testing::AssertionFailure() << "Compilation error"; |
30 | |
31 | auto Matches = ast_matchers::match(Matcher: functionDecl().bind(ID: "fn"), Context); |
32 | if (Matches.empty()) |
33 | return testing::AssertionFailure() << "No function declaration found"; |
34 | |
35 | if (Matches.size() > 1) |
36 | return testing::AssertionFailure() |
37 | << "Multiple function declarations found"; |
38 | |
39 | Result = Matches[0].getNodeAs<FunctionDecl>(ID: "fn"); |
40 | return testing::AssertionSuccess(); |
41 | } |
42 | }; |
43 | |
44 | TEST_F(IsCLibraryFunctionTest, AcceptsGlobal) { |
45 | ASSERT_TRUE(buildAST(R"cpp(void fun();)cpp")); |
46 | EXPECT_TRUE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
47 | } |
48 | |
49 | TEST_F(IsCLibraryFunctionTest, AcceptsExternCGlobal) { |
50 | ASSERT_TRUE(buildAST(R"cpp(extern "C" { void fun(); })cpp")); |
51 | EXPECT_TRUE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
52 | } |
53 | |
54 | TEST_F(IsCLibraryFunctionTest, RejectsNoInlineNoExternalLinkage) { |
55 | // Functions that are neither inlined nor externally visible cannot be C |
56 | // library functions. |
57 | ASSERT_TRUE(buildAST(R"cpp(static void fun();)cpp")); |
58 | EXPECT_FALSE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
59 | } |
60 | |
61 | TEST_F(IsCLibraryFunctionTest, RejectsAnonymousNamespace) { |
62 | ASSERT_TRUE(buildAST(R"cpp(namespace { void fun(); })cpp")); |
63 | EXPECT_FALSE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
64 | } |
65 | |
66 | TEST_F(IsCLibraryFunctionTest, AcceptsStdNamespace) { |
67 | ASSERT_TRUE(buildAST(R"cpp(namespace std { void fun(); })cpp")); |
68 | EXPECT_TRUE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
69 | } |
70 | |
71 | TEST_F(IsCLibraryFunctionTest, RejectsOtherNamespaces) { |
72 | ASSERT_TRUE(buildAST(R"cpp(namespace stdx { void fun(); })cpp")); |
73 | EXPECT_FALSE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
74 | } |
75 | |
76 | TEST_F(IsCLibraryFunctionTest, RejectsClassStatic) { |
77 | ASSERT_TRUE(buildAST(R"cpp(class A { static void fun(); };)cpp")); |
78 | EXPECT_FALSE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
79 | } |
80 | |
81 | TEST_F(IsCLibraryFunctionTest, RejectsClassMember) { |
82 | ASSERT_TRUE(buildAST(R"cpp(class A { void fun(); };)cpp")); |
83 | EXPECT_FALSE(CheckerContext::isCLibraryFunction(getFunctionDecl())); |
84 | } |
85 |