blob: 226b0ccb18a9b5aa8101d7afbb46765d2d7db2c2 [file] [log] [blame]
Googler07271532023-11-29 16:21:50 -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/eligible_ranges.h"
6
7#include <optional>
8#include <string>
9#include <utility>
10
11#include "absl/log/check.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/AST/Decl.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/ASTMatchers/ASTMatchers.h"
16#include "clang/Basic/LLVM.h"
17#include "clang/Testing/TestAST.h"
18#include "llvm/Testing/Annotations/Annotations.h"
19#include "third_party/llvm/llvm-project/third-party/unittest/googlemock/include/gmock/gmock.h" // IWYU pragma: keep
20#include "third_party/llvm/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h"
21
22namespace clang::tidy::nullability {
23namespace {
24using ::clang::ast_matchers::functionDecl;
25using ::llvm::Annotations;
26using ::testing::ExplainMatchResult;
27using ::testing::Optional;
28using ::testing::UnorderedElementsAre;
29
30constexpr char MainFileName[] = "main.cpp";
31
32MATCHER_P2(SlotRange, SlotID, Range,
33 absl::StrCat("is a SlotRange with ID ", SlotID,
34 " and range equivalent to [", Range.Begin, ",",
35 Range.End, ")")) {
36 return arg.slot() == SlotID && Range.Begin == arg.begin() &&
37 Range.End == arg.end();
38}
39
40MATCHER_P2(TypeLocRanges, Path, Ranges, "") {
41 return ExplainMatchResult(Path, arg.path(), result_listener) &&
42 ExplainMatchResult(Ranges, arg.range(), result_listener);
43}
44
45std::optional<clang::tidy::nullability::TypeLocRanges> getEligibleRanges(
46 llvm::StringRef Input) {
47 auto TI = TestInputs(Input);
48 TI.FileName = std::string(MainFileName);
49 auto TU = TestAST(std::move(TI));
50 ASTContext &Context = TU.context();
51 const auto *FunDecl = ast_matchers::selectFirst<FunctionDecl>(
52 "fun", ast_matchers::match(functionDecl().bind("fun"), Context));
53 CHECK(FunDecl != nullptr);
54 return clang::tidy::nullability::getEligibleRanges(*FunDecl);
55}
56
57TEST(GenEditsTest, ReturnAndOneParameterIdentified) {
58 auto Input = Annotations("$r[[int *]]foo($p[[int *]]p) { return p; }");
59 EXPECT_THAT(
60 getEligibleRanges(Input.code()),
61 Optional(TypeLocRanges(
62 MainFileName, UnorderedElementsAre(SlotRange(0, Input.range("r")),
63 SlotRange(1, Input.range("p"))))));
64}
65
66TEST(GenEditsTest, OnlyFirstParameterIdentified) {
67 auto Input = Annotations("void foo([[int *]]p1, int p2) { return; }");
68 EXPECT_THAT(
69 getEligibleRanges(Input.code()),
70 Optional(TypeLocRanges(
71 MainFileName, UnorderedElementsAre(SlotRange(1, Input.range())))));
72}
73
Googler8d9c2d72023-12-01 06:18:07 -080074// Checks that a function decl without a body is handled correctly.
75TEST(GenEditsTest, DeclHandled) {
Googler07271532023-11-29 16:21:50 -080076 auto Input = Annotations("void foo([[int *]]p1, int p2);");
77 EXPECT_THAT(
78 getEligibleRanges(Input.code()),
79 Optional(TypeLocRanges(
80 MainFileName, UnorderedElementsAre(SlotRange(1, Input.range())))));
81}
82
Googler8d9c2d72023-12-01 06:18:07 -080083TEST(GenEditsTest, NestedPointerEligible) {
84 auto Input = Annotations("void foo([[int **]]p);");
85 EXPECT_THAT(
86 getEligibleRanges(Input.code()),
87 Optional(TypeLocRanges(
88 MainFileName, UnorderedElementsAre(SlotRange(1, Input.range())))));
89}
90
91TEST(GenEditsTest, DeclConstExcluded) {
92 auto Input = Annotations("void foo([[int *]] const p1, int p2);");
93 EXPECT_THAT(
94 getEligibleRanges(Input.code()),
95 Optional(TypeLocRanges(
96 MainFileName, UnorderedElementsAre(SlotRange(1, Input.range())))));
97}
98
99TEST(GenEditsTest, PointeeConstIncluded) {
100 auto Input = Annotations("void foo([[const int *]]p1, int p2);");
101 EXPECT_THAT(
102 getEligibleRanges(Input.code()),
103 Optional(TypeLocRanges(
104 MainFileName, UnorderedElementsAre(SlotRange(1, Input.range())))));
105}
106
107TEST(GenEditsTest, NestedPointeeConstIncluded) {
108 auto Input = Annotations("void foo([[const int **]]p1, int p2);");
109 EXPECT_THAT(
110 getEligibleRanges(Input.code()),
111 Optional(TypeLocRanges(
112 MainFileName, UnorderedElementsAre(SlotRange(1, Input.range())))));
113}
114
115TEST(GenEditsTest, FunctionPointerTypeIgnored) {
116 std::string Input = "void foo(int (*p)(int));";
Googler07271532023-11-29 16:21:50 -0800117 EXPECT_EQ(getEligibleRanges(Input), std::nullopt);
118}
119
Googler8d9c2d72023-12-01 06:18:07 -0800120TEST(GenEditsTest, ArrayTypeIgnored) {
121 std::string Input = "void foo(int p[]);";
122 EXPECT_EQ(getEligibleRanges(Input), std::nullopt);
123}
124
125TEST(GenEditsTest, FunctionAndArrayTypeIgnored) {
126 std::string Input = "void foo(int (*z[3])(float));";
127 EXPECT_EQ(getEligibleRanges(Input), std::nullopt);
128}
Googler07271532023-11-29 16:21:50 -0800129
130} // namespace
131} // namespace clang::tidy::nullability