blob: 2772fe20de19bc942f39d07624656f15911be35b [file] [edit]
//===- LifetimeAnnotations.cpp - -*--------------- C++------------------*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
namespace clang::lifetimes {
const FunctionDecl *
getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
}
const CXXMethodDecl *
getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) {
const FunctionDecl *FD = CMD;
return cast_if_present<CXXMethodDecl>(
getDeclWithMergedLifetimeBoundAttrs(FD));
}
bool isNormalAssignmentOperator(const FunctionDecl *FD) {
OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator();
bool IsAssignment = OO == OO_Equal || isCompoundAssignmentOperator(OO);
if (!IsAssignment)
return false;
QualType RetT = FD->getReturnType();
if (!RetT->isLValueReferenceType())
return false;
ASTContext &Ctx = FD->getASTContext();
QualType LHST;
auto *MD = dyn_cast<CXXMethodDecl>(FD);
if (MD && MD->isCXXInstanceMember())
LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType());
else
LHST = FD->getParamDecl(0)->getType();
return Ctx.hasSameType(RetT, LHST);
}
bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) {
CMD = getDeclWithMergedLifetimeBoundAttrs(CMD);
return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>();
}
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
FD = getDeclWithMergedLifetimeBoundAttrs(FD);
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
if (!TSI)
return false;
// Don't declare this variable in the second operand of the for-statement;
// GCC miscompiles that by ending its lifetime before evaluating the
// third operand. See gcc.gnu.org/PR86769.
AttributedTypeLoc ATL;
for (TypeLoc TL = TSI->getTypeLoc();
(ATL = TL.getAsAdjusted<AttributedTypeLoc>());
TL = ATL.getModifiedLoc()) {
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
return true;
}
return isNormalAssignmentOperator(FD);
}
bool isInStlNamespace(const Decl *D) {
const DeclContext *DC = D->getDeclContext();
if (!DC)
return false;
if (const auto *ND = dyn_cast<NamespaceDecl>(DC))
if (const IdentifierInfo *II = ND->getIdentifier()) {
StringRef Name = II->getName();
if (Name.size() >= 2 && Name.front() == '_' &&
(Name[1] == '_' || isUppercase(Name[1])))
return true;
}
return DC->isStdNamespace();
}
bool isPointerLikeType(QualType QT) {
return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();
}
bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee))
if (isGslPointerType(Conv->getConversionType()) &&
Callee->getParent()->hasAttr<OwnerAttr>())
return true;
if (!isInStlNamespace(Callee->getParent()))
return false;
if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
!isGslOwnerType(Callee->getFunctionObjectParameterType()))
return false;
if (isPointerLikeType(Callee->getReturnType())) {
if (!Callee->getIdentifier())
return false;
return llvm::StringSwitch<bool>(Callee->getName())
.Cases(
{// Begin and end iterators.
"begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin",
"crend",
// Inner pointer getters.
"c_str", "data", "get",
// Map and set types.
"find", "equal_range", "lower_bound", "upper_bound"},
true)
.Default(false);
}
if (Callee->getReturnType()->isReferenceType()) {
if (!Callee->getIdentifier()) {
auto OO = Callee->getOverloadedOperator();
if (!Callee->getParent()->hasAttr<OwnerAttr>())
return false;
return OO == OverloadedOperatorKind::OO_Subscript ||
OO == OverloadedOperatorKind::OO_Star;
}
return llvm::StringSwitch<bool>(Callee->getName())
.Cases({"front", "back", "at", "top", "value"}, true)
.Default(false);
}
return false;
}
bool shouldTrackFirstArgument(const FunctionDecl *FD) {
if (!FD->getIdentifier() || FD->getNumParams() != 1)
return false;
const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl();
if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace())
return false;
if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())
return false;
if (FD->getReturnType()->isPointerType() ||
isGslPointerType(FD->getReturnType())) {
return llvm::StringSwitch<bool>(FD->getName())
.Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)
.Cases({"end", "rend", "cend", "crend"}, true)
.Case("data", true)
.Default(false);
}
if (FD->getReturnType()->isReferenceType()) {
return llvm::StringSwitch<bool>(FD->getName())
.Cases({"get", "any_cast"}, true)
.Default(false);
}
return false;
}
template <typename T> static bool isRecordWithAttr(QualType Type) {
auto *RD = Type->getAsCXXRecordDecl();
if (!RD)
return false;
// Generally, if a primary template class declaration is annotated with an
// attribute, all its specializations generated from template instantiations
// should inherit the attribute.
//
// However, since lifetime analysis occurs during parsing, we may encounter
// cases where a full definition of the specialization is not required. In
// such cases, the specialization declaration remains incomplete and lacks the
// attribute. Therefore, we fall back to checking the primary template class.
//
// Note: it is possible for a specialization declaration to have an attribute
// even if the primary template does not.
//
// FIXME: What if the primary template and explicit specialization
// declarations have conflicting attributes? We should consider diagnosing
// this scenario.
bool Result = RD->hasAttr<T>();
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
Result |= CTSD->getSpecializedTemplate()->getTemplatedDecl()->hasAttr<T>();
return Result;
}
bool isGslPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); }
bool isGslOwnerType(QualType QT) { return isRecordWithAttr<OwnerAttr>(QT); }
} // namespace clang::lifetimes