blob: d2001b9f65a9e17a0199af806c8fb9224d6ef10d [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 "lifetime_annotations/pointee_type.h"
8#include "clang/AST/ASTContext.h"
9#include "clang/AST/DeclCXX.h"
10
11namespace clang {
12namespace tidy {
13namespace lifetimes {
14
15// Returns whether `type` is an unsigned integer type or an enum with an
16// underlying unsigned integer type.
17// Note that, unlike this function, Type::isUnsignedIntegerType() considers
18// `bool` to be an unsigned integer type.
19static bool IsUnsignedIntegerOrEnumType(clang::QualType type) {
20 type = type.getCanonicalType();
21 return type->isUnsignedIntegerType() && !type->isBooleanType();
22}
23
24bool PointeesCompatible(clang::QualType pointee_type,
25 clang::QualType object_type,
26 clang::ASTContext& ast_context) {
27 assert(!pointee_type.isNull());
28 assert(!object_type.isNull());
29
30 pointee_type = pointee_type.getCanonicalType();
31 object_type = object_type.getCanonicalType();
32
33 // `void *`, `char *`, `unsigned char *` and `std::byte *` are allowed to
34 // point at anything.
35 if (pointee_type->isVoidType() || pointee_type->isCharType() ||
36 pointee_type->isStdByteType()) {
37 return true;
38 }
39
40 // Anything is allowed to point at `void`. IOW, a function is allowed to cast
41 // a void pointer back to any other type of pointer.
42 if (object_type->isVoidType()) {
43 return true;
44 }
45
46 // Records.
47 if (pointee_type->isRecordType()) {
48 const clang::CXXRecordDecl* pointee_record_decl =
49 pointee_type->getAsCXXRecordDecl();
50 const clang::CXXRecordDecl* object_record_decl =
51 object_type->getAsCXXRecordDecl();
52 // We leave the case where the records are the same to the hasSimilarType()
53 // case below.
54 if (pointee_record_decl && object_record_decl &&
55 (object_record_decl->isDerivedFrom(pointee_record_decl) ||
56 pointee_record_decl->isDerivedFrom(object_record_decl))) {
57 return true;
58 }
59 }
60
61 // A signed integer pointer may point to the unsigned variant of the integer
62 // type and vice versa -- so arbitrarily canonicalize integer types to the
63 // signed version.
64 if (IsUnsignedIntegerOrEnumType(pointee_type)) {
65 pointee_type = ast_context.getCorrespondingSignedType(pointee_type);
66 }
67 if (IsUnsignedIntegerOrEnumType(object_type)) {
68 object_type = ast_context.getCorrespondingSignedType(object_type);
69 }
70
71 return ast_context.hasSimilarType(pointee_type, object_type);
72}
73
74bool MayPointTo(clang::QualType pointer_type, clang::QualType object_type,
75 clang::ASTContext& ast_context) {
76 assert(!pointer_type.isNull());
77 assert(!object_type.isNull());
78
79 pointer_type = pointer_type.getCanonicalType();
80 object_type = object_type.getCanonicalType();
81
82 clang::QualType pointee_type = PointeeType(pointer_type);
83
84 if (pointee_type.isNull()) {
85 llvm::report_fatal_error("pointee_type is null");
86 }
87
88 return PointeesCompatible(pointee_type, object_type, ast_context);
89}
90
91} // namespace lifetimes
92} // namespace tidy
93} // namespace clang