blob: 92d3bca118e64a11fad1516fa800334ce3d48bd9 [file] [log] [blame]
Googler325a12f2024-01-24 12:03:37 -08001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "nullability/inference/replace_macros.h"
6
7#include <memory>
8#include <string>
9#include <string_view>
10
11#include "nullability/inference/ctn_replacement_macros.h"
Googlercd6e81b2024-04-10 06:10:55 -070012#include "nullability/macro_arg_capture.h"
Googler325a12f2024-01-24 12:03:37 -080013#include "clang/AST/Decl.h"
14#include "clang/AST/Expr.h"
15#include "clang/ASTMatchers/ASTMatchFinder.h"
16#include "clang/ASTMatchers/ASTMatchers.h"
17#include "clang/Testing/TestAST.h"
18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/StringRef.h"
20#include "third_party/llvm/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h" // IWYU pragma: keep
21#include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
22
23namespace clang::tidy::nullability {
24namespace {
25
26using ::clang::CallExpr;
Googler2c398c12024-01-24 12:34:33 -080027using ::clang::ast_matchers::anyOf;
Googler325a12f2024-01-24 12:03:37 -080028using ::clang::ast_matchers::callExpr;
29using ::clang::ast_matchers::functionDecl;
30using ::clang::ast_matchers::hasDeclaration;
31using ::clang::ast_matchers::hasName;
32using ::clang::ast_matchers::match;
33using ::clang::ast_matchers::selectFirst;
34using ::testing::IsEmpty;
35using ::testing::IsNull;
36using ::testing::NotNull;
37
38clang::TestInputs getInputs(llvm::StringRef Source) {
39 clang::TestInputs Inputs = Source;
40 for (const auto& Entry : llvm::ArrayRef(ctn_replacement_macros_create(),
41 ctn_replacement_macros_size()))
42 Inputs.ExtraFiles.try_emplace(Entry.name, Entry.data);
43 Inputs.ExtraArgs.push_back("-include");
44 Inputs.ExtraArgs.push_back(std::string(ReplacementMacrosHeaderFileName));
45
46 Inputs.MakeAction = []() { return std::make_unique<ReplaceMacrosAction>(); };
47 return Inputs;
48}
49
50TEST(ReplaceMacrosAction, ReplacesCHECK) {
51 static constexpr std::string_view Source = R"cc(
52#define CHECK(x) \
53 if (!x) __builtin_abort();
54
55 void foo() { CHECK(nullptr); }
56 )cc";
57 clang::TestAST AST(getInputs(Source));
58
59 const CallExpr* ArgumentCapture = selectFirst<CallExpr>(
Googler2c398c12024-01-24 12:34:33 -080060 "call", match(callExpr(hasDeclaration(
61 functionDecl(hasName(ArgCaptureAbortIfFalse))))
Googler325a12f2024-01-24 12:03:37 -080062 .bind("call"),
63 AST.context()));
64 ASSERT_THAT(ArgumentCapture, NotNull());
65 ASSERT_THAT(ArgumentCapture->getArg(0), NotNull());
66 EXPECT_TRUE(ArgumentCapture->getArg(0)->isNullPointerConstant(
67 AST.context(), Expr::NPC_ValueDependentIsNotNull));
68}
69
Googler2c398c12024-01-24 12:34:33 -080070TEST(ReplaceMacrosAction, ReplacesCHECK_NE) {
71 static constexpr std::string_view Source = R"cc(
72#define CHECK_NE(x, y) \
73 if (x == y) __builtin_abort();
74
75 void foo() {
76 CHECK_NE(nullptr, nullptr);
77 }
78 )cc";
79 clang::TestAST AST(getInputs(Source));
80
81 const CallExpr* ArgumentCapture = selectFirst<CallExpr>(
82 "call", match(callExpr(hasDeclaration(
83 functionDecl(hasName(ArgCaptureAbortIfEqual))))
84 .bind("call"),
85 AST.context()));
86 ASSERT_THAT(ArgumentCapture, NotNull());
87 ASSERT_THAT(ArgumentCapture->getArg(0), NotNull());
88 EXPECT_TRUE(ArgumentCapture->getArg(0)->isNullPointerConstant(
89 AST.context(), Expr::NPC_ValueDependentIsNotNull));
90 ASSERT_THAT(ArgumentCapture->getArg(1), NotNull());
91 EXPECT_TRUE(ArgumentCapture->getArg(1)->isNullPointerConstant(
92 AST.context(), Expr::NPC_ValueDependentIsNotNull));
93}
94
Googler325a12f2024-01-24 12:03:37 -080095TEST(ReplaceMacrosAction, DoesNotReplaceMacroNotInReplacementFile) {
96 static constexpr std::string_view Source = R"cc(
97#define TOTALLY_MADE_UP_CHECK_THAT_IS_NOT_IN_REPLACEMENT_FILE(x) \
98 if (!x) __builtin_abort();
99
100 void foo() {
101 TOTALLY_MADE_UP_CHECK_THAT_IS_NOT_IN_REPLACEMENT_FILE(nullptr);
102 }
103 )cc";
104 clang::TestAST AST(getInputs(Source));
105
106 const CallExpr* ArgumentCaptureFunctionCall = selectFirst<CallExpr>(
Googler2c398c12024-01-24 12:34:33 -0800107 "call",
108 match(callExpr(hasDeclaration(
109 anyOf(functionDecl(hasName(ArgCaptureAbortIfFalse)),
110 functionDecl(hasName(ArgCaptureAbortIfEqual)))))
111 .bind("call"),
112 AST.context()));
Googler325a12f2024-01-24 12:03:37 -0800113 ASSERT_THAT(ArgumentCaptureFunctionCall, IsNull());
114}
115
116TEST(ReplaceMacrosAction, ArgumentCaptureCompilesWithVariousTypes) {
117 static constexpr std::string_view Source = R"cc(
118#pragma clang diagnostic push
119#pragma clang diagnostic ignored "-Wmacro-redefined"
120#define CHECK(x) \
121 if (!x) __builtin_abort();
122#pragma clang diagnostic pop
123
124 struct S {
125 S();
126 S(const S &);
127 S(S &&);
128
129 operator bool() &;
130 operator bool() const &;
131 };
132
133 void foo() {
134 S s;
135 CHECK(static_cast<S *>(&s));
136 CHECK(static_cast<const S *>(&s));
137 CHECK(static_cast<S &>(s));
138 CHECK(static_cast<const S &>(s));
139 CHECK(static_cast<S &&>(s));
140 }
141 )cc";
142
143 clang::TestAST AST(getInputs(Source));
144
145 EXPECT_THAT(AST.diagnostics(), IsEmpty());
146}
147} // namespace
148} // namespace clang::tidy::nullability