|  | //===--- CheckExprLifetime.cpp --------------------------------------------===// | 
|  | // | 
|  | // 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 "CheckExprLifetime.h" | 
|  | #include "clang/AST/Decl.h" | 
|  | #include "clang/AST/Expr.h" | 
|  | #include "clang/AST/Type.h" | 
|  | #include "clang/Basic/DiagnosticSema.h" | 
|  | #include "clang/Sema/Initialization.h" | 
|  | #include "clang/Sema/Sema.h" | 
|  | #include "llvm/ADT/PointerIntPair.h" | 
|  |  | 
|  | namespace clang::sema { | 
|  | namespace { | 
|  | enum LifetimeKind { | 
|  | /// The lifetime of a temporary bound to this entity ends at the end of the | 
|  | /// full-expression, and that's (probably) fine. | 
|  | LK_FullExpression, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity is extended to the | 
|  | /// lifeitme of the entity itself. | 
|  | LK_Extended, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity probably ends too soon, | 
|  | /// because the entity is allocated in a new-expression. | 
|  | LK_New, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity ends too soon, because | 
|  | /// the entity is a return object. | 
|  | LK_Return, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity ends too soon, because | 
|  | /// the entity passed to a musttail function call. | 
|  | LK_MustTail, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity ends too soon, because | 
|  | /// the entity is the result of a statement expression. | 
|  | LK_StmtExprResult, | 
|  |  | 
|  | /// This is a mem-initializer: if it would extend a temporary (other than via | 
|  | /// a default member initializer), the program is ill-formed. | 
|  | LK_MemInitializer, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity may end too soon, | 
|  | /// because the entity is a pointer and we assign the address of a temporary | 
|  | /// object to it. | 
|  | LK_Assignment, | 
|  |  | 
|  | /// The lifetime of a temporary bound to this entity may end too soon, | 
|  | /// because the entity may capture the reference to a temporary object. | 
|  | LK_LifetimeCapture, | 
|  | }; | 
|  | using LifetimeResult = | 
|  | llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>; | 
|  | } // namespace | 
|  |  | 
|  | /// Determine the declaration which an initialized entity ultimately refers to, | 
|  | /// for the purpose of lifetime-extending a temporary bound to a reference in | 
|  | /// the initialization of \p Entity. | 
|  | static LifetimeResult | 
|  | getEntityLifetime(const InitializedEntity *Entity, | 
|  | const InitializedEntity *InitField = nullptr) { | 
|  | // C++11 [class.temporary]p5: | 
|  | switch (Entity->getKind()) { | 
|  | case InitializedEntity::EK_Variable: | 
|  | //   The temporary [...] persists for the lifetime of the reference | 
|  | return {Entity, LK_Extended}; | 
|  |  | 
|  | case InitializedEntity::EK_Member: | 
|  | // For subobjects, we look at the complete object. | 
|  | if (Entity->getParent()) | 
|  | return getEntityLifetime(Entity->getParent(), Entity); | 
|  |  | 
|  | //   except: | 
|  | // C++17 [class.base.init]p8: | 
|  | //   A temporary expression bound to a reference member in a | 
|  | //   mem-initializer is ill-formed. | 
|  | // C++17 [class.base.init]p11: | 
|  | //   A temporary expression bound to a reference member from a | 
|  | //   default member initializer is ill-formed. | 
|  | // | 
|  | // The context of p11 and its example suggest that it's only the use of a | 
|  | // default member initializer from a constructor that makes the program | 
|  | // ill-formed, not its mere existence, and that it can even be used by | 
|  | // aggregate initialization. | 
|  | return {Entity, Entity->isDefaultMemberInitializer() ? LK_Extended | 
|  | : LK_MemInitializer}; | 
|  |  | 
|  | case InitializedEntity::EK_Binding: | 
|  | // Per [dcl.decomp]p3, the binding is treated as a variable of reference | 
|  | // type. | 
|  | return {Entity, LK_Extended}; | 
|  |  | 
|  | case InitializedEntity::EK_Parameter: | 
|  | case InitializedEntity::EK_Parameter_CF_Audited: | 
|  | //   -- A temporary bound to a reference parameter in a function call | 
|  | //      persists until the completion of the full-expression containing | 
|  | //      the call. | 
|  | return {nullptr, LK_FullExpression}; | 
|  |  | 
|  | case InitializedEntity::EK_TemplateParameter: | 
|  | // FIXME: This will always be ill-formed; should we eagerly diagnose it | 
|  | // here? | 
|  | return {nullptr, LK_FullExpression}; | 
|  |  | 
|  | case InitializedEntity::EK_Result: | 
|  | //   -- The lifetime of a temporary bound to the returned value in a | 
|  | //      function return statement is not extended; the temporary is | 
|  | //      destroyed at the end of the full-expression in the return statement. | 
|  | return {nullptr, LK_Return}; | 
|  |  | 
|  | case InitializedEntity::EK_StmtExprResult: | 
|  | // FIXME: Should we lifetime-extend through the result of a statement | 
|  | // expression? | 
|  | return {nullptr, LK_StmtExprResult}; | 
|  |  | 
|  | case InitializedEntity::EK_New: | 
|  | //   -- A temporary bound to a reference in a new-initializer persists | 
|  | //      until the completion of the full-expression containing the | 
|  | //      new-initializer. | 
|  | return {nullptr, LK_New}; | 
|  |  | 
|  | case InitializedEntity::EK_Temporary: | 
|  | case InitializedEntity::EK_CompoundLiteralInit: | 
|  | case InitializedEntity::EK_RelatedResult: | 
|  | // We don't yet know the storage duration of the surrounding temporary. | 
|  | // Assume it's got full-expression duration for now, it will patch up our | 
|  | // storage duration if that's not correct. | 
|  | return {nullptr, LK_FullExpression}; | 
|  |  | 
|  | case InitializedEntity::EK_ArrayElement: | 
|  | // For subobjects, we look at the complete object. | 
|  | return getEntityLifetime(Entity->getParent(), InitField); | 
|  |  | 
|  | case InitializedEntity::EK_Base: | 
|  | // For subobjects, we look at the complete object. | 
|  | if (Entity->getParent()) | 
|  | return getEntityLifetime(Entity->getParent(), InitField); | 
|  | return {InitField, LK_MemInitializer}; | 
|  |  | 
|  | case InitializedEntity::EK_Delegating: | 
|  | // We can reach this case for aggregate initialization in a constructor: | 
|  | //   struct A { int &&r; }; | 
|  | //   struct B : A { B() : A{0} {} }; | 
|  | // In this case, use the outermost field decl as the context. | 
|  | return {InitField, LK_MemInitializer}; | 
|  |  | 
|  | case InitializedEntity::EK_BlockElement: | 
|  | case InitializedEntity::EK_LambdaToBlockConversionBlockElement: | 
|  | case InitializedEntity::EK_LambdaCapture: | 
|  | case InitializedEntity::EK_VectorElement: | 
|  | case InitializedEntity::EK_ComplexElement: | 
|  | return {nullptr, LK_FullExpression}; | 
|  |  | 
|  | case InitializedEntity::EK_Exception: | 
|  | // FIXME: Can we diagnose lifetime problems with exceptions? | 
|  | return {nullptr, LK_FullExpression}; | 
|  |  | 
|  | case InitializedEntity::EK_ParenAggInitMember: | 
|  | //   -- A temporary object bound to a reference element of an aggregate of | 
|  | //      class type initialized from a parenthesized expression-list | 
|  | //      [dcl.init, 9.3] persists until the completion of the full-expression | 
|  | //      containing the expression-list. | 
|  | return {nullptr, LK_FullExpression}; | 
|  | } | 
|  |  | 
|  | llvm_unreachable("unknown entity kind"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | enum ReferenceKind { | 
|  | /// Lifetime would be extended by a reference binding to a temporary. | 
|  | RK_ReferenceBinding, | 
|  | /// Lifetime would be extended by a std::initializer_list object binding to | 
|  | /// its backing array. | 
|  | RK_StdInitializerList, | 
|  | }; | 
|  |  | 
|  | /// A temporary or local variable. This will be one of: | 
|  | ///  * A MaterializeTemporaryExpr. | 
|  | ///  * A DeclRefExpr whose declaration is a local. | 
|  | ///  * An AddrLabelExpr. | 
|  | ///  * A BlockExpr for a block with captures. | 
|  | using Local = Expr *; | 
|  |  | 
|  | /// Expressions we stepped over when looking for the local state. Any steps | 
|  | /// that would inhibit lifetime extension or take us out of subexpressions of | 
|  | /// the initializer are included. | 
|  | struct IndirectLocalPathEntry { | 
|  | enum EntryKind { | 
|  | DefaultInit, | 
|  | AddressOf, | 
|  | VarInit, | 
|  | LValToRVal, | 
|  | LifetimeBoundCall, | 
|  | TemporaryCopy, | 
|  | LambdaCaptureInit, | 
|  | MemberExpr, | 
|  | GslReferenceInit, | 
|  | GslPointerInit, | 
|  | GslPointerAssignment, | 
|  | DefaultArg, | 
|  | ParenAggInit, | 
|  | } Kind; | 
|  | Expr *E; | 
|  | union { | 
|  | const Decl *D = nullptr; | 
|  | const LambdaCapture *Capture; | 
|  | }; | 
|  | IndirectLocalPathEntry() {} | 
|  | IndirectLocalPathEntry(EntryKind K, Expr *E) : Kind(K), E(E) {} | 
|  | IndirectLocalPathEntry(EntryKind K, Expr *E, const Decl *D) | 
|  | : Kind(K), E(E), D(D) {} | 
|  | IndirectLocalPathEntry(EntryKind K, Expr *E, const LambdaCapture *Capture) | 
|  | : Kind(K), E(E), Capture(Capture) {} | 
|  | }; | 
|  |  | 
|  | using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>; | 
|  |  | 
|  | struct RevertToOldSizeRAII { | 
|  | IndirectLocalPath &Path; | 
|  | unsigned OldSize = Path.size(); | 
|  | RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {} | 
|  | ~RevertToOldSizeRAII() { Path.resize(OldSize); } | 
|  | }; | 
|  |  | 
|  | using LocalVisitor = llvm::function_ref<bool(IndirectLocalPath &Path, Local L, | 
|  | ReferenceKind RK)>; | 
|  | } // namespace | 
|  |  | 
|  | static bool isVarOnPath(const IndirectLocalPath &Path, VarDecl *VD) { | 
|  | for (auto E : Path) | 
|  | if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool pathContainsInit(const IndirectLocalPath &Path) { | 
|  | return llvm::any_of(Path, [=](IndirectLocalPathEntry E) { | 
|  | return E.Kind == IndirectLocalPathEntry::DefaultInit || | 
|  | E.Kind == IndirectLocalPathEntry::VarInit; | 
|  | }); | 
|  | } | 
|  |  | 
|  | static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, | 
|  | Expr *Init, LocalVisitor Visit, | 
|  | bool RevisitSubinits); | 
|  |  | 
|  | static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, | 
|  | Expr *Init, ReferenceKind RK, | 
|  | LocalVisitor Visit); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | // Tells whether the type is annotated with [[gsl::Pointer]]. | 
|  | bool isGLSPointerType(QualType QT) { return isRecordWithAttr<PointerAttr>(QT); } | 
|  |  | 
|  | static bool isPointerLikeType(QualType QT) { | 
|  | return isGLSPointerType(QT) || QT->isPointerType() || QT->isNullPtrType(); | 
|  | } | 
|  |  | 
|  | // Decl::isInStdNamespace will return false for iterators in some STL | 
|  | // implementations due to them being defined in a namespace outside of the std | 
|  | // namespace. | 
|  | static 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(); | 
|  | } | 
|  |  | 
|  | // Returns true if the given Record decl is a form of `GSLOwner<Pointer>` | 
|  | // type, e.g. std::vector<string_view>, std::optional<string_view>. | 
|  | static bool isContainerOfPointer(const RecordDecl *Container) { | 
|  | if (const auto *CTSD = | 
|  | dyn_cast_if_present<ClassTemplateSpecializationDecl>(Container)) { | 
|  | if (!CTSD->hasAttr<OwnerAttr>()) // Container must be a GSL owner type. | 
|  | return false; | 
|  | const auto &TAs = CTSD->getTemplateArgs(); | 
|  | return TAs.size() > 0 && TAs[0].getKind() == TemplateArgument::Type && | 
|  | isPointerLikeType(TAs[0].getAsType()); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | static bool isContainerOfOwner(const RecordDecl *Container) { | 
|  | const auto *CTSD = | 
|  | dyn_cast_if_present<ClassTemplateSpecializationDecl>(Container); | 
|  | if (!CTSD) | 
|  | return false; | 
|  | if (!CTSD->hasAttr<OwnerAttr>()) // Container must be a GSL owner type. | 
|  | return false; | 
|  | const auto &TAs = CTSD->getTemplateArgs(); | 
|  | return TAs.size() > 0 && TAs[0].getKind() == TemplateArgument::Type && | 
|  | isRecordWithAttr<OwnerAttr>(TAs[0].getAsType()); | 
|  | } | 
|  |  | 
|  | // Returns true if the given Record is `std::initializer_list<pointer>`. | 
|  | static bool isStdInitializerListOfPointer(const RecordDecl *RD) { | 
|  | if (const auto *CTSD = | 
|  | dyn_cast_if_present<ClassTemplateSpecializationDecl>(RD)) { | 
|  | const auto &TAs = CTSD->getTemplateArgs(); | 
|  | return isInStlNamespace(RD) && RD->getIdentifier() && | 
|  | RD->getName() == "initializer_list" && TAs.size() > 0 && | 
|  | TAs[0].getKind() == TemplateArgument::Type && | 
|  | isPointerLikeType(TAs[0].getAsType()); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) { | 
|  | if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee)) | 
|  | if (isRecordWithAttr<PointerAttr>(Conv->getConversionType()) && | 
|  | Callee->getParent()->hasAttr<OwnerAttr>()) | 
|  | return true; | 
|  | if (!isInStlNamespace(Callee->getParent())) | 
|  | return false; | 
|  | if (!isRecordWithAttr<PointerAttr>( | 
|  | Callee->getFunctionObjectParameterType()) && | 
|  | !isRecordWithAttr<OwnerAttr>(Callee->getFunctionObjectParameterType())) | 
|  | return false; | 
|  | if (isPointerLikeType(Callee->getReturnType())) { | 
|  | if (!Callee->getIdentifier()) | 
|  | return false; | 
|  | return llvm::StringSwitch<bool>(Callee->getName()) | 
|  | .Cases("begin", "rbegin", "cbegin", "crbegin", true) | 
|  | .Cases("end", "rend", "cend", "crend", true) | 
|  | .Cases("c_str", "data", "get", true) | 
|  | // Map and set types. | 
|  | .Cases("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; | 
|  | } | 
|  |  | 
|  | static 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() || | 
|  | isRecordWithAttr<PointerAttr>(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; | 
|  | } | 
|  |  | 
|  | // Returns true if the given constructor is a copy-like constructor, such as | 
|  | // `Ctor(Owner<U>&&)` or `Ctor(const Owner<U>&)`. | 
|  | static bool isCopyLikeConstructor(const CXXConstructorDecl *Ctor) { | 
|  | if (!Ctor || Ctor->param_size() != 1) | 
|  | return false; | 
|  | const auto *ParamRefType = | 
|  | Ctor->getParamDecl(0)->getType()->getAs<ReferenceType>(); | 
|  | if (!ParamRefType) | 
|  | return false; | 
|  |  | 
|  | // Check if the first parameter type is "Owner<U>". | 
|  | if (const auto *TST = | 
|  | ParamRefType->getPointeeType()->getAs<TemplateSpecializationType>()) | 
|  | return TST->getTemplateName() | 
|  | .getAsTemplateDecl() | 
|  | ->getTemplatedDecl() | 
|  | ->hasAttr<OwnerAttr>(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Returns true if we should perform the GSL analysis on the first argument for | 
|  | // the given constructor. | 
|  | static bool | 
|  | shouldTrackFirstArgumentForConstructor(const CXXConstructExpr *Ctor) { | 
|  | const auto *LHSRecordDecl = Ctor->getConstructor()->getParent(); | 
|  |  | 
|  | // Case 1, construct a GSL pointer, e.g. std::string_view | 
|  | // Always inspect when LHS is a pointer. | 
|  | if (LHSRecordDecl->hasAttr<PointerAttr>()) | 
|  | return true; | 
|  |  | 
|  | if (Ctor->getConstructor()->param_empty() || | 
|  | !isContainerOfPointer(LHSRecordDecl)) | 
|  | return false; | 
|  |  | 
|  | // Now, the LHS is an Owner<Pointer> type, e.g., std::vector<string_view>. | 
|  | // | 
|  | // At a high level, we cannot precisely determine what the nested pointer | 
|  | // owns. However, by analyzing the RHS owner type, we can use heuristics to | 
|  | // infer ownership information. These heuristics are designed to be | 
|  | // conservative, minimizing false positives while still providing meaningful | 
|  | // diagnostics. | 
|  | // | 
|  | // While this inference isn't perfect, it helps catch common use-after-free | 
|  | // patterns. | 
|  | auto RHSArgType = Ctor->getArg(0)->getType(); | 
|  | const auto *RHSRD = RHSArgType->getAsRecordDecl(); | 
|  | // LHS is constructed from an intializer_list. | 
|  | // | 
|  | // std::initializer_list is a proxy object that provides access to the backing | 
|  | // array. We perform analysis on it to determine if there are any dangling | 
|  | // temporaries in the backing array. | 
|  | // E.g. std::vector<string_view> abc = {string()}; | 
|  | if (isStdInitializerListOfPointer(RHSRD)) | 
|  | return true; | 
|  |  | 
|  | // RHS must be an owner. | 
|  | if (!isRecordWithAttr<OwnerAttr>(RHSArgType)) | 
|  | return false; | 
|  |  | 
|  | // Bail out if the RHS is Owner<Pointer>. | 
|  | // | 
|  | // We cannot reliably determine what the LHS nested pointer owns -- it could | 
|  | // be the entire RHS or the nested pointer in RHS. To avoid false positives, | 
|  | // we skip this case, such as: | 
|  | //   std::stack<std::string_view> s(std::deque<std::string_view>{}); | 
|  | // | 
|  | // TODO: this also has a false negative, it doesn't catch the case like: | 
|  | //   std::optional<span<int*>> os = std::vector<int*>{} | 
|  | if (isContainerOfPointer(RHSRD)) | 
|  | return false; | 
|  |  | 
|  | // Assume that the nested Pointer is constructed from the nested Owner. | 
|  | // E.g. std::optional<string_view> sv = std::optional<string>(s); | 
|  | if (isContainerOfOwner(RHSRD)) | 
|  | return true; | 
|  |  | 
|  | // Now, the LHS is an Owner<Pointer> and the RHS is an Owner<X>,  where X is | 
|  | // neither an `Owner` nor a `Pointer`. | 
|  | // | 
|  | // Use the constructor's signature as a hint. If it is a copy-like constructor | 
|  | // `Owner1<Pointer>(Owner2<X>&&)`, we assume that the nested pointer is | 
|  | // constructed from X. In such cases, we do not diagnose, as `X` is not an | 
|  | // owner, e.g. | 
|  | //   std::optional<string_view> sv = std::optional<Foo>(); | 
|  | if (const auto *PrimaryCtorTemplate = | 
|  | Ctor->getConstructor()->getPrimaryTemplate(); | 
|  | PrimaryCtorTemplate && | 
|  | isCopyLikeConstructor(dyn_cast_if_present<CXXConstructorDecl>( | 
|  | PrimaryCtorTemplate->getTemplatedDecl()))) { | 
|  | return false; | 
|  | } | 
|  | // Assume that the nested pointer is constructed from the whole RHS. | 
|  | // E.g. optional<string_view> s = std::string(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Return true if this is an "normal" assignment operator. | 
|  | // We assume that a normal assignment operator always returns *this, that is, | 
|  | // an lvalue reference that is the same type as the implicit object parameter | 
|  | // (or the LHS for a non-member operator$=). | 
|  | static bool isNormalAssignmentOperator(const FunctionDecl *FD) { | 
|  | OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator(); | 
|  | if (OO == OO_Equal || isCompoundAssignmentOperator(OO)) { | 
|  | QualType RetT = FD->getReturnType(); | 
|  | if (RetT->isLValueReferenceType()) { | 
|  | 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(); | 
|  | if (Ctx.hasSameType(RetT, LHST)) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static const FunctionDecl * | 
|  | getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) { | 
|  | return FD != nullptr ? FD->getMostRecentDecl() : nullptr; | 
|  | } | 
|  |  | 
|  | static const CXXMethodDecl * | 
|  | getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) { | 
|  | const FunctionDecl *FD = CMD; | 
|  | return cast_if_present<CXXMethodDecl>( | 
|  | getDeclWithMergedLifetimeBoundAttrs(FD)); | 
|  | } | 
|  |  | 
|  | 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<LifetimeBoundAttr>()) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return isNormalAssignmentOperator(FD); | 
|  | } | 
|  |  | 
|  | // Visit lifetimebound or gsl-pointer arguments. | 
|  | static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call, | 
|  | LocalVisitor Visit) { | 
|  | const FunctionDecl *Callee; | 
|  | ArrayRef<Expr *> Args; | 
|  |  | 
|  | if (auto *CE = dyn_cast<CallExpr>(Call)) { | 
|  | Callee = CE->getDirectCallee(); | 
|  | Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs()); | 
|  | } else { | 
|  | auto *CCE = cast<CXXConstructExpr>(Call); | 
|  | Callee = CCE->getConstructor(); | 
|  | Args = llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs()); | 
|  | } | 
|  | if (!Callee) | 
|  | return; | 
|  |  | 
|  | bool EnableGSLAnalysis = !Callee->getASTContext().getDiagnostics().isIgnored( | 
|  | diag::warn_dangling_lifetime_pointer, SourceLocation()); | 
|  | Expr *ObjectArg = nullptr; | 
|  | if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) { | 
|  | ObjectArg = Args[0]; | 
|  | Args = Args.slice(1); | 
|  | } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) { | 
|  | ObjectArg = MCE->getImplicitObjectArgument(); | 
|  | } | 
|  |  | 
|  | auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) { | 
|  | Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D}); | 
|  | if (Arg->isGLValue()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding, | 
|  | Visit); | 
|  | else | 
|  | visitLocalsRetainedByInitializer(Path, Arg, Visit, true); | 
|  | Path.pop_back(); | 
|  | }; | 
|  | auto VisitGSLPointerArg = [&](const FunctionDecl *Callee, Expr *Arg) { | 
|  | auto ReturnType = Callee->getReturnType(); | 
|  |  | 
|  | // Once we initialized a value with a non gsl-owner reference, it can no | 
|  | // longer dangle. | 
|  | if (ReturnType->isReferenceType() && | 
|  | !isRecordWithAttr<OwnerAttr>(ReturnType->getPointeeType())) { | 
|  | for (const IndirectLocalPathEntry &PE : llvm::reverse(Path)) { | 
|  | if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit || | 
|  | PE.Kind == IndirectLocalPathEntry::LifetimeBoundCall) | 
|  | continue; | 
|  | if (PE.Kind == IndirectLocalPathEntry::GslPointerInit || | 
|  | PE.Kind == IndirectLocalPathEntry::GslPointerAssignment) | 
|  | return; | 
|  | break; | 
|  | } | 
|  | } | 
|  | Path.push_back({ReturnType->isReferenceType() | 
|  | ? IndirectLocalPathEntry::GslReferenceInit | 
|  | : IndirectLocalPathEntry::GslPointerInit, | 
|  | Arg, Callee}); | 
|  | if (Arg->isGLValue()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding, | 
|  | Visit); | 
|  | else | 
|  | visitLocalsRetainedByInitializer(Path, Arg, Visit, true); | 
|  | Path.pop_back(); | 
|  | }; | 
|  |  | 
|  | bool CheckCoroCall = false; | 
|  | if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) { | 
|  | CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() && | 
|  | RD->hasAttr<CoroReturnTypeAttr>() && | 
|  | !Callee->hasAttr<CoroDisableLifetimeBoundAttr>(); | 
|  | } | 
|  |  | 
|  | if (ObjectArg) { | 
|  | bool CheckCoroObjArg = CheckCoroCall; | 
|  | // Coroutine lambda objects with empty capture list are not lifetimebound. | 
|  | if (auto *LE = dyn_cast<LambdaExpr>(ObjectArg->IgnoreImplicit()); | 
|  | LE && LE->captures().empty()) | 
|  | CheckCoroObjArg = false; | 
|  | // Allow `get_return_object()` as the object param (__promise) is not | 
|  | // lifetimebound. | 
|  | if (Sema::CanBeGetReturnObject(Callee)) | 
|  | CheckCoroObjArg = false; | 
|  | if (implicitObjectParamIsLifetimeBound(Callee) || CheckCoroObjArg) | 
|  | VisitLifetimeBoundArg(Callee, ObjectArg); | 
|  | else if (EnableGSLAnalysis) { | 
|  | if (auto *CME = dyn_cast<CXXMethodDecl>(Callee); | 
|  | CME && shouldTrackImplicitObjectArg(CME)) | 
|  | VisitGSLPointerArg(Callee, ObjectArg); | 
|  | } | 
|  | } | 
|  |  | 
|  | const FunctionDecl *CanonCallee = getDeclWithMergedLifetimeBoundAttrs(Callee); | 
|  | unsigned NP = std::min(Callee->getNumParams(), CanonCallee->getNumParams()); | 
|  | for (unsigned I = 0, N = std::min<unsigned>(NP, Args.size()); I != N; ++I) { | 
|  | Expr *Arg = Args[I]; | 
|  | RevertToOldSizeRAII RAII(Path); | 
|  | if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) { | 
|  | Path.push_back( | 
|  | {IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()}); | 
|  | Arg = DAE->getExpr(); | 
|  | } | 
|  | if (CheckCoroCall || | 
|  | CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>()) | 
|  | VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg); | 
|  | else if (const auto *CaptureAttr = | 
|  | CanonCallee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>(); | 
|  | CaptureAttr && isa<CXXConstructorDecl>(CanonCallee) && | 
|  | llvm::any_of(CaptureAttr->params(), [](int ArgIdx) { | 
|  | return ArgIdx == LifetimeCaptureByAttr::This; | 
|  | })) | 
|  | // `lifetime_capture_by(this)` in a class constructor has the same | 
|  | // semantics as `lifetimebound`: | 
|  | // | 
|  | // struct Foo { | 
|  | //   const int& a; | 
|  | //   // Equivalent to Foo(const int& t [[clang::lifetimebound]]) | 
|  | //   Foo(const int& t [[clang::lifetime_capture_by(this)]]) : a(t) {} | 
|  | // }; | 
|  | // | 
|  | // In the implementation, `lifetime_capture_by` is treated as an alias for | 
|  | // `lifetimebound` and shares the same code path. This implies the emitted | 
|  | // diagnostics will be emitted under `-Wdangling`, not | 
|  | // `-Wdangling-capture`. | 
|  | VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg); | 
|  | else if (EnableGSLAnalysis && I == 0) { | 
|  | // Perform GSL analysis for the first argument | 
|  | if (shouldTrackFirstArgument(CanonCallee)) { | 
|  | VisitGSLPointerArg(CanonCallee, Arg); | 
|  | } else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call); | 
|  | Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) { | 
|  | VisitGSLPointerArg(Ctor->getConstructor(), Arg); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Visit the locals that would be reachable through a reference bound to the | 
|  | /// glvalue expression \c Init. | 
|  | static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, | 
|  | Expr *Init, ReferenceKind RK, | 
|  | LocalVisitor Visit) { | 
|  | RevertToOldSizeRAII RAII(Path); | 
|  |  | 
|  | // Walk past any constructs which we can lifetime-extend across. | 
|  | Expr *Old; | 
|  | do { | 
|  | Old = Init; | 
|  |  | 
|  | if (auto *FE = dyn_cast<FullExpr>(Init)) | 
|  | Init = FE->getSubExpr(); | 
|  |  | 
|  | if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { | 
|  | // If this is just redundant braces around an initializer, step over it. | 
|  | if (ILE->isTransparent()) | 
|  | Init = ILE->getInit(0); | 
|  | } | 
|  |  | 
|  | if (MemberExpr *ME = dyn_cast<MemberExpr>(Init->IgnoreImpCasts())) | 
|  | Path.push_back( | 
|  | {IndirectLocalPathEntry::MemberExpr, ME, ME->getMemberDecl()}); | 
|  | // Step over any subobject adjustments; we may have a materialized | 
|  | // temporary inside them. | 
|  | Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); | 
|  |  | 
|  | // Per current approach for DR1376, look through casts to reference type | 
|  | // when performing lifetime extension. | 
|  | if (CastExpr *CE = dyn_cast<CastExpr>(Init)) | 
|  | if (CE->getSubExpr()->isGLValue()) | 
|  | Init = CE->getSubExpr(); | 
|  |  | 
|  | // Per the current approach for DR1299, look through array element access | 
|  | // on array glvalues when performing lifetime extension. | 
|  | if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Init)) { | 
|  | Init = ASE->getBase(); | 
|  | auto *ICE = dyn_cast<ImplicitCastExpr>(Init); | 
|  | if (ICE && ICE->getCastKind() == CK_ArrayToPointerDecay) | 
|  | Init = ICE->getSubExpr(); | 
|  | else | 
|  | // We can't lifetime extend through this but we might still find some | 
|  | // retained temporaries. | 
|  | return visitLocalsRetainedByInitializer(Path, Init, Visit, true); | 
|  | } | 
|  |  | 
|  | // Step into CXXDefaultInitExprs so we can diagnose cases where a | 
|  | // constructor inherits one as an implicit mem-initializer. | 
|  | if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { | 
|  | Path.push_back( | 
|  | {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); | 
|  | Init = DIE->getExpr(); | 
|  | } | 
|  | } while (Init != Old); | 
|  |  | 
|  | if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) { | 
|  | if (Visit(Path, Local(MTE), RK)) | 
|  | visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit, true); | 
|  | } | 
|  |  | 
|  | if (auto *M = dyn_cast<MemberExpr>(Init)) { | 
|  | // Lifetime of a non-reference type field is same as base object. | 
|  | if (auto *F = dyn_cast<FieldDecl>(M->getMemberDecl()); | 
|  | F && !F->getType()->isReferenceType()) | 
|  | visitLocalsRetainedByInitializer(Path, M->getBase(), Visit, true); | 
|  | } | 
|  |  | 
|  | if (isa<CallExpr>(Init)) | 
|  | return visitFunctionCallArguments(Path, Init, Visit); | 
|  |  | 
|  | switch (Init->getStmtClass()) { | 
|  | case Stmt::DeclRefExprClass: { | 
|  | // If we find the name of a local non-reference parameter, we could have a | 
|  | // lifetime problem. | 
|  | auto *DRE = cast<DeclRefExpr>(Init); | 
|  | auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); | 
|  | if (VD && VD->hasLocalStorage() && | 
|  | !DRE->refersToEnclosingVariableOrCapture()) { | 
|  | if (!VD->getType()->isReferenceType()) { | 
|  | Visit(Path, Local(DRE), RK); | 
|  | } else if (isa<ParmVarDecl>(DRE->getDecl())) { | 
|  | // The lifetime of a reference parameter is unknown; assume it's OK | 
|  | // for now. | 
|  | break; | 
|  | } else if (VD->getInit() && !isVarOnPath(Path, VD)) { | 
|  | Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); | 
|  | visitLocalsRetainedByReferenceBinding(Path, VD->getInit(), | 
|  | RK_ReferenceBinding, Visit); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::UnaryOperatorClass: { | 
|  | // The only unary operator that make sense to handle here | 
|  | // is Deref.  All others don't resolve to a "name."  This includes | 
|  | // handling all sorts of rvalues passed to a unary operator. | 
|  | const UnaryOperator *U = cast<UnaryOperator>(Init); | 
|  | if (U->getOpcode() == UO_Deref) | 
|  | visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::ArraySectionExprClass: { | 
|  | visitLocalsRetainedByInitializer( | 
|  | Path, cast<ArraySectionExpr>(Init)->getBase(), Visit, true); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::ConditionalOperatorClass: | 
|  | case Stmt::BinaryConditionalOperatorClass: { | 
|  | auto *C = cast<AbstractConditionalOperator>(Init); | 
|  | if (!C->getTrueExpr()->getType()->isVoidType()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit); | 
|  | if (!C->getFalseExpr()->getType()->isVoidType()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::CompoundLiteralExprClass: { | 
|  | if (auto *CLE = dyn_cast<CompoundLiteralExpr>(Init)) { | 
|  | if (!CLE->isFileScope()) | 
|  | Visit(Path, Local(CLE), RK); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | // FIXME: Visit the left-hand side of an -> or ->*. | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Visit the locals that would be reachable through an object initialized by | 
|  | /// the prvalue expression \c Init. | 
|  | static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, | 
|  | Expr *Init, LocalVisitor Visit, | 
|  | bool RevisitSubinits) { | 
|  | RevertToOldSizeRAII RAII(Path); | 
|  |  | 
|  | Expr *Old; | 
|  | do { | 
|  | Old = Init; | 
|  |  | 
|  | // Step into CXXDefaultInitExprs so we can diagnose cases where a | 
|  | // constructor inherits one as an implicit mem-initializer. | 
|  | if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { | 
|  | Path.push_back( | 
|  | {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); | 
|  | Init = DIE->getExpr(); | 
|  | } | 
|  |  | 
|  | if (auto *FE = dyn_cast<FullExpr>(Init)) | 
|  | Init = FE->getSubExpr(); | 
|  |  | 
|  | // Dig out the expression which constructs the extended temporary. | 
|  | Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); | 
|  |  | 
|  | if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init)) | 
|  | Init = BTE->getSubExpr(); | 
|  |  | 
|  | Init = Init->IgnoreParens(); | 
|  |  | 
|  | // Step over value-preserving rvalue casts. | 
|  | if (auto *CE = dyn_cast<CastExpr>(Init)) { | 
|  | switch (CE->getCastKind()) { | 
|  | case CK_LValueToRValue: | 
|  | // If we can match the lvalue to a const object, we can look at its | 
|  | // initializer. | 
|  | Path.push_back({IndirectLocalPathEntry::LValToRVal, CE}); | 
|  | return visitLocalsRetainedByReferenceBinding( | 
|  | Path, Init, RK_ReferenceBinding, | 
|  | [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool { | 
|  | if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { | 
|  | auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); | 
|  | if (VD && VD->getType().isConstQualified() && VD->getInit() && | 
|  | !isVarOnPath(Path, VD)) { | 
|  | Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); | 
|  | visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, | 
|  | true); | 
|  | } | 
|  | } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { | 
|  | if (MTE->getType().isConstQualified()) | 
|  | visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), | 
|  | Visit, true); | 
|  | } | 
|  | return false; | 
|  | }); | 
|  |  | 
|  | // We assume that objects can be retained by pointers cast to integers, | 
|  | // but not if the integer is cast to floating-point type or to _Complex. | 
|  | // We assume that casts to 'bool' do not preserve enough information to | 
|  | // retain a local object. | 
|  | case CK_NoOp: | 
|  | case CK_BitCast: | 
|  | case CK_BaseToDerived: | 
|  | case CK_DerivedToBase: | 
|  | case CK_UncheckedDerivedToBase: | 
|  | case CK_Dynamic: | 
|  | case CK_ToUnion: | 
|  | case CK_UserDefinedConversion: | 
|  | case CK_ConstructorConversion: | 
|  | case CK_IntegralToPointer: | 
|  | case CK_PointerToIntegral: | 
|  | case CK_VectorSplat: | 
|  | case CK_IntegralCast: | 
|  | case CK_CPointerToObjCPointerCast: | 
|  | case CK_BlockPointerToObjCPointerCast: | 
|  | case CK_AnyPointerToBlockPointerCast: | 
|  | case CK_AddressSpaceConversion: | 
|  | break; | 
|  |  | 
|  | case CK_ArrayToPointerDecay: | 
|  | // Model array-to-pointer decay as taking the address of the array | 
|  | // lvalue. | 
|  | Path.push_back({IndirectLocalPathEntry::AddressOf, CE}); | 
|  | return visitLocalsRetainedByReferenceBinding( | 
|  | Path, CE->getSubExpr(), RK_ReferenceBinding, Visit); | 
|  |  | 
|  | default: | 
|  | return; | 
|  | } | 
|  |  | 
|  | Init = CE->getSubExpr(); | 
|  | } | 
|  | } while (Old != Init); | 
|  |  | 
|  | // C++17 [dcl.init.list]p6: | 
|  | //   initializing an initializer_list object from the array extends the | 
|  | //   lifetime of the array exactly like binding a reference to a temporary. | 
|  | if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init)) | 
|  | return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(), | 
|  | RK_StdInitializerList, Visit); | 
|  |  | 
|  | if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { | 
|  | // We already visited the elements of this initializer list while | 
|  | // performing the initialization. Don't visit them again unless we've | 
|  | // changed the lifetime of the initialized entity. | 
|  | if (!RevisitSubinits) | 
|  | return; | 
|  |  | 
|  | if (ILE->isTransparent()) | 
|  | return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit, | 
|  | RevisitSubinits); | 
|  |  | 
|  | if (ILE->getType()->isArrayType()) { | 
|  | for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I) | 
|  | visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit, | 
|  | RevisitSubinits); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (CXXRecordDecl *RD = ILE->getType()->getAsCXXRecordDecl()) { | 
|  | assert(RD->isAggregate() && "aggregate init on non-aggregate"); | 
|  |  | 
|  | // If we lifetime-extend a braced initializer which is initializing an | 
|  | // aggregate, and that aggregate contains reference members which are | 
|  | // bound to temporaries, those temporaries are also lifetime-extended. | 
|  | if (RD->isUnion() && ILE->getInitializedFieldInUnion() && | 
|  | ILE->getInitializedFieldInUnion()->getType()->isReferenceType()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0), | 
|  | RK_ReferenceBinding, Visit); | 
|  | else { | 
|  | unsigned Index = 0; | 
|  | for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index) | 
|  | visitLocalsRetainedByInitializer(Path, ILE->getInit(Index), Visit, | 
|  | RevisitSubinits); | 
|  | for (const auto *I : RD->fields()) { | 
|  | if (Index >= ILE->getNumInits()) | 
|  | break; | 
|  | if (I->isUnnamedBitField()) | 
|  | continue; | 
|  | Expr *SubInit = ILE->getInit(Index); | 
|  | if (I->getType()->isReferenceType()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, SubInit, | 
|  | RK_ReferenceBinding, Visit); | 
|  | else | 
|  | // This might be either aggregate-initialization of a member or | 
|  | // initialization of a std::initializer_list object. Regardless, | 
|  | // we should recursively lifetime-extend that initializer. | 
|  | visitLocalsRetainedByInitializer(Path, SubInit, Visit, | 
|  | RevisitSubinits); | 
|  | ++Index; | 
|  | } | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The lifetime of an init-capture is that of the closure object constructed | 
|  | // by a lambda-expression. | 
|  | if (auto *LE = dyn_cast<LambdaExpr>(Init)) { | 
|  | LambdaExpr::capture_iterator CapI = LE->capture_begin(); | 
|  | for (Expr *E : LE->capture_inits()) { | 
|  | assert(CapI != LE->capture_end()); | 
|  | const LambdaCapture &Cap = *CapI++; | 
|  | if (!E) | 
|  | continue; | 
|  | if (Cap.capturesVariable()) | 
|  | Path.push_back({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap}); | 
|  | if (E->isGLValue()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, E, RK_ReferenceBinding, | 
|  | Visit); | 
|  | else | 
|  | visitLocalsRetainedByInitializer(Path, E, Visit, true); | 
|  | if (Cap.capturesVariable()) | 
|  | Path.pop_back(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Assume that a copy or move from a temporary references the same objects | 
|  | // that the temporary does. | 
|  | if (auto *CCE = dyn_cast<CXXConstructExpr>(Init)) { | 
|  | if (CCE->getConstructor()->isCopyOrMoveConstructor()) { | 
|  | if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(CCE->getArg(0))) { | 
|  | Expr *Arg = MTE->getSubExpr(); | 
|  | Path.push_back({IndirectLocalPathEntry::TemporaryCopy, Arg, | 
|  | CCE->getConstructor()}); | 
|  | visitLocalsRetainedByInitializer(Path, Arg, Visit, true); | 
|  | Path.pop_back(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) | 
|  | return visitFunctionCallArguments(Path, Init, Visit); | 
|  |  | 
|  | if (auto *CPE = dyn_cast<CXXParenListInitExpr>(Init)) { | 
|  | RevertToOldSizeRAII RAII(Path); | 
|  | Path.push_back({IndirectLocalPathEntry::ParenAggInit, CPE}); | 
|  | for (auto *I : CPE->getInitExprs()) { | 
|  | if (I->isGLValue()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, I, RK_ReferenceBinding, | 
|  | Visit); | 
|  | else | 
|  | visitLocalsRetainedByInitializer(Path, I, Visit, true); | 
|  | } | 
|  | } | 
|  | switch (Init->getStmtClass()) { | 
|  | case Stmt::UnaryOperatorClass: { | 
|  | auto *UO = cast<UnaryOperator>(Init); | 
|  | // If the initializer is the address of a local, we could have a lifetime | 
|  | // problem. | 
|  | if (UO->getOpcode() == UO_AddrOf) { | 
|  | // If this is &rvalue, then it's ill-formed and we have already diagnosed | 
|  | // it. Don't produce a redundant warning about the lifetime of the | 
|  | // temporary. | 
|  | if (isa<MaterializeTemporaryExpr>(UO->getSubExpr())) | 
|  | return; | 
|  |  | 
|  | Path.push_back({IndirectLocalPathEntry::AddressOf, UO}); | 
|  | visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(), | 
|  | RK_ReferenceBinding, Visit); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::BinaryOperatorClass: { | 
|  | // Handle pointer arithmetic. | 
|  | auto *BO = cast<BinaryOperator>(Init); | 
|  | BinaryOperatorKind BOK = BO->getOpcode(); | 
|  | if (!BO->getType()->isPointerType() || (BOK != BO_Add && BOK != BO_Sub)) | 
|  | break; | 
|  |  | 
|  | if (BO->getLHS()->getType()->isPointerType()) | 
|  | visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true); | 
|  | else if (BO->getRHS()->getType()->isPointerType()) | 
|  | visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::ConditionalOperatorClass: | 
|  | case Stmt::BinaryConditionalOperatorClass: { | 
|  | auto *C = cast<AbstractConditionalOperator>(Init); | 
|  | // In C++, we can have a throw-expression operand, which has 'void' type | 
|  | // and isn't interesting from a lifetime perspective. | 
|  | if (!C->getTrueExpr()->getType()->isVoidType()) | 
|  | visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true); | 
|  | if (!C->getFalseExpr()->getType()->isVoidType()) | 
|  | visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case Stmt::BlockExprClass: | 
|  | if (cast<BlockExpr>(Init)->getBlockDecl()->hasCaptures()) { | 
|  | // This is a local block, whose lifetime is that of the function. | 
|  | Visit(Path, Local(cast<BlockExpr>(Init)), RK_ReferenceBinding); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case Stmt::AddrLabelExprClass: | 
|  | // We want to warn if the address of a label would escape the function. | 
|  | Visit(Path, Local(cast<AddrLabelExpr>(Init)), RK_ReferenceBinding); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Whether a path to an object supports lifetime extension. | 
|  | enum PathLifetimeKind { | 
|  | /// Lifetime-extend along this path. | 
|  | Extend, | 
|  | /// Do not lifetime extend along this path. | 
|  | NoExtend | 
|  | }; | 
|  |  | 
|  | /// Determine whether this is an indirect path to a temporary that we are | 
|  | /// supposed to lifetime-extend along. | 
|  | static PathLifetimeKind | 
|  | shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { | 
|  | for (auto Elem : Path) { | 
|  | if (Elem.Kind == IndirectLocalPathEntry::MemberExpr || | 
|  | Elem.Kind == IndirectLocalPathEntry::LambdaCaptureInit) | 
|  | continue; | 
|  | return Elem.Kind == IndirectLocalPathEntry::DefaultInit | 
|  | ? PathLifetimeKind::Extend | 
|  | : PathLifetimeKind::NoExtend; | 
|  | } | 
|  | return PathLifetimeKind::Extend; | 
|  | } | 
|  |  | 
|  | /// Find the range for the first interesting entry in the path at or after I. | 
|  | static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, | 
|  | Expr *E) { | 
|  | for (unsigned N = Path.size(); I != N; ++I) { | 
|  | switch (Path[I].Kind) { | 
|  | case IndirectLocalPathEntry::AddressOf: | 
|  | case IndirectLocalPathEntry::LValToRVal: | 
|  | case IndirectLocalPathEntry::LifetimeBoundCall: | 
|  | case IndirectLocalPathEntry::TemporaryCopy: | 
|  | case IndirectLocalPathEntry::GslReferenceInit: | 
|  | case IndirectLocalPathEntry::GslPointerInit: | 
|  | case IndirectLocalPathEntry::GslPointerAssignment: | 
|  | case IndirectLocalPathEntry::ParenAggInit: | 
|  | case IndirectLocalPathEntry::MemberExpr: | 
|  | // These exist primarily to mark the path as not permitting or | 
|  | // supporting lifetime extension. | 
|  | break; | 
|  |  | 
|  | case IndirectLocalPathEntry::VarInit: | 
|  | if (cast<VarDecl>(Path[I].D)->isImplicit()) | 
|  | return SourceRange(); | 
|  | [[fallthrough]]; | 
|  | case IndirectLocalPathEntry::DefaultInit: | 
|  | return Path[I].E->getSourceRange(); | 
|  |  | 
|  | case IndirectLocalPathEntry::LambdaCaptureInit: | 
|  | if (!Path[I].Capture->capturesVariable()) | 
|  | continue; | 
|  | return Path[I].E->getSourceRange(); | 
|  |  | 
|  | case IndirectLocalPathEntry::DefaultArg: | 
|  | return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation(); | 
|  | } | 
|  | } | 
|  | return E->getSourceRange(); | 
|  | } | 
|  |  | 
|  | static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) { | 
|  | for (const auto &It : llvm::reverse(Path)) { | 
|  | switch (It.Kind) { | 
|  | case IndirectLocalPathEntry::VarInit: | 
|  | case IndirectLocalPathEntry::AddressOf: | 
|  | case IndirectLocalPathEntry::LifetimeBoundCall: | 
|  | case IndirectLocalPathEntry::MemberExpr: | 
|  | continue; | 
|  | case IndirectLocalPathEntry::GslPointerInit: | 
|  | case IndirectLocalPathEntry::GslReferenceInit: | 
|  | case IndirectLocalPathEntry::GslPointerAssignment: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  | // Result of analyzing the Path for GSLPointer. | 
|  | enum AnalysisResult { | 
|  | // Path does not correspond to a GSLPointer. | 
|  | NotGSLPointer, | 
|  |  | 
|  | // A relevant case was identified. | 
|  | Report, | 
|  | // Stop the entire traversal. | 
|  | Abandon, | 
|  | // Skip this step and continue traversing inner AST nodes. | 
|  | Skip, | 
|  | }; | 
|  | // Analyze cases where a GSLPointer is initialized or assigned from a | 
|  | // temporary owner object. | 
|  | static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path, | 
|  | Local L, LifetimeKind LK) { | 
|  | if (!pathOnlyHandlesGslPointer(Path)) | 
|  | return NotGSLPointer; | 
|  |  | 
|  | // At this point, Path represents a series of operations involving a | 
|  | // GSLPointer, either in the process of initialization or assignment. | 
|  |  | 
|  | // Process  temporary base objects for MemberExpr cases, e.g. Temp().field. | 
|  | for (const auto &E : Path) { | 
|  | if (E.Kind == IndirectLocalPathEntry::MemberExpr) { | 
|  | // Avoid interfering  with the local base object. | 
|  | if (pathContainsInit(Path)) | 
|  | return Abandon; | 
|  |  | 
|  | // We are not interested in the temporary base objects of gsl Pointers: | 
|  | //   auto p1 = Temp().ptr; // Here p1 might not dangle. | 
|  | // However, we want to diagnose for gsl owner fields: | 
|  | //   auto p2 = Temp().owner; // Here p2 is dangling. | 
|  | if (const auto *FD = llvm::dyn_cast_or_null<FieldDecl>(E.D); | 
|  | FD && !FD->getType()->isReferenceType() && | 
|  | isRecordWithAttr<OwnerAttr>(FD->getType()) && | 
|  | LK != LK_MemInitializer) { | 
|  | return Report; | 
|  | } | 
|  | return Abandon; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Note: A LifetimeBoundCall can appear interleaved in this sequence. | 
|  | // For example: | 
|  | //    const std::string& Ref(const std::string& a [[clang::lifetimebound]]); | 
|  | //    string_view abc = Ref(std::string()); | 
|  | // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the | 
|  | // temporary "std::string()" object. We need to check the return type of the | 
|  | // function with the lifetimebound attribute. | 
|  | if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) { | 
|  | // The lifetimebound applies to the implicit object parameter of a method. | 
|  | const FunctionDecl *FD = | 
|  | llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D); | 
|  | // The lifetimebound applies to a function parameter. | 
|  | if (const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D)) | 
|  | FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext()); | 
|  |  | 
|  | if (isa_and_present<CXXConstructorDecl>(FD)) { | 
|  | // Constructor case: the parameter is annotated with lifetimebound | 
|  | //   e.g., GSLPointer(const S& s [[clang::lifetimebound]]) | 
|  | // We still respect this case even the type S is not an owner. | 
|  | return Report; | 
|  | } | 
|  | // Check the return type, e.g. | 
|  | //   const GSLOwner& func(const Foo& foo [[clang::lifetimebound]]) | 
|  | //   GSLOwner* func(cosnt Foo& foo [[clang::lifetimebound]]) | 
|  | //   GSLPointer func(const Foo& foo [[clang::lifetimebound]]) | 
|  | if (FD && | 
|  | ((FD->getReturnType()->isPointerOrReferenceType() && | 
|  | isRecordWithAttr<OwnerAttr>(FD->getReturnType()->getPointeeType())) || | 
|  | isGLSPointerType(FD->getReturnType()))) | 
|  | return Report; | 
|  |  | 
|  | return Abandon; | 
|  | } | 
|  |  | 
|  | if (isa<DeclRefExpr>(L)) { | 
|  | // We do not want to follow the references when returning a pointer | 
|  | // originating from a local owner to avoid the following false positive: | 
|  | //   int &p = *localUniquePtr; | 
|  | //   someContainer.add(std::move(localUniquePtr)); | 
|  | //   return p; | 
|  | if (!pathContainsInit(Path) && isRecordWithAttr<OwnerAttr>(L->getType())) | 
|  | return Report; | 
|  | return Abandon; | 
|  | } | 
|  |  | 
|  | // The GSLPointer is from a temporary object. | 
|  | auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L); | 
|  |  | 
|  | bool IsGslPtrValueFromGslTempOwner = | 
|  | MTE && !MTE->getExtendingDecl() && | 
|  | isRecordWithAttr<OwnerAttr>(MTE->getType()); | 
|  | // Skipping a chain of initializing gsl::Pointer annotated objects. | 
|  | // We are looking only for the final source to find out if it was | 
|  | // a local or temporary owner or the address of a local | 
|  | // variable/param. | 
|  | if (!IsGslPtrValueFromGslTempOwner) | 
|  | return Skip; | 
|  | return Report; | 
|  | } | 
|  |  | 
|  | static bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) { | 
|  | CMD = getDeclWithMergedLifetimeBoundAttrs(CMD); | 
|  | return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 && | 
|  | CMD->getParamDecl(0)->hasAttr<LifetimeBoundAttr>(); | 
|  | } | 
|  |  | 
|  | static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef, | 
|  | const AssignedEntity &Entity) { | 
|  | bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored( | 
|  | diag::warn_dangling_lifetime_pointer_assignment, SourceLocation()); | 
|  | return (EnableGSLAssignmentWarnings && | 
|  | (isRecordWithAttr<PointerAttr>(Entity.LHS->getType()) || | 
|  | isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator))); | 
|  | } | 
|  |  | 
|  | static void | 
|  | checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, | 
|  | const InitializedEntity *ExtendingEntity, LifetimeKind LK, | 
|  | const AssignedEntity *AEntity, | 
|  | const CapturingEntity *CapEntity, Expr *Init) { | 
|  | assert(!AEntity || LK == LK_Assignment); | 
|  | assert(!CapEntity || LK == LK_LifetimeCapture); | 
|  | assert(!InitEntity || (LK != LK_Assignment && LK != LK_LifetimeCapture)); | 
|  | // If this entity doesn't have an interesting lifetime, don't bother looking | 
|  | // for temporaries within its initializer. | 
|  | if (LK == LK_FullExpression) | 
|  | return; | 
|  |  | 
|  | // FIXME: consider moving the TemporaryVisitor and visitLocalsRetained* | 
|  | // functions to a dedicated class. | 
|  | auto TemporaryVisitor = [&](const IndirectLocalPath &Path, Local L, | 
|  | ReferenceKind RK) -> bool { | 
|  | SourceRange DiagRange = nextPathEntryRange(Path, 0, L); | 
|  | SourceLocation DiagLoc = DiagRange.getBegin(); | 
|  |  | 
|  | auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L); | 
|  |  | 
|  | bool IsGslPtrValueFromGslTempOwner = true; | 
|  | switch (analyzePathForGSLPointer(Path, L, LK)) { | 
|  | case Abandon: | 
|  | return false; | 
|  | case Skip: | 
|  | return true; | 
|  | case NotGSLPointer: | 
|  | IsGslPtrValueFromGslTempOwner = false; | 
|  | LLVM_FALLTHROUGH; | 
|  | case Report: | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (LK) { | 
|  | case LK_FullExpression: | 
|  | llvm_unreachable("already handled this"); | 
|  |  | 
|  | case LK_Extended: { | 
|  | if (!MTE) { | 
|  | // The initialized entity has lifetime beyond the full-expression, | 
|  | // and the local entity does too, so don't warn. | 
|  | // | 
|  | // FIXME: We should consider warning if a static / thread storage | 
|  | // duration variable retains an automatic storage duration local. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) { | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer) | 
|  | << DiagRange; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | switch (shouldLifetimeExtendThroughPath(Path)) { | 
|  | case PathLifetimeKind::Extend: | 
|  | // Update the storage duration of the materialized temporary. | 
|  | // FIXME: Rebuild the expression instead of mutating it. | 
|  | MTE->setExtendingDecl(ExtendingEntity->getDecl(), | 
|  | ExtendingEntity->allocateManglingNumber()); | 
|  | // Also visit the temporaries lifetime-extended by this initializer. | 
|  | return true; | 
|  |  | 
|  | case PathLifetimeKind::NoExtend: | 
|  | // If the path goes through the initialization of a variable or field, | 
|  | // it can't possibly reach a temporary created in this full-expression. | 
|  | // We will have already diagnosed any problems with the initializer. | 
|  | if (pathContainsInit(Path)) | 
|  | return false; | 
|  |  | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_variable) | 
|  | << RK << !InitEntity->getParent() | 
|  | << ExtendingEntity->getDecl()->isImplicit() | 
|  | << ExtendingEntity->getDecl() << Init->isGLValue() << DiagRange; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case LK_LifetimeCapture: { | 
|  | // The captured entity has lifetime beyond the full-expression, | 
|  | // and the capturing entity does too, so don't warn. | 
|  | if (!MTE) | 
|  | return false; | 
|  | if (CapEntity->Entity) | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured) | 
|  | << CapEntity->Entity << DiagRange; | 
|  | else | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured_by_unknown) | 
|  | << DiagRange; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | case LK_Assignment: { | 
|  | if (!MTE || pathContainsInit(Path)) | 
|  | return false; | 
|  | if (IsGslPtrValueFromGslTempOwner) | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer_assignment) | 
|  | << AEntity->LHS << DiagRange; | 
|  | else | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_pointer_assignment) | 
|  | << AEntity->LHS->getType()->isPointerType() << AEntity->LHS | 
|  | << DiagRange; | 
|  | return false; | 
|  | } | 
|  | case LK_MemInitializer: { | 
|  | if (MTE) { | 
|  | // Under C++ DR1696, if a mem-initializer (or a default member | 
|  | // initializer used by the absence of one) would lifetime-extend a | 
|  | // temporary, the program is ill-formed. | 
|  | if (auto *ExtendingDecl = | 
|  | ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { | 
|  | if (IsGslPtrValueFromGslTempOwner) { | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer_member) | 
|  | << ExtendingDecl << DiagRange; | 
|  | SemaRef.Diag(ExtendingDecl->getLocation(), | 
|  | diag::note_ref_or_ptr_member_declared_here) | 
|  | << true; | 
|  | return false; | 
|  | } | 
|  | bool IsSubobjectMember = ExtendingEntity != InitEntity; | 
|  | SemaRef.Diag(DiagLoc, shouldLifetimeExtendThroughPath(Path) != | 
|  | PathLifetimeKind::NoExtend | 
|  | ? diag::err_dangling_member | 
|  | : diag::warn_dangling_member) | 
|  | << ExtendingDecl << IsSubobjectMember << RK << DiagRange; | 
|  | // Don't bother adding a note pointing to the field if we're inside | 
|  | // its default member initializer; our primary diagnostic points to | 
|  | // the same place in that case. | 
|  | if (Path.empty() || | 
|  | Path.back().Kind != IndirectLocalPathEntry::DefaultInit) { | 
|  | SemaRef.Diag(ExtendingDecl->getLocation(), | 
|  | diag::note_lifetime_extending_member_declared_here) | 
|  | << RK << IsSubobjectMember; | 
|  | } | 
|  | } else { | 
|  | // We have a mem-initializer but no particular field within it; this | 
|  | // is either a base class or a delegating initializer directly | 
|  | // initializing the base-class from something that doesn't live long | 
|  | // enough. | 
|  | // | 
|  | // FIXME: Warn on this. | 
|  | return false; | 
|  | } | 
|  | } else { | 
|  | // Paths via a default initializer can only occur during error recovery | 
|  | // (there's no other way that a default initializer can refer to a | 
|  | // local). Don't produce a bogus warning on those cases. | 
|  | if (pathContainsInit(Path)) | 
|  | return false; | 
|  |  | 
|  | auto *DRE = dyn_cast<DeclRefExpr>(L); | 
|  | // Suppress false positives for code like the one below: | 
|  | //   Ctor(unique_ptr<T> up) : pointer(up.get()), owner(move(up)) {} | 
|  | // FIXME: move this logic to analyzePathForGSLPointer. | 
|  | if (DRE && isRecordWithAttr<OwnerAttr>(DRE->getType())) | 
|  | return false; | 
|  |  | 
|  | auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr; | 
|  | if (!VD) { | 
|  | // A member was initialized to a local block. | 
|  | // FIXME: Warn on this. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (auto *Member = | 
|  | ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { | 
|  | bool IsPointer = !Member->getType()->isReferenceType(); | 
|  | SemaRef.Diag(DiagLoc, | 
|  | IsPointer ? diag::warn_init_ptr_member_to_parameter_addr | 
|  | : diag::warn_bind_ref_member_to_parameter) | 
|  | << Member << VD << isa<ParmVarDecl>(VD) << DiagRange; | 
|  | SemaRef.Diag(Member->getLocation(), | 
|  | diag::note_ref_or_ptr_member_declared_here) | 
|  | << (unsigned)IsPointer; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case LK_New: | 
|  | if (isa<MaterializeTemporaryExpr>(L)) { | 
|  | if (IsGslPtrValueFromGslTempOwner) | 
|  | SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer) | 
|  | << DiagRange; | 
|  | else | 
|  | SemaRef.Diag(DiagLoc, RK == RK_ReferenceBinding | 
|  | ? diag::warn_new_dangling_reference | 
|  | : diag::warn_new_dangling_initializer_list) | 
|  | << !InitEntity->getParent() << DiagRange; | 
|  | } else { | 
|  | // We can't determine if the allocation outlives the local declaration. | 
|  | return false; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case LK_Return: | 
|  | case LK_MustTail: | 
|  | case LK_StmtExprResult: | 
|  | if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { | 
|  | // We can't determine if the local variable outlives the statement | 
|  | // expression. | 
|  | if (LK == LK_StmtExprResult) | 
|  | return false; | 
|  | SemaRef.Diag(DiagLoc, diag::warn_ret_stack_addr_ref) | 
|  | << InitEntity->getType()->isReferenceType() << DRE->getDecl() | 
|  | << isa<ParmVarDecl>(DRE->getDecl()) << (LK == LK_MustTail) | 
|  | << DiagRange; | 
|  | } else if (isa<BlockExpr>(L)) { | 
|  | SemaRef.Diag(DiagLoc, diag::err_ret_local_block) << DiagRange; | 
|  | } else if (isa<AddrLabelExpr>(L)) { | 
|  | // Don't warn when returning a label from a statement expression. | 
|  | // Leaving the scope doesn't end its lifetime. | 
|  | if (LK == LK_StmtExprResult) | 
|  | return false; | 
|  | SemaRef.Diag(DiagLoc, diag::warn_ret_addr_label) << DiagRange; | 
|  | } else if (auto *CLE = dyn_cast<CompoundLiteralExpr>(L)) { | 
|  | SemaRef.Diag(DiagLoc, diag::warn_ret_stack_addr_ref) | 
|  | << InitEntity->getType()->isReferenceType() << CLE->getInitializer() | 
|  | << 2 << (LK == LK_MustTail) << DiagRange; | 
|  | } else { | 
|  | // P2748R5: Disallow Binding a Returned Glvalue to a Temporary. | 
|  | // [stmt.return]/p6: In a function whose return type is a reference, | 
|  | // other than an invented function for std::is_convertible ([meta.rel]), | 
|  | // a return statement that binds the returned reference to a temporary | 
|  | // expression ([class.temporary]) is ill-formed. | 
|  | if (SemaRef.getLangOpts().CPlusPlus26 && | 
|  | InitEntity->getType()->isReferenceType()) | 
|  | SemaRef.Diag(DiagLoc, diag::err_ret_local_temp_ref) | 
|  | << InitEntity->getType()->isReferenceType() << DiagRange; | 
|  | else if (LK == LK_MustTail) | 
|  | SemaRef.Diag(DiagLoc, diag::warn_musttail_local_temp_addr_ref) | 
|  | << InitEntity->getType()->isReferenceType() << DiagRange; | 
|  | else | 
|  | SemaRef.Diag(DiagLoc, diag::warn_ret_local_temp_addr_ref) | 
|  | << InitEntity->getType()->isReferenceType() << DiagRange; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | for (unsigned I = 0; I != Path.size(); ++I) { | 
|  | auto Elem = Path[I]; | 
|  |  | 
|  | switch (Elem.Kind) { | 
|  | case IndirectLocalPathEntry::AddressOf: | 
|  | case IndirectLocalPathEntry::LValToRVal: | 
|  | case IndirectLocalPathEntry::ParenAggInit: | 
|  | // These exist primarily to mark the path as not permitting or | 
|  | // supporting lifetime extension. | 
|  | break; | 
|  |  | 
|  | case IndirectLocalPathEntry::LifetimeBoundCall: | 
|  | case IndirectLocalPathEntry::TemporaryCopy: | 
|  | case IndirectLocalPathEntry::MemberExpr: | 
|  | case IndirectLocalPathEntry::GslPointerInit: | 
|  | case IndirectLocalPathEntry::GslReferenceInit: | 
|  | case IndirectLocalPathEntry::GslPointerAssignment: | 
|  | // FIXME: Consider adding a note for these. | 
|  | break; | 
|  |  | 
|  | case IndirectLocalPathEntry::DefaultInit: { | 
|  | auto *FD = cast<FieldDecl>(Elem.D); | 
|  | SemaRef.Diag(FD->getLocation(), | 
|  | diag::note_init_with_default_member_initializer) | 
|  | << FD << nextPathEntryRange(Path, I + 1, L); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case IndirectLocalPathEntry::VarInit: { | 
|  | const VarDecl *VD = cast<VarDecl>(Elem.D); | 
|  | SemaRef.Diag(VD->getLocation(), diag::note_local_var_initializer) | 
|  | << VD->getType()->isReferenceType() << VD->isImplicit() | 
|  | << VD->getDeclName() << nextPathEntryRange(Path, I + 1, L); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case IndirectLocalPathEntry::LambdaCaptureInit: { | 
|  | if (!Elem.Capture->capturesVariable()) | 
|  | break; | 
|  | // FIXME: We can't easily tell apart an init-capture from a nested | 
|  | // capture of an init-capture. | 
|  | const ValueDecl *VD = Elem.Capture->getCapturedVar(); | 
|  | SemaRef.Diag(Elem.Capture->getLocation(), | 
|  | diag::note_lambda_capture_initializer) | 
|  | << VD << VD->isInitCapture() << Elem.Capture->isExplicit() | 
|  | << (Elem.Capture->getCaptureKind() == LCK_ByRef) << VD | 
|  | << nextPathEntryRange(Path, I + 1, L); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case IndirectLocalPathEntry::DefaultArg: { | 
|  | const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E); | 
|  | const ParmVarDecl *Param = DAE->getParam(); | 
|  | SemaRef.Diag(Param->getDefaultArgRange().getBegin(), | 
|  | diag::note_init_with_default_argument) | 
|  | << Param << nextPathEntryRange(Path, I + 1, L); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // We didn't lifetime-extend, so don't go any further; we don't need more | 
|  | // warnings or errors on inner temporaries within this one's initializer. | 
|  | return false; | 
|  | }; | 
|  |  | 
|  | llvm::SmallVector<IndirectLocalPathEntry, 8> Path; | 
|  | switch (LK) { | 
|  | case LK_Assignment: { | 
|  | if (shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity)) | 
|  | Path.push_back( | 
|  | {isAssignmentOperatorLifetimeBound(AEntity->AssignmentOperator) | 
|  | ? IndirectLocalPathEntry::LifetimeBoundCall | 
|  | : IndirectLocalPathEntry::GslPointerAssignment, | 
|  | Init}); | 
|  | break; | 
|  | } | 
|  | case LK_LifetimeCapture: { | 
|  | if (isPointerLikeType(Init->getType())) | 
|  | Path.push_back({IndirectLocalPathEntry::GslPointerInit, Init}); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (Init->isGLValue()) | 
|  | visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding, | 
|  | TemporaryVisitor); | 
|  | else | 
|  | visitLocalsRetainedByInitializer( | 
|  | Path, Init, TemporaryVisitor, | 
|  | // Don't revisit the sub inits for the intialization case. | 
|  | /*RevisitSubinits=*/!InitEntity); | 
|  | } | 
|  |  | 
|  | void checkInitLifetime(Sema &SemaRef, const InitializedEntity &Entity, | 
|  | Expr *Init) { | 
|  | auto LTResult = getEntityLifetime(&Entity); | 
|  | LifetimeKind LK = LTResult.getInt(); | 
|  | const InitializedEntity *ExtendingEntity = LTResult.getPointer(); | 
|  | checkExprLifetimeImpl(SemaRef, &Entity, ExtendingEntity, LK, | 
|  | /*AEntity=*/nullptr, /*CapEntity=*/nullptr, Init); | 
|  | } | 
|  |  | 
|  | void checkExprLifetimeMustTailArg(Sema &SemaRef, | 
|  | const InitializedEntity &Entity, Expr *Init) { | 
|  | checkExprLifetimeImpl(SemaRef, &Entity, nullptr, LK_MustTail, | 
|  | /*AEntity=*/nullptr, /*CapEntity=*/nullptr, Init); | 
|  | } | 
|  |  | 
|  | void checkAssignmentLifetime(Sema &SemaRef, const AssignedEntity &Entity, | 
|  | Expr *Init) { | 
|  | bool EnableDanglingPointerAssignment = !SemaRef.getDiagnostics().isIgnored( | 
|  | diag::warn_dangling_pointer_assignment, SourceLocation()); | 
|  | bool RunAnalysis = (EnableDanglingPointerAssignment && | 
|  | Entity.LHS->getType()->isPointerType()) || | 
|  | shouldRunGSLAssignmentAnalysis(SemaRef, Entity); | 
|  |  | 
|  | if (!RunAnalysis) | 
|  | return; | 
|  |  | 
|  | checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr, | 
|  | /*ExtendingEntity=*/nullptr, LK_Assignment, &Entity, | 
|  | /*CapEntity=*/nullptr, Init); | 
|  | } | 
|  |  | 
|  | void checkCaptureByLifetime(Sema &SemaRef, const CapturingEntity &Entity, | 
|  | Expr *Init) { | 
|  | if (SemaRef.getDiagnostics().isIgnored(diag::warn_dangling_reference_captured, | 
|  | SourceLocation()) && | 
|  | SemaRef.getDiagnostics().isIgnored( | 
|  | diag::warn_dangling_reference_captured_by_unknown, SourceLocation())) | 
|  | return; | 
|  | return checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr, | 
|  | /*ExtendingEntity=*/nullptr, LK_LifetimeCapture, | 
|  | /*AEntity=*/nullptr, | 
|  | /*CapEntity=*/&Entity, Init); | 
|  | } | 
|  |  | 
|  | } // namespace clang::sema |