blob: 3d7184e2172b43a9a2b43f288022142f8518ee85 [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "lifetime_analysis/pointer_compatibility.h"
#include <cassert>
#include "lifetime_annotations/pointee_type.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "llvm/Support/ErrorHandling.h"
namespace clang {
namespace tidy {
namespace lifetimes {
// Returns whether `type` is an unsigned integer type or an enum with an
// underlying unsigned integer type.
// Note that, unlike this function, Type::isUnsignedIntegerType() considers
// `bool` to be an unsigned integer type.
static bool IsUnsignedIntegerOrEnumType(clang::QualType type) {
type = type.getCanonicalType();
return type->isUnsignedIntegerType() && !type->isBooleanType();
}
bool PointeesCompatible(clang::QualType pointee_type,
clang::QualType object_type,
clang::ASTContext& ast_context) {
assert(!pointee_type.isNull());
assert(!object_type.isNull());
pointee_type = pointee_type.getCanonicalType();
object_type = object_type.getCanonicalType();
// `void *`, `char *`, `unsigned char *` and `std::byte *` are allowed to
// point at anything.
if (pointee_type->isVoidType() || pointee_type->isCharType() ||
pointee_type->isStdByteType()) {
return true;
}
// Anything is allowed to point at `void`. IOW, a function is allowed to cast
// a void pointer back to any other type of pointer.
if (object_type->isVoidType()) {
return true;
}
// Records.
if (pointee_type->isRecordType()) {
const clang::CXXRecordDecl* pointee_record_decl =
pointee_type->getAsCXXRecordDecl();
const clang::CXXRecordDecl* object_record_decl =
object_type->getAsCXXRecordDecl();
// We leave the case where the records are the same to the hasSimilarType()
// case below.
if (pointee_record_decl && object_record_decl &&
(object_record_decl->isDerivedFrom(pointee_record_decl) ||
pointee_record_decl->isDerivedFrom(object_record_decl))) {
return true;
}
}
// A signed integer pointer may point to the unsigned variant of the integer
// type and vice versa -- so arbitrarily canonicalize integer types to the
// signed version.
if (IsUnsignedIntegerOrEnumType(pointee_type)) {
pointee_type = ast_context.getCorrespondingSignedType(pointee_type);
}
if (IsUnsignedIntegerOrEnumType(object_type)) {
object_type = ast_context.getCorrespondingSignedType(object_type);
}
return ast_context.hasSimilarType(pointee_type, object_type);
}
bool MayPointTo(clang::QualType pointer_type, clang::QualType object_type,
clang::ASTContext& ast_context) {
assert(!pointer_type.isNull());
assert(!object_type.isNull());
pointer_type = pointer_type.getCanonicalType();
object_type = object_type.getCanonicalType();
clang::QualType pointee_type = PointeeType(pointer_type);
if (pointee_type.isNull()) {
llvm::report_fatal_error("pointee_type is null");
}
return PointeesCompatible(pointee_type, object_type, ast_context);
}
} // namespace lifetimes
} // namespace tidy
} // namespace clang