blob: f24cefd7db68554ee8536b21ca29cf201935f989 [file] [log] [blame]
Luca Versari99fddff2022-05-25 10:22:32 -07001// 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 "lifetime_analysis/pointer_compatibility.h"
6
7#include <functional>
8
9#include "gmock/gmock.h"
10#include "gtest/gtest.h"
11#include "lifetime_annotations/lifetime_annotations.h"
12#include "lifetime_annotations/test/run_on_code.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
16
17namespace clang {
18namespace tidy {
19namespace lifetimes {
20namespace {
21
22using clang::ast_matchers::cxxRecordDecl;
23using clang::ast_matchers::enumDecl;
24using clang::ast_matchers::hasName;
25using clang::ast_matchers::match;
26using clang::ast_matchers::selectFirst;
27
28clang::QualType getClassType(llvm::StringRef name,
29 const clang::ASTContext& ast_context) {
30 return ast_context.getRecordType(selectFirst<clang::CXXRecordDecl>(
31 "class", match(cxxRecordDecl(hasName(name)).bind("class"),
32 const_cast<clang::ASTContext&>(ast_context))));
33}
34
35clang::QualType getEnumType(llvm::StringRef name,
36 const clang::ASTContext& ast_context) {
37 return ast_context.getEnumType(selectFirst<clang::EnumDecl>(
38 "enum", match(enumDecl(hasName(name)).bind("enum"),
39 const_cast<clang::ASTContext&>(ast_context))));
40}
41
42bool MayPointTo(clang::QualType pointer_type, clang::QualType object_type,
43 const clang::ASTContext& ast_context) {
44 return clang::tidy::lifetimes::MayPointTo(
45 pointer_type, object_type, const_cast<clang::ASTContext&>(ast_context));
46}
47
48TEST(PointerCompatibilityTest, MayPointTo) {
49 runOnCodeWithLifetimeHandlers(
50 "class Base {};"
51 "class Derived : public Base {};"
52 "class Unrelated {};"
53 "enum SignedEnum {};"
54 "enum UnsignedEnum : unsigned {};"
55 "enum LongEnum : long {};",
56 [](const clang::ASTContext& ast_context,
57 const LifetimeAnnotationContext&) {
58 auto pointer_to = [&ast_context](clang::QualType type) {
59 return ast_context.getPointerType(type);
60 };
61
62 clang::QualType void_type = ast_context.VoidTy;
63 clang::QualType char_type = ast_context.CharTy;
64 clang::QualType signed_char_type = ast_context.SignedCharTy;
65 clang::QualType unsigned_char_type = ast_context.UnsignedCharTy;
66 clang::QualType int_type = ast_context.IntTy;
67 clang::QualType unsigned_int_type = ast_context.UnsignedIntTy;
68 clang::QualType long_type = ast_context.LongTy;
69 clang::QualType bool_type = ast_context.BoolTy;
70
71 clang::QualType base_type = getClassType("Base", ast_context);
72 clang::QualType derived_type = getClassType("Derived", ast_context);
73 clang::QualType unrelated_type = getClassType("Unrelated", ast_context);
74 clang::QualType signed_enum_type =
75 getEnumType("SignedEnum", ast_context);
76 clang::QualType unsigned_enum_type =
77 getEnumType("UnsignedEnum", ast_context);
78 clang::QualType long_enum_type = getEnumType("LongEnum", ast_context);
79
80 // Trivial case: A pointer can point to its exact pointee type.
81 EXPECT_TRUE(MayPointTo(pointer_to(base_type), base_type, ast_context));
82
83 // void pointers and character pointers may point to anything.
84 EXPECT_TRUE(MayPointTo(pointer_to(void_type), base_type, ast_context));
85 EXPECT_TRUE(MayPointTo(pointer_to(char_type), base_type, ast_context));
86 EXPECT_TRUE(
87 MayPointTo(pointer_to(signed_char_type), base_type, ast_context));
88 EXPECT_TRUE(
89 MayPointTo(pointer_to(unsigned_char_type), base_type, ast_context));
90
91 // But an int pointer may not point at an unrelated type.
92 EXPECT_FALSE(MayPointTo(pointer_to(int_type), base_type, ast_context));
93
94 // We also allow a void pointer to be converted back to any other
95 // pointer type, but we don't allow the same for character pointers.
96 EXPECT_TRUE(MayPointTo(pointer_to(base_type), void_type, ast_context));
97 EXPECT_FALSE(MayPointTo(pointer_to(base_type), char_type, ast_context));
98 EXPECT_FALSE(
99 MayPointTo(pointer_to(base_type), signed_char_type, ast_context));
100 EXPECT_FALSE(
101 MayPointTo(pointer_to(base_type), unsigned_char_type, ast_context));
102
103 // A signed integer pointer may point to the unsigned variant of the
104 // integer type and vice versa.
105 EXPECT_TRUE(
106 MayPointTo(pointer_to(int_type), unsigned_int_type, ast_context));
107 EXPECT_TRUE(
108 MayPointTo(pointer_to(unsigned_int_type), int_type, ast_context));
109
110 // An enum pointer may point to any enum that has the same underlying
111 // type or to its underlying type (ignoring signedness in both cases).
112 // Signed enum:
113 EXPECT_TRUE(MayPointTo(pointer_to(signed_enum_type), signed_enum_type,
114 ast_context));
115 EXPECT_TRUE(MayPointTo(pointer_to(signed_enum_type), unsigned_enum_type,
116 ast_context));
117 EXPECT_TRUE(
118 MayPointTo(pointer_to(signed_enum_type), int_type, ast_context));
119 EXPECT_TRUE(MayPointTo(pointer_to(signed_enum_type), unsigned_int_type,
120 ast_context));
121 EXPECT_TRUE(
122 MayPointTo(pointer_to(int_type), signed_enum_type, ast_context));
123 EXPECT_TRUE(MayPointTo(pointer_to(unsigned_int_type), signed_enum_type,
124 ast_context));
125 // Unsigned enum:
126 EXPECT_TRUE(MayPointTo(pointer_to(unsigned_enum_type), signed_enum_type,
127 ast_context));
128 EXPECT_TRUE(MayPointTo(pointer_to(unsigned_enum_type),
129 unsigned_enum_type, ast_context));
130 EXPECT_TRUE(
131 MayPointTo(pointer_to(unsigned_enum_type), int_type, ast_context));
132 EXPECT_TRUE(MayPointTo(pointer_to(unsigned_enum_type),
133 unsigned_int_type, ast_context));
134 EXPECT_TRUE(
135 MayPointTo(pointer_to(int_type), unsigned_enum_type, ast_context));
136 EXPECT_TRUE(MayPointTo(pointer_to(unsigned_int_type),
137 unsigned_enum_type, ast_context));
138 // Underlying types of different width are not compatible:
139 EXPECT_FALSE(MayPointTo(pointer_to(long_enum_type), signed_enum_type,
140 ast_context));
141 EXPECT_FALSE(
142 MayPointTo(pointer_to(long_type), signed_enum_type, ast_context));
143
144 // A bool pointer may point to bool. This is a regression test for an
145 // assertion failure that we were getting because
146 // Type::isUnsignedIntegerType() considers `bool` to be an unsigned
147 // integer type.
148 EXPECT_TRUE(MayPointTo(pointer_to(bool_type), bool_type, ast_context));
149
150 // But an integer pointer may not point at an integer of a different
151 // size.
152 EXPECT_FALSE(MayPointTo(pointer_to(long_type), int_type, ast_context));
153 EXPECT_FALSE(MayPointTo(pointer_to(int_type), long_type, ast_context));
154
155 // A pointer to a base class may point to an object of the derived
156 // class, and vice versa. However, a pointer to a class type may not
157 // point to an object of a class unrelated by inheritance.
158 EXPECT_TRUE(
159 MayPointTo(pointer_to(base_type), derived_type, ast_context));
160 EXPECT_TRUE(
161 MayPointTo(pointer_to(derived_type), base_type, ast_context));
162 EXPECT_FALSE(
163 MayPointTo(pointer_to(base_type), unrelated_type, ast_context));
164
165 // A pointer to const may point at a non-const object (unsurprisingly),
166 // but we also allow the opposite. IOW, in propagating pointees through
167 // a function, we assume the function may cast away const.
168 // As a side note, this is consistent with the "strict aliasing" rules,
169 // which the pointer's pointee type and the dynamic type of the object
170 // to be similar by C++'s definition of similar.
171 EXPECT_TRUE(MayPointTo(pointer_to(base_type.withConst()), base_type,
172 ast_context));
173 EXPECT_TRUE(MayPointTo(pointer_to(base_type), base_type.withConst(),
174 ast_context));
175
176 // Likewise, a pointer to volatile may point at a non-volatile object
177 // and vice versa.
178 EXPECT_TRUE(MayPointTo(pointer_to(base_type.withVolatile()), base_type,
179 ast_context));
180 EXPECT_TRUE(MayPointTo(pointer_to(base_type), base_type.withVolatile(),
181 ast_context));
182
183 // We also allow points-to relationships that would be disallowed by
184 // invariance. The example below is equivalent to the following:
185 // int **pp;
186 // const int ***ppp = &pp;
187 // The code above doesn't compile, but the strict aliasing rules permit
188 // this type of aliasing.
189 EXPECT_TRUE(
190 MayPointTo(pointer_to(pointer_to(pointer_to(int_type.withConst()))),
191 pointer_to(pointer_to(int_type)), ast_context));
192 },
193 {});
194}
195
196} // namespace
197} // namespace lifetimes
198} // namespace tidy
199} // namespace clang