https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/177363
>From a8aa95e4eb7aab8cfb8bd8d947af1508e7907c11 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <[email protected]> Date: Wed, 21 Jan 2026 23:28:49 +0000 Subject: [PATCH] Detect dangling fields --- .../Analysis/Analyses/LifetimeSafety/Facts.h | 52 +- .../Analyses/LifetimeSafety/FactsGenerator.h | 4 +- .../Analyses/LifetimeSafety/LifetimeSafety.h | 6 +- clang/include/clang/Analysis/CFG.h | 5 + clang/include/clang/Basic/DiagnosticGroups.td | 11 +- .../clang/Basic/DiagnosticSemaKinds.td | 6 + clang/lib/Analysis/CFG.cpp | 21 +- .../lib/Analysis/FlowSensitive/AdornedCFG.cpp | 1 + clang/lib/Analysis/LifetimeSafety/Checker.cpp | 17 +- clang/lib/Analysis/LifetimeSafety/Facts.cpp | 13 +- .../LifetimeSafety/FactsGenerator.cpp | 68 +- .../Analysis/LifetimeSafety/LiveOrigins.cpp | 9 +- clang/lib/Analysis/LifetimeSafety/Origins.cpp | 25 +- clang/lib/Sema/AnalysisBasedWarnings.cpp | 632 +++++++++--------- clang/test/Analysis/lifetime-cfg-output.cpp | 28 - clang/test/Analysis/scopes-cfg-output.cpp | 2 - .../Sema/warn-lifetime-analysis-nocfg.cpp | 25 +- .../warn-lifetime-safety-dangling-field.cpp | 151 +++++ .../Sema/warn-lifetime-safety-dataflow.cpp | 2 +- 19 files changed, 672 insertions(+), 406 deletions(-) create mode 100644 clang/test/Sema/warn-lifetime-safety-dangling-field.cpp diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index a66925b7302ca..61a4eea05bcb5 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -136,19 +136,63 @@ class OriginFlowFact : public Fact { const OriginManager &OM) const override; }; +/// Represents that an origin escapes the current scope through various means. +/// This is the base class for different escape scenarios. class OriginEscapesFact : public Fact { OriginID OID; - const Expr *EscapeExpr; public: + /// The way an origin can escape the current scope. + enum class EscapeKind : uint8_t { + Return, /// Escapes via return statement. + Field, /// Escapes via assignment to a field. + // FIXME: Add support for escape to global (dangling global ptr). + } EscKind; + static bool classof(const Fact *F) { return F->getKind() == Kind::OriginEscapes; } - OriginEscapesFact(OriginID OID, const Expr *EscapeExpr) - : Fact(Kind::OriginEscapes), OID(OID), EscapeExpr(EscapeExpr) {} + OriginEscapesFact(OriginID OID, EscapeKind EscKind) + : Fact(Kind::OriginEscapes), OID(OID), EscKind(EscKind) {} OriginID getEscapedOriginID() const { return OID; } - const Expr *getEscapeExpr() const { return EscapeExpr; }; + EscapeKind getEscapeKind() const { return EscKind; } +}; + +/// Represents that an origin escapes via a return statement. +class ReturnEscapeFact : public OriginEscapesFact { + const Expr *ReturnExpr; + +public: + ReturnEscapeFact(OriginID OID, const Expr *ReturnExpr) + : OriginEscapesFact(OID, EscapeKind::Return), ReturnExpr(ReturnExpr) {} + + static bool classof(const Fact *F) { + return F->getKind() == Kind::OriginEscapes && + static_cast<const OriginEscapesFact *>(F)->getEscapeKind() == + EscapeKind::Return; + } + const Expr *getReturnExpr() const { return ReturnExpr; }; + void dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const override; +}; + +/// Represents that an origin escapes via assignment to a field. +/// Example: `this->view = local_var;` where local_var outlives the assignment +/// but not the object containing the field. +class FieldEscapeFact : public OriginEscapesFact { + const FieldDecl *FDecl; + +public: + FieldEscapeFact(OriginID OID, const FieldDecl *FDecl) + : OriginEscapesFact(OID, EscapeKind::Field), FDecl(FDecl) {} + + static bool classof(const Fact *F) { + return F->getKind() == Kind::OriginEscapes && + static_cast<const OriginEscapesFact *>(F)->getEscapeKind() == + EscapeKind::Field; + } + const FieldDecl *getFieldDecl() const { return FDecl; }; void dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const override; }; diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index a47505ee9f159..e4487b0d1dbc7 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -58,10 +58,12 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> { void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr); + void handleCXXCtorInitializer(const CXXCtorInitializer *CII); void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds); - void handleTemporaryDtor(const CFGTemporaryDtor &TemporaryDtor); + void handleExitBlock(); + void handleGSLPointerConstruction(const CXXConstructExpr *CCE); /// Checks if a call-like expression creates a borrow by passing a value to a diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 9c91355355233..8256d5829dcb9 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -52,10 +52,14 @@ class LifetimeSafetyReporter { Confidence Confidence) {} virtual void reportUseAfterReturn(const Expr *IssueExpr, - const Expr *EscapeExpr, + const Expr *ReturnExpr, SourceLocation ExpiryLoc, Confidence Confidence) {} + virtual void reportDanglingField(const Expr *IssueExpr, + const FieldDecl *Field, + SourceLocation ExpiryLoc) {} + // Suggests lifetime bound annotations for function paramters virtual void suggestAnnotation(SuggestionScope Scope, const ParmVarDecl *ParmToAnnotate, diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index a4bafd4927df0..16efb24e58211 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -1235,6 +1235,11 @@ class CFG { bool AddEHEdges = false; bool AddInitializers = false; bool AddImplicitDtors = false; + // Add dtors for function parameters. In principle, function parameters are + // constructed and destructed in the caller context but analyses could still + // choose to include these in the callee's CFG to represent the dtors run on + // function exit. + bool AddParameterDtors = false; bool AddLifetime = false; bool AddLoopExit = false; bool AddTemporaryDtors = false; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 34624dd3eed3a..c4cae4a30ec4f 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -533,14 +533,21 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment, DanglingGsl, ReturnStackAddress]>; -def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive">; +def LifetimeSafetyDanglingField : DiagGroup<"lifetime-safety-dangling-field"> { + code Documentation = [{Warning to detect dangling field references.}]; +} + +def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive", + [LifetimeSafetyDanglingField]>; def LifetimeSafetyStrict : DiagGroup<"lifetime-safety-strict">; + def LifetimeSafety : DiagGroup<"lifetime-safety", [LifetimeSafetyPermissive, LifetimeSafetyStrict]> { code Documentation = [{ - Experimental warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis. + Warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis. }]; } + def LifetimeSafetyCrossTUSuggestions : DiagGroup<"lifetime-safety-cross-tu-suggestions">; def LifetimeSafetyIntraTUSuggestions diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a2be7ab3791b9..8b05b0ffdd04d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10816,9 +10816,15 @@ def warn_lifetime_safety_return_stack_addr_strict InGroup<LifetimeSafetyStrict>, DefaultIgnore; +def warn_lifetime_safety_dangling_field + : Warning<"address of stack memory escapes to a field">, + InGroup<LifetimeSafetyDanglingField>, + DefaultIgnore; + def note_lifetime_safety_used_here : Note<"later used here">; def note_lifetime_safety_destroyed_here : Note<"destroyed here">; def note_lifetime_safety_returned_here : Note<"returned here">; +def note_lifetime_safety_dangling_field_here: Note<"this field dangles">; def warn_lifetime_safety_intra_tu_suggestion : Warning<"parameter in intra-TU function should be marked " diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index a9c7baa00543c..fdca00a0f196f 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -1667,12 +1667,21 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { assert(Succ == &cfg->getExit()); Block = nullptr; // the EXIT block is empty. Create all other blocks lazily. - // Add parameters to the initial scope to handle their dtos and lifetime ends. - LocalScope *paramScope = nullptr; - if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) - for (ParmVarDecl *PD : FD->parameters()) - paramScope = addLocalScopeForVarDecl(PD, paramScope); - + if (BuildOpts.AddParameterDtors) { + // Add parameters to the initial scope to handle their dtos and lifetime + // ends. + LocalScope *paramScope = nullptr; + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) + for (ParmVarDecl *PD : FD->parameters()) { + paramScope = addLocalScopeForVarDecl(PD, paramScope); + } + if (auto *C = dyn_cast<CompoundStmt>(Statement)) + if (C->body_empty() || !isa<ReturnStmt>(*C->body_rbegin())) + // If the body ends with a ReturnStmt, the dtors will be added in + // VisitReturnStmt. + addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), + Statement); + } if (BuildOpts.AddImplicitDtors) if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D)) addImplicitDtorsForDestructor(DD); diff --git a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp index 6c4847c7c23fb..c47d7b5f9c3d6 100644 --- a/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp +++ b/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp @@ -159,6 +159,7 @@ llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl &D, Stmt &S, Options.PruneTriviallyFalseEdges = true; Options.AddImplicitDtors = true; Options.AddTemporaryDtors = true; + Options.AddParameterDtors = true; Options.AddInitializers = true; Options.AddCXXDefaultInitExprInCtors = true; Options.AddLifetime = true; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index f7383126fac38..9ae17168d9b93 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -91,7 +91,8 @@ class LifetimeChecker { const ParmVarDecl *PVD = PL->getParmVarDecl(); if (PVD->hasAttr<LifetimeBoundAttr>()) continue; - AnnotationWarningsMap.try_emplace(PVD, OEF->getEscapeExpr()); + if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) + AnnotationWarningsMap.try_emplace(PVD, ReturnEsc->getReturnExpr()); } } } @@ -154,10 +155,16 @@ class LifetimeChecker { Reporter->reportUseAfterFree(IssueExpr, UF->getUseExpr(), ExpiryLoc, Confidence); else if (const auto *OEF = - CausingFact.dyn_cast<const OriginEscapesFact *>()) - Reporter->reportUseAfterReturn(IssueExpr, OEF->getEscapeExpr(), - ExpiryLoc, Confidence); - else + CausingFact.dyn_cast<const OriginEscapesFact *>()) { + if (const auto *RetEscape = dyn_cast<ReturnEscapeFact>(OEF)) + Reporter->reportUseAfterReturn(IssueExpr, RetEscape->getReturnExpr(), + ExpiryLoc, Confidence); + else if (const auto *FieldEscape = dyn_cast<FieldEscapeFact>(OEF)) + Reporter->reportDanglingField(IssueExpr, FieldEscape->getFieldDecl(), + ExpiryLoc); + else + llvm_unreachable("Unhandled OriginEscapesFact type"); + } else llvm_unreachable("Unhandled CausingFact type"); } } diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp index 2673ce5ba354b..1fc72aa0a4259 100644 --- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp @@ -45,11 +45,18 @@ void OriginFlowFact::dump(llvm::raw_ostream &OS, const LoanManager &, OS << "\n"; } -void OriginEscapesFact::dump(llvm::raw_ostream &OS, const LoanManager &, - const OriginManager &OM) const { +void ReturnEscapeFact::dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const { OS << "OriginEscapes ("; OM.dump(getEscapedOriginID(), OS); - OS << ")\n"; + OS << ", via Return)\n"; +} + +void FieldEscapeFact::dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const { + OS << "OriginEscapes ("; + OM.dump(getEscapedOriginID(), OS); + OS << ", via Field)\n"; } void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &, diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index d5990597e1614..47c919b7d139d 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -10,6 +10,7 @@ #include <string> #include "clang/AST/OperationKinds.h" +#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h" @@ -107,6 +108,9 @@ void FactsGenerator::run() { const CFGElement &Element = Block->Elements[I]; if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>()) Visit(CS->getStmt()); + else if (std::optional<CFGInitializer> Initializer = + Element.getAs<CFGInitializer>()) + handleCXXCtorInitializer(Initializer->getInitializer()); else if (std::optional<CFGLifetimeEnds> LifetimeEnds = Element.getAs<CFGLifetimeEnds>()) handleLifetimeEnds(*LifetimeEnds); @@ -114,6 +118,9 @@ void FactsGenerator::run() { Element.getAs<CFGTemporaryDtor>()) handleTemporaryDtor(*TemporaryDtor); } + if (Block == &Cfg.getExit()) + handleExitBlock(); + CurrentBlockFacts.append(EscapesInCurrentBlock.begin(), EscapesInCurrentBlock.end()); FactMgr.addBlockFacts(Block, CurrentBlockFacts); @@ -180,6 +187,13 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) { } } +void FactsGenerator::handleCXXCtorInitializer(const CXXCtorInitializer *CII) { + // Flows origins from the initializer expression to the field. + // Example: `MyObj(std::string s) : view(s) {}` + if (const FieldDecl *FD = CII->getAnyMember()) + killAndFlowOrigin(*FD, *CII->getInit()); +} + void FactsGenerator::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) { // Specifically for conversion operators, // like `std::string_view p = std::string{};` @@ -316,31 +330,42 @@ void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) { if (const Expr *RetExpr = RS->getRetValue()) { if (OriginList *List = getOriginsList(*RetExpr)) for (OriginList *L = List; L != nullptr; L = L->peelOuterOrigin()) - EscapesInCurrentBlock.push_back(FactMgr.createFact<OriginEscapesFact>( + EscapesInCurrentBlock.push_back(FactMgr.createFact<ReturnEscapeFact>( L->getOuterOriginID(), RetExpr)); } } void FactsGenerator::handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) { - if (const auto *DRE_LHS = - dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) { - OriginList *LHSList = getOriginsList(*DRE_LHS); - assert(LHSList && "LHS is a DRE and should have an origin list"); - OriginList *RHSList = getOriginsList(*RHSExpr); - - // For operator= with reference parameters (e.g., - // `View& operator=(const View&)`), the RHS argument stays an lvalue, - // unlike built-in assignment where LValueToRValue cast strips the outer - // lvalue origin. Strip it manually to get the actual value origins being - // assigned. - RHSList = getRValueOrigins(RHSExpr, RHSList); + LHSExpr = LHSExpr->IgnoreParenImpCasts(); + OriginList *LHSList = nullptr; - markUseAsWrite(DRE_LHS); - // Kill the old loans of the destination origin and flow the new loans - // from the source origin. - flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true); + if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) { + LHSList = getOriginsList(*DRE_LHS); + assert(LHSList && "LHS is a DRE and should have an origin list"); + } + // Handle assignment to member fields (e.g., `this->view = s` or `view = s`). + // This enables detection of dangling fields when local values escape to + // fields. + if (const auto *ME_LHS = dyn_cast<MemberExpr>(LHSExpr)) { + LHSList = getOriginsList(*ME_LHS); + assert(LHSList && "LHS is a MemberExpr and should have an origin list"); } + if (!LHSList) + return; + OriginList *RHSList = getOriginsList(*RHSExpr); + // For operator= with reference parameters (e.g., + // `View& operator=(const View&)`), the RHS argument stays an lvalue, + // unlike built-in assignment where LValueToRValue cast strips the outer + // lvalue origin. Strip it manually to get the actual value origins being + // assigned. + RHSList = getRValueOrigins(RHSExpr, RHSList); + + if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) + markUseAsWrite(DRE_LHS); + // Kill the old loans of the destination origin and flow the new loans + // from the source origin. + flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true); } void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) { @@ -463,6 +488,15 @@ void FactsGenerator::handleTemporaryDtor( } } +void FactsGenerator::handleExitBlock() { + // Creates FieldEscapeFacts for all field origins that remain live at exit. + for (const Origin &O : FactMgr.getOriginMgr().getOrigins()) + if (O.getDecl()) + if (auto *FD = dyn_cast<FieldDecl>(O.getDecl())) + EscapesInCurrentBlock.push_back( + FactMgr.createFact<FieldEscapeFact>(O.ID, FD)); +} + void FactsGenerator::handleGSLPointerConstruction(const CXXConstructExpr *CCE) { assert(isGslPointerType(CCE->getType())); if (CCE->getNumArgs() != 1) diff --git a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp index 862dca256280f..f210fb4d752d4 100644 --- a/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LiveOrigins.cpp @@ -8,6 +8,7 @@ #include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h" #include "Dataflow.h" +#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" #include "llvm/Support/ErrorHandling.h" namespace clang::lifetimes::internal { @@ -56,8 +57,12 @@ struct Lattice { static SourceLocation GetFactLoc(CausingFactType F) { if (const auto *UF = F.dyn_cast<const UseFact *>()) return UF->getUseExpr()->getExprLoc(); - if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) - return OEF->getEscapeExpr()->getExprLoc(); + if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) { + if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF)) + return ReturnEsc->getReturnExpr()->getExprLoc(); + if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF)) + return FieldEsc->getFieldDecl()->getLocation(); + } llvm_unreachable("unhandled causing fact in PointerUnion"); } diff --git a/clang/lib/Analysis/LifetimeSafety/Origins.cpp b/clang/lib/Analysis/LifetimeSafety/Origins.cpp index ca933f612eb08..6062b3d42ebe2 100644 --- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp @@ -9,6 +9,7 @@ #include "clang/Analysis/Analyses/LifetimeSafety/Origins.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/Expr.h" @@ -138,25 +139,33 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) { QualType Type = E->getType(); - // Special handling for DeclRefExpr to share origins with the underlying decl. - if (auto *DRE = dyn_cast<DeclRefExpr>(E)) { + // Special handling for expressions referring to a decl to share origins with + // the underlying decl. + const ValueDecl *ReferencedDecl = nullptr; + if (auto *DRE = dyn_cast<DeclRefExpr>(E)) + ReferencedDecl = DRE->getDecl(); + if (auto *ME = dyn_cast<MemberExpr>(E)) + if (auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl()); + Field && isa<CXXThisExpr>(ME->getBase())) + ReferencedDecl = Field; + if (ReferencedDecl) { OriginList *Head = nullptr; - // For non-reference declarations (e.g., `int* p`), the DeclRefExpr is an + // For non-reference declarations (e.g., `int* p`), the expression is an // lvalue (addressable) that can be borrowed, so we create an outer origin // for the lvalue itself, with the pointee being the declaration's list. // This models taking the address: `&p` borrows the storage of `p`, not what // `p` points to. - if (doesDeclHaveStorage(DRE->getDecl())) { - Head = createNode(DRE, QualType{}); - // This ensures origin sharing: multiple DeclRefExprs to the same + if (doesDeclHaveStorage(ReferencedDecl)) { + Head = createNode(E, QualType{}); + // This ensures origin sharing: multiple expressions to the same // declaration share the same underlying origins. - Head->setInnerOriginList(getOrCreateList(DRE->getDecl())); + Head->setInnerOriginList(getOrCreateList(ReferencedDecl)); } else { // For reference-typed declarations (e.g., `int& r = p`) which have no // storage, the DeclRefExpr directly reuses the declaration's list since // references don't add an extra level of indirection at the expression // level. - Head = getOrCreateList(DRE->getDecl()); + Head = getOrCreateList(ReferencedDecl); } return ExprToList[E] = Head; } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 03d84fc935b8e..4b115a4efdbe1 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -68,61 +68,60 @@ using namespace clang; //===----------------------------------------------------------------------===// namespace { - class UnreachableCodeHandler : public reachable_code::Callback { - Sema &S; - SourceRange PreviousSilenceableCondVal; - - public: - UnreachableCodeHandler(Sema &s) : S(s) {} - - void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L, - SourceRange SilenceableCondVal, SourceRange R1, - SourceRange R2, bool HasFallThroughAttr) override { - // If the diagnosed code is `[[fallthrough]];` and - // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never - // be executed` warning to avoid generating diagnostic twice - if (HasFallThroughAttr && - !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr, - SourceLocation())) - return; +class UnreachableCodeHandler : public reachable_code::Callback { + Sema &S; + SourceRange PreviousSilenceableCondVal; - // Avoid reporting multiple unreachable code diagnostics that are - // triggered by the same conditional value. - if (PreviousSilenceableCondVal.isValid() && - SilenceableCondVal.isValid() && - PreviousSilenceableCondVal == SilenceableCondVal) - return; - PreviousSilenceableCondVal = SilenceableCondVal; +public: + UnreachableCodeHandler(Sema &s) : S(s) {} + + void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L, + SourceRange SilenceableCondVal, SourceRange R1, + SourceRange R2, bool HasFallThroughAttr) override { + // If the diagnosed code is `[[fallthrough]];` and + // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never + // be executed` warning to avoid generating diagnostic twice + if (HasFallThroughAttr && + !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr, + SourceLocation())) + return; - unsigned diag = diag::warn_unreachable; - switch (UK) { - case reachable_code::UK_Break: - diag = diag::warn_unreachable_break; - break; - case reachable_code::UK_Return: - diag = diag::warn_unreachable_return; - break; - case reachable_code::UK_Loop_Increment: - diag = diag::warn_unreachable_loop_increment; - break; - case reachable_code::UK_Other: - break; - } + // Avoid reporting multiple unreachable code diagnostics that are + // triggered by the same conditional value. + if (PreviousSilenceableCondVal.isValid() && SilenceableCondVal.isValid() && + PreviousSilenceableCondVal == SilenceableCondVal) + return; + PreviousSilenceableCondVal = SilenceableCondVal; + + unsigned diag = diag::warn_unreachable; + switch (UK) { + case reachable_code::UK_Break: + diag = diag::warn_unreachable_break; + break; + case reachable_code::UK_Return: + diag = diag::warn_unreachable_return; + break; + case reachable_code::UK_Loop_Increment: + diag = diag::warn_unreachable_loop_increment; + break; + case reachable_code::UK_Other: + break; + } - S.Diag(L, diag) << R1 << R2; + S.Diag(L, diag) << R1 << R2; - SourceLocation Open = SilenceableCondVal.getBegin(); - if (Open.isValid()) { - SourceLocation Close = SilenceableCondVal.getEnd(); - Close = S.getLocForEndOfToken(Close); - if (Close.isValid()) { - S.Diag(Open, diag::note_unreachable_silence) + SourceLocation Open = SilenceableCondVal.getBegin(); + if (Open.isValid()) { + SourceLocation Close = SilenceableCondVal.getEnd(); + Close = S.getLocForEndOfToken(Close); + if (Close.isValid()) { + S.Diag(Open, diag::note_unreachable_silence) << FixItHint::CreateInsertion(Open, "/* DISABLES CODE */ (") << FixItHint::CreateInsertion(Close, ")"); - } } } - }; + } +}; } // anonymous namespace /// CheckUnreachable - Check for unreachable code. @@ -292,7 +291,8 @@ static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD, return; CFG *cfg = AC.getCFG(); - if (!cfg) return; + if (!cfg) + return; // If the exit block is unreachable, skip processing the function. if (cfg->getExit().pred_empty()) @@ -327,10 +327,9 @@ static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock, if (Succ->getBlockID() == Body->getExit().getBlockID()) return true; - if (auto *Catch = - dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) { + if (auto *Catch = dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) { QualType Caught = Catch->getCaughtType(); - if (Caught.isNull() || // catch (...) catches everything + if (Caught.isNull() || // catch (...) catches everything !E->getSubExpr() || // throw; is considered cuaght by any handler S.handlerCanCatch(Caught, E->getSubExpr()->getType())) // Exception doesn't escape via this path. @@ -349,7 +348,8 @@ static void visitReachableThrows( CFG *BodyCFG, llvm::function_ref<void(const CXXThrowExpr *, CFGBlock &)> Visit) { llvm::BitVector Reachable(BodyCFG->getNumBlockIDs()); - clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), Reachable); + clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), + Reachable); for (CFGBlock *B : *BodyCFG) { if (!Reachable[B->getBlockID()]) continue; @@ -372,8 +372,8 @@ static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc, (isa<CXXDestructorDecl>(FD) || FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) { - if (const auto *Ty = FD->getTypeSourceInfo()->getType()-> - getAs<FunctionProtoType>()) + if (const auto *Ty = + FD->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>()) S.Diag(FD->getLocation(), diag::note_throw_in_dtor) << !isa<CXXDestructorDecl>(FD) << !Ty->hasExceptionSpec() << FD->getExceptionSpecSourceRange(); @@ -390,10 +390,11 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, return; if (BodyCFG->getExit().pred_empty()) return; - visitReachableThrows(BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { - if (throwEscapes(S, Throw, Block, BodyCFG)) - EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); - }); + visitReachableThrows( + BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { + if (throwEscapes(S, Throw, Block, BodyCFG)) + EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); + }); } static bool isNoexcept(const FunctionDecl *FD) { @@ -566,13 +567,14 @@ enum ControlFlowKind { /// will return. static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { CFG *cfg = AC.getCFG(); - if (!cfg) return UnknownFallThrough; + if (!cfg) + return UnknownFallThrough; // The CFG leaves in dead things, and we don't want the dead code paths to // confuse us, so we mark all live things first. llvm::BitVector live(cfg->getNumBlockIDs()); - unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(), - live); + unsigned count = + reachable_code::ScanReachableFromBlock(&cfg->getEntry(), live); bool AddEHEdges = AC.getAddEHEdges(); if (!AddEHEdges && count != cfg->getNumBlockIDs()) @@ -625,7 +627,7 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { // statement (if it exists). CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend(); - for ( ; ri != re ; ++ri) + for (; ri != re; ++ri) if (ri->getAs<CFGStmt>()) break; @@ -799,14 +801,12 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, else ReturnsVoid = FD->getReturnType()->isVoidType(); HasNoReturn = FD->isNoReturn() || FD->hasAttr<InferredNoReturnAttr>(); - } - else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { ReturnsVoid = MD->getReturnType()->isVoidType(); HasNoReturn = MD->hasAttr<NoReturnAttr>(); - } - else if (isa<BlockDecl>(D)) { + } else if (isa<BlockDecl>(D)) { if (const FunctionType *FT = - BlockType->getPointeeType()->getAs<FunctionType>()) { + BlockType->getPointeeType()->getAs<FunctionType>()) { if (FT->getReturnType()->isVoidType()) ReturnsVoid = true; if (FT->getNoReturnAttr()) @@ -818,7 +818,7 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, // Short circuit for compilation speed. if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn)) - return; + return; SourceLocation LBrace = Body->getBeginLoc(), RBrace = Body->getEndLoc(); // cpu_dispatch functions permit empty function bodies for ICC compatibility. @@ -895,7 +895,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> { typedef ConstEvaluatedExprVisitor<ContainsReference> Inherited; ContainsReference(ASTContext &Context, const DeclRefExpr *Needle) - : Inherited(Context), FoundReference(false), Needle(Needle) {} + : Inherited(Context), FoundReference(false), Needle(Needle) {} void VisitExpr(const Expr *E) { // Stop evaluating if we already have a reference. @@ -918,8 +918,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> { static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { QualType VariableTy = VD->getType().getCanonicalType(); - if (VariableTy->isBlockPointerType() && - !VD->hasAttr<BlocksAttr>()) { + if (VariableTy->isBlockPointerType() && !VD->hasAttr<BlocksAttr>()) { S.Diag(VD->getLocation(), diag::note_block_var_fixit_add_initialization) << VD->getDeclName() << FixItHint::CreateInsertion(VD->getLocation(), "__block "); @@ -941,16 +940,16 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { if (Init.empty()) return false; - S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() - << FixItHint::CreateInsertion(Loc, Init); + S.Diag(Loc, diag::note_var_fixit_add_initialization) + << VD->getDeclName() << FixItHint::CreateInsertion(Loc, Init); return true; } /// Create a fixit to remove an if-like statement, on the assumption that its /// condition is CondVal. static void CreateIfFixit(Sema &S, const Stmt *If, const Stmt *Then, - const Stmt *Else, bool CondVal, - FixItHint &Fixit1, FixItHint &Fixit2) { + const Stmt *Else, bool CondVal, FixItHint &Fixit1, + FixItHint &Fixit2) { if (CondVal) { // If condition is always true, remove all but the 'then'. Fixit1 = FixItHint::CreateRemoval( @@ -1017,9 +1016,9 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, // For all binary terminators, branch 0 is taken if the condition is true, // and branch 1 is taken if the condition is false. int RemoveDiagKind = -1; - const char *FixitStr = - S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false") - : (I->Output ? "1" : "0"); + const char *FixitStr = S.getLangOpts().CPlusPlus + ? (I->Output ? "true" : "false") + : (I->Output ? "1" : "0"); FixItHint Fixit1, Fixit2; switch (Term ? Term->getStmtClass() : Stmt::DeclStmtClass) { @@ -1035,8 +1034,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, Str = "if"; Range = IS->getCond()->getSourceRange(); RemoveDiagKind = 0; - CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), - I->Output, Fixit1, Fixit2); + CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), I->Output, Fixit1, + Fixit2); break; } case Stmt::ConditionalOperatorClass: { @@ -1045,8 +1044,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, Str = "?:"; Range = CO->getCond()->getSourceRange(); RemoveDiagKind = 0; - CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), - I->Output, Fixit1, Fixit2); + CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), I->Output, + Fixit1, Fixit2); break; } case Stmt::BinaryOperatorClass: { @@ -1121,13 +1120,13 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, } S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var) - << VD->getDeclName() << IsCapturedByBlock << DiagKind - << Str << I->Output << Range; + << VD->getDeclName() << IsCapturedByBlock << DiagKind << Str + << I->Output << Range; S.Diag(User->getBeginLoc(), diag::note_uninit_var_use) << IsCapturedByBlock << User->getSourceRange(); if (RemoveDiagKind != -1) S.Diag(Fixit1.RemoveRange.getBegin(), diag::note_uninit_fixit_remove_cond) - << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; + << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; Diagnosed = true; } @@ -1297,32 +1296,32 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor { // Don't care about other unreachable statements. } } - // If there are no unreachable statements, this may be a special - // case in CFG: - // case X: { - // A a; // A has a destructor. - // break; - // } - // // <<<< This place is represented by a 'hanging' CFG block. - // case Y: - continue; + // If there are no unreachable statements, this may be a special + // case in CFG: + // case X: { + // A a; // A has a destructor. + // break; + // } + // // <<<< This place is represented by a 'hanging' CFG block. + // case Y: + continue; } - const Stmt *LastStmt = getLastStmt(*P); - if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { - markFallthroughVisited(AS); - ++AnnotatedCnt; - continue; // Fallthrough annotation, good. - } + const Stmt *LastStmt = getLastStmt(*P); + if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { + markFallthroughVisited(AS); + ++AnnotatedCnt; + continue; // Fallthrough annotation, good. + } - if (!LastStmt) { // This block contains no executable statements. - // Traverse its predecessors. - std::copy(P->pred_begin(), P->pred_end(), - std::back_inserter(BlockQueue)); - continue; - } + if (!LastStmt) { // This block contains no executable statements. + // Traverse its predecessors. + std::copy(P->pred_begin(), P->pred_end(), + std::back_inserter(BlockQueue)); + continue; + } - ++UnannotatedCnt; + ++UnannotatedCnt; } return !!UnannotatedCnt; } @@ -1338,64 +1337,63 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor { return true; } - // We don't want to traverse local type declarations. We analyze their - // methods separately. - bool TraverseDecl(Decl *D) override { return true; } - - // We analyze lambda bodies separately. Skip them here. - bool TraverseLambdaExpr(LambdaExpr *LE) override { - // Traverse the captures, but not the body. - for (const auto C : zip(LE->captures(), LE->capture_inits())) - TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C)); - return true; - } + // We don't want to traverse local type declarations. We analyze their + // methods separately. + bool TraverseDecl(Decl *D) override { return true; } - private: + // We analyze lambda bodies separately. Skip them here. + bool TraverseLambdaExpr(LambdaExpr *LE) override { + // Traverse the captures, but not the body. + for (const auto C : zip(LE->captures(), LE->capture_inits())) + TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C)); + return true; + } - static const AttributedStmt *asFallThroughAttr(const Stmt *S) { - if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { - if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) - return AS; - } - return nullptr; +private: + static const AttributedStmt *asFallThroughAttr(const Stmt *S) { + if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { + if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) + return AS; } + return nullptr; + } - static const Stmt *getLastStmt(const CFGBlock &B) { - if (const Stmt *Term = B.getTerminatorStmt()) - return Term; - for (const CFGElement &Elem : llvm::reverse(B)) - if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>()) - return CS->getStmt(); - // Workaround to detect a statement thrown out by CFGBuilder: - // case X: {} case Y: - // case X: ; case Y: - if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) - if (!isa<SwitchCase>(SW->getSubStmt())) - return SW->getSubStmt(); + static const Stmt *getLastStmt(const CFGBlock &B) { + if (const Stmt *Term = B.getTerminatorStmt()) + return Term; + for (const CFGElement &Elem : llvm::reverse(B)) + if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>()) + return CS->getStmt(); + // Workaround to detect a statement thrown out by CFGBuilder: + // case X: {} case Y: + // case X: ; case Y: + if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) + if (!isa<SwitchCase>(SW->getSubStmt())) + return SW->getSubStmt(); - return nullptr; - } + return nullptr; + } - bool FoundSwitchStatements; - AttrStmts FallthroughStmts; - Sema &S; - llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks; + bool FoundSwitchStatements; + AttrStmts FallthroughStmts; + Sema &S; + llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks; }; } // anonymous namespace static StringRef getFallthroughAttrSpelling(Preprocessor &PP, SourceLocation Loc) { - TokenValue FallthroughTokens[] = { - tok::l_square, tok::l_square, - PP.getIdentifierInfo("fallthrough"), - tok::r_square, tok::r_square - }; - - TokenValue ClangFallthroughTokens[] = { - tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"), - tok::coloncolon, PP.getIdentifierInfo("fallthrough"), - tok::r_square, tok::r_square - }; + TokenValue FallthroughTokens[] = {tok::l_square, tok::l_square, + PP.getIdentifierInfo("fallthrough"), + tok::r_square, tok::r_square}; + + TokenValue ClangFallthroughTokens[] = {tok::l_square, + tok::l_square, + PP.getIdentifierInfo("clang"), + tok::coloncolon, + PP.getIdentifierInfo("fallthrough"), + tok::r_square, + tok::r_square}; bool PreferClangAttr = !PP.getLangOpts().CPlusPlus17 && !PP.getLangOpts().C23; @@ -1510,13 +1508,12 @@ static bool isInLoop(const ASTContext &Ctx, const ParentMap &PM, static void diagnoseRepeatedUseOfWeak(Sema &S, const sema::FunctionScopeInfo *CurFn, - const Decl *D, - const ParentMap &PM) { + const Decl *D, const ParentMap &PM) { typedef sema::FunctionScopeInfo::WeakObjectProfileTy WeakObjectProfileTy; typedef sema::FunctionScopeInfo::WeakObjectUseMap WeakObjectUseMap; typedef sema::FunctionScopeInfo::WeakUseVector WeakUseVector; typedef std::pair<const Stmt *, WeakObjectUseMap::const_iterator> - StmtUsesPair; + StmtUsesPair; ASTContext &Ctx = S.getASTContext(); @@ -1530,7 +1527,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Find the first read of the weak object. WeakUseVector::const_iterator UI = Uses.begin(), UE = Uses.end(); - for ( ; UI != UE; ++UI) { + for (; UI != UE; ++UI) { if (UI->isUnsafe()) break; } @@ -1587,12 +1584,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak. // FIXME: Should we use a common classification enum and the same set of // possibilities all throughout Sema? - enum { - Function, - Method, - Block, - Lambda - } FunctionKind; + enum { Function, Method, Block, Lambda } FunctionKind; if (isa<sema::BlockScopeInfo>(CurFn)) FunctionKind = Block; @@ -1623,12 +1615,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Classify the weak object being accessed for better warning text. // This enum should stay in sync with the cases in // warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak. - enum { - Variable, - Property, - ImplicitProperty, - Ivar - } ObjectKind; + enum { Variable, Property, ImplicitProperty, Ivar } ObjectKind; const NamedDecl *KeyProp = Key.getProperty(); if (isa<VarDecl>(KeyProp)) @@ -1730,7 +1717,7 @@ class UninitValsDiagReporter : public UninitVariablesHandler { } private: - static bool hasAlwaysUninitializedUse(const UsesVec* vec) { + static bool hasAlwaysUninitializedUse(const UsesVec *vec) { return llvm::any_of(*vec, [](const UninitUse &U) { return U.getKind() == UninitUse::Always || U.getKind() == UninitUse::AfterCall || @@ -1980,10 +1967,10 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { : getNotes(); } - public: +public: ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL) - : S(S), FunLocation(FL), FunEndLocation(FEL), - CurrentFunction(nullptr), Verbose(false) {} + : S(S), FunLocation(FL), FunEndLocation(FEL), CurrentFunction(nullptr), + Verbose(false) {} void setVerbose(bool b) { Verbose = b; } @@ -2078,18 +2065,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { bool ReentrancyMismatch) override { unsigned DiagID = 0; switch (LEK) { - case LEK_LockedSomePredecessors: - DiagID = diag::warn_lock_some_predecessors; - break; - case LEK_LockedSomeLoopIterations: - DiagID = diag::warn_expecting_lock_held_on_loop; - break; - case LEK_LockedAtEndOfFunction: - DiagID = diag::warn_no_unlock; - break; - case LEK_NotLockedAtEndOfFunction: - DiagID = diag::warn_expecting_locked; - break; + case LEK_LockedSomePredecessors: + DiagID = diag::warn_lock_some_predecessors; + break; + case LEK_LockedSomeLoopIterations: + DiagID = diag::warn_expecting_lock_held_on_loop; + break; + case LEK_LockedAtEndOfFunction: + DiagID = diag::warn_no_unlock; + break; + case LEK_NotLockedAtEndOfFunction: + DiagID = diag::warn_expecting_locked; + break; } if (LocEndOfScope.isInvalid()) LocEndOfScope = FunEndLocation; @@ -2135,7 +2122,7 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { break; } PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) - << D << getLockKindFromAccessKind(AK)); + << D << getLockKindFromAccessKind(AK)); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2146,43 +2133,42 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { unsigned DiagID = 0; if (PossibleMatch) { switch (POK) { - case POK_VarAccess: - DiagID = diag::warn_variable_requires_lock_precise; - break; - case POK_VarDereference: - DiagID = diag::warn_var_deref_requires_lock_precise; - break; - case POK_FunctionCall: - DiagID = diag::warn_fun_requires_lock_precise; - break; - case POK_PassByRef: - DiagID = diag::warn_guarded_pass_by_reference; - break; - case POK_PtPassByRef: - DiagID = diag::warn_pt_guarded_pass_by_reference; - break; - case POK_ReturnByRef: - DiagID = diag::warn_guarded_return_by_reference; - break; - case POK_PtReturnByRef: - DiagID = diag::warn_pt_guarded_return_by_reference; - break; - case POK_PassPointer: - DiagID = diag::warn_guarded_pass_pointer; - break; - case POK_PtPassPointer: - DiagID = diag::warn_pt_guarded_pass_pointer; - break; - case POK_ReturnPointer: - DiagID = diag::warn_guarded_return_pointer; - break; - case POK_PtReturnPointer: - DiagID = diag::warn_pt_guarded_return_pointer; - break; + case POK_VarAccess: + DiagID = diag::warn_variable_requires_lock_precise; + break; + case POK_VarDereference: + DiagID = diag::warn_var_deref_requires_lock_precise; + break; + case POK_FunctionCall: + DiagID = diag::warn_fun_requires_lock_precise; + break; + case POK_PassByRef: + DiagID = diag::warn_guarded_pass_by_reference; + break; + case POK_PtPassByRef: + DiagID = diag::warn_pt_guarded_pass_by_reference; + break; + case POK_ReturnByRef: + DiagID = diag::warn_guarded_return_by_reference; + break; + case POK_PtReturnByRef: + DiagID = diag::warn_pt_guarded_return_by_reference; + break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } - PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind - << D - << LockName << LK); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) + << Kind << D << LockName << LK); PartialDiagnosticAt Note(Loc, S.PDiag(diag::note_found_mutex_near_match) << *PossibleMatch); if (Verbose && POK == POK_VarAccess) { @@ -2194,43 +2180,42 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { Warnings.emplace_back(std::move(Warning), getNotes(Note)); } else { switch (POK) { - case POK_VarAccess: - DiagID = diag::warn_variable_requires_lock; - break; - case POK_VarDereference: - DiagID = diag::warn_var_deref_requires_lock; - break; - case POK_FunctionCall: - DiagID = diag::warn_fun_requires_lock; - break; - case POK_PassByRef: - DiagID = diag::warn_guarded_pass_by_reference; - break; - case POK_PtPassByRef: - DiagID = diag::warn_pt_guarded_pass_by_reference; - break; - case POK_ReturnByRef: - DiagID = diag::warn_guarded_return_by_reference; - break; - case POK_PtReturnByRef: - DiagID = diag::warn_pt_guarded_return_by_reference; - break; - case POK_PassPointer: - DiagID = diag::warn_guarded_pass_pointer; - break; - case POK_PtPassPointer: - DiagID = diag::warn_pt_guarded_pass_pointer; - break; - case POK_ReturnPointer: - DiagID = diag::warn_guarded_return_pointer; - break; - case POK_PtReturnPointer: - DiagID = diag::warn_pt_guarded_return_pointer; - break; + case POK_VarAccess: + DiagID = diag::warn_variable_requires_lock; + break; + case POK_VarDereference: + DiagID = diag::warn_var_deref_requires_lock; + break; + case POK_FunctionCall: + DiagID = diag::warn_fun_requires_lock; + break; + case POK_PassByRef: + DiagID = diag::warn_guarded_pass_by_reference; + break; + case POK_PtPassByRef: + DiagID = diag::warn_pt_guarded_pass_by_reference; + break; + case POK_ReturnByRef: + DiagID = diag::warn_guarded_return_by_reference; + break; + case POK_PtReturnByRef: + DiagID = diag::warn_pt_guarded_return_by_reference; + break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } - PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind - << D - << LockName << LK); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) + << Kind << D << LockName << LK); if (Verbose && POK == POK_VarAccess) { PartialDiagnosticAt Note(D->getLocation(), S.PDiag(diag::note_guarded_by_declared_here)); @@ -2242,9 +2227,9 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquire_requires_negative_cap) - << Kind << LockName << Neg); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_acquire_requires_negative_cap) + << Kind << LockName << Neg); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2264,22 +2249,20 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { void handleLockAcquiredBefore(StringRef Kind, Name L1Name, Name L2Name, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquired_before) << Kind << L1Name << L2Name); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_acquired_before) + << Kind << L1Name << L2Name); Warnings.emplace_back(std::move(Warning), getNotes()); } void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); Warnings.emplace_back(std::move(Warning), getNotes()); } - void enterFunction(const FunctionDecl* FD) override { - CurrentFunction = FD; - } + void enterFunction(const FunctionDecl *FD) override { CurrentFunction = FD; } - void leaveFunction(const FunctionDecl* FD) override { + void leaveFunction(const FunctionDecl *FD) override { CurrentFunction = nullptr; } }; @@ -2300,7 +2283,6 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { DiagList Warnings; public: - ConsumedWarningsHandler(Sema &S) : S(S) {} void emitDiagnostics() override { @@ -2314,8 +2296,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnLoopStateMismatch(SourceLocation Loc, StringRef VariableName) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) << - VariableName); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) + << VariableName); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2325,9 +2307,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_param_return_typestate_mismatch) << VariableName << - ExpectedState << ObservedState); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_param_return_typestate_mismatch) + << VariableName << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2335,16 +2317,18 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnParamTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_param_typestate_mismatch) << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_param_typestate_mismatch) + << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } void warnReturnTypestateForUnconsumableType(SourceLocation Loc, StringRef TypeName) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_return_typestate_for_unconsumable_type) << TypeName); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_return_typestate_for_unconsumable_type) + << TypeName); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2352,8 +2336,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_return_typestate_mismatch) + << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2361,8 +2346,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_use_of_temp_in_invalid_state) << MethodName << State); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_use_of_temp_in_invalid_state) + << MethodName << State); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2370,8 +2356,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnUseInInvalidState(StringRef MethodName, StringRef VariableName, StringRef State, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) << - MethodName << VariableName << State); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) + << MethodName << VariableName + << State); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2387,7 +2374,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { namespace { class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { Sema &S; - bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? + bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? // Lists as a string the names of variables in `VarGroupForVD` except for `VD` // itself: @@ -2426,7 +2413,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { public: UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions) - : S(S), SuggestSuggestions(SuggestSuggestions) {} + : S(S), SuggestSuggestions(SuggestSuggestions) {} void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx) override { @@ -2611,7 +2598,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { #ifndef NDEBUG if (areDebugNotesRequested()) - for (const DebugNote &Note: DebugNotesByVar[Variable]) + for (const DebugNote &Note : DebugNotesByVar[Variable]) S.Diag(Note.first, diag::note_safe_buffer_debug_mode) << Note.second; #endif } @@ -2713,8 +2700,7 @@ sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) MaxCFGBlocksPerFunction(0), NumUninitAnalysisFunctions(0), NumUninitAnalysisVariables(0), MaxUninitAnalysisVariablesPerFunction(0), NumUninitAnalysisBlockVisits(0), - MaxUninitAnalysisBlockVisitsPerFunction(0) { -} + MaxUninitAnalysisBlockVisitsPerFunction(0) {} // We need this here for unique_ptr with forward declared class. sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default; @@ -2889,7 +2875,7 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << UseExpr->getSourceRange(); } - void reportUseAfterReturn(const Expr *IssueExpr, const Expr *EscapeExpr, + void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, SourceLocation ExpiryLoc, Confidence C) override { S.Diag(IssueExpr->getExprLoc(), C == Confidence::Definite @@ -2897,8 +2883,17 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { : diag::warn_lifetime_safety_return_stack_addr_strict) << IssueExpr->getSourceRange(); - S.Diag(EscapeExpr->getExprLoc(), diag::note_lifetime_safety_returned_here) - << EscapeExpr->getSourceRange(); + S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here) + << ReturnExpr->getSourceRange(); + } + + void reportDanglingField(const Expr *IssueExpr, const FieldDecl *DanglingField, + SourceLocation ExpiryLoc) override { + S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_dangling_field) + << IssueExpr->getSourceRange(); + S.Diag(DanglingField->getLocation(), + diag::note_lifetime_safety_dangling_field_here) + << DanglingField->getEndLoc(); } void suggestAnnotation(SuggestionScope Scope, @@ -2952,6 +2947,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU, AC.getCFGBuildOptions().PruneTriviallyFalseEdges = false; AC.getCFGBuildOptions().AddLifetime = true; AC.getCFGBuildOptions().AddImplicitDtors = true; + AC.getCFGBuildOptions().AddParameterDtors = true; AC.getCFGBuildOptions().AddTemporaryDtors = true; AC.getCFGBuildOptions().setAllAlwaysAdd(); if (AC.getCFG()) @@ -2960,7 +2956,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU, } void clang::sema::AnalysisBasedWarnings::IssueWarnings( - TranslationUnitDecl *TU) { + TranslationUnitDecl *TU) { if (!TU) return; // This is unexpected, give up quietly. @@ -2974,7 +2970,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // UnsafeBufferUsage analysis settings. bool UnsafeBufferUsageCanEmitSuggestions = S.getLangOpts().CPlusPlus20; - bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. + bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. UnsafeBufferUsageCanEmitSuggestions && DiagOpts.ShowSafeBufferUsageSuggestions; bool UnsafeBufferUsageShouldSuggestSuggestions = @@ -3059,6 +3055,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFGBuildOptions().AddEHEdges = false; AC.getCFGBuildOptions().AddInitializers = true; AC.getCFGBuildOptions().AddImplicitDtors = true; + AC.getCFGBuildOptions().AddParameterDtors = true; AC.getCFGBuildOptions().AddTemporaryDtors = true; AC.getCFGBuildOptions().AddCXXNewAllocator = false; AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true; @@ -3089,13 +3086,13 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFGBuildOptions().setAllAlwaysAdd(); } else { AC.getCFGBuildOptions() - .setAlwaysAdd(Stmt::BinaryOperatorClass) - .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) - .setAlwaysAdd(Stmt::BlockExprClass) - .setAlwaysAdd(Stmt::CStyleCastExprClass) - .setAlwaysAdd(Stmt::DeclRefExprClass) - .setAlwaysAdd(Stmt::ImplicitCastExprClass) - .setAlwaysAdd(Stmt::UnaryOperatorClass); + .setAlwaysAdd(Stmt::BinaryOperatorClass) + .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) + .setAlwaysAdd(Stmt::BlockExprClass) + .setAlwaysAdd(Stmt::CStyleCastExprClass) + .setAlwaysAdd(Stmt::DeclRefExprClass) + .setAlwaysAdd(Stmt::ImplicitCastExprClass) + .setAlwaysAdd(Stmt::UnaryOperatorClass); } if (EnableLifetimeSafetyAnalysis) AC.getCFGBuildOptions().AddLifetime = true; @@ -3176,12 +3173,10 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( ++NumUninitAnalysisFunctions; NumUninitAnalysisVariables += stats.NumVariablesAnalyzed; NumUninitAnalysisBlockVisits += stats.NumBlockVisits; - MaxUninitAnalysisVariablesPerFunction = - std::max(MaxUninitAnalysisVariablesPerFunction, - stats.NumVariablesAnalyzed); - MaxUninitAnalysisBlockVisitsPerFunction = - std::max(MaxUninitAnalysisBlockVisitsPerFunction, - stats.NumBlockVisits); + MaxUninitAnalysisVariablesPerFunction = std::max( + MaxUninitAnalysisVariablesPerFunction, stats.NumVariablesAnalyzed); + MaxUninitAnalysisBlockVisitsPerFunction = std::max( + MaxUninitAnalysisBlockVisitsPerFunction, stats.NumBlockVisits); } } } @@ -3219,7 +3214,6 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( !Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, D->getBeginLoc())) diagnoseRepeatedUseOfWeak(S, fscope, D, AC.getParentMap()); - // Check for infinite self-recursion in functions if (!Diags.isIgnored(diag::warn_infinite_recursive_function, D->getBeginLoc())) { @@ -3250,8 +3244,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // If we successfully built a CFG for this context, record some more // detail information about it. NumCFGBlocks += cfg->getNumBlockIDs(); - MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction, - cfg->getNumBlockIDs()); + MaxCFGBlocksPerFunction = + std::max(MaxCFGBlocksPerFunction, cfg->getNumBlockIDs()); } else { ++NumFunctionsWithBadCFGs; } @@ -3263,7 +3257,7 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs; unsigned AvgCFGBlocksPerFunction = - !NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt; + !NumCFGsBuilt ? 0 : NumCFGBlocks / NumCFGsBuilt; llvm::errs() << NumFunctionsAnalyzed << " functions analyzed (" << NumFunctionsWithBadCFGs << " w/o CFGs).\n" << " " << NumCFGBlocks << " CFG blocks built.\n" @@ -3272,10 +3266,14 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { << " " << MaxCFGBlocksPerFunction << " max CFG blocks per function.\n"; - unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0 - : NumUninitAnalysisVariables/NumUninitAnalysisFunctions; - unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0 - : NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions; + unsigned AvgUninitVariablesPerFunction = + !NumUninitAnalysisFunctions + ? 0 + : NumUninitAnalysisVariables / NumUninitAnalysisFunctions; + unsigned AvgUninitBlockVisitsPerFunction = + !NumUninitAnalysisFunctions + ? 0 + : NumUninitAnalysisBlockVisits / NumUninitAnalysisFunctions; llvm::errs() << NumUninitAnalysisFunctions << " functions analyzed for uninitialiazed variables\n" << " " << NumUninitAnalysisVariables << " variables analyzed.\n" diff --git a/clang/test/Analysis/lifetime-cfg-output.cpp b/clang/test/Analysis/lifetime-cfg-output.cpp index 36b36eddc440c..0a75c5bcc0bcc 100644 --- a/clang/test/Analysis/lifetime-cfg-output.cpp +++ b/clang/test/Analysis/lifetime-cfg-output.cpp @@ -935,31 +935,3 @@ int backpatched_goto() { goto label; i++; } - -// CHECK: [B2 (ENTRY)] -// CHECK-NEXT: Succs (1): B1 -// CHECK: [B1] -// CHECK-NEXT: 1: a -// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) -// CHECK-NEXT: 3: b -// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, int) -// CHECK-NEXT: 5: [B1.2] + [B1.4] -// CHECK-NEXT: 6: c -// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, LValueToRValue, int) -// CHECK-NEXT: 8: [B1.5] + [B1.7] -// CHECK-NEXT: 9: int res = a + b + c; -// CHECK-NEXT: 10: res -// CHECK-NEXT: 11: [B1.10] (ImplicitCastExpr, LValueToRValue, int) -// CHECK-NEXT: 12: return [B1.11]; -// CHECK-NEXT: 13: [B1.9] (Lifetime ends) -// CHECK-NEXT: 14: [Parm: c] (Lifetime ends) -// CHECK-NEXT: 15: [Parm: b] (Lifetime ends) -// CHECK-NEXT: 16: [Parm: a] (Lifetime ends) -// CHECK-NEXT: Preds (1): B2 -// CHECK-NEXT: Succs (1): B0 -// CHECK: [B0 (EXIT)] -// CHECK-NEXT: Preds (1): B1 -int test_param_scope_end_order(int a, int b, int c) { - int res = a + b + c; - return res; -} diff --git a/clang/test/Analysis/scopes-cfg-output.cpp b/clang/test/Analysis/scopes-cfg-output.cpp index 9c75492c33a42..6ed6f3638f75b 100644 --- a/clang/test/Analysis/scopes-cfg-output.cpp +++ b/clang/test/Analysis/scopes-cfg-output.cpp @@ -1437,14 +1437,12 @@ void test_cleanup_functions() { // CHECK-NEXT: 4: return; // CHECK-NEXT: 5: CleanupFunction (cleanup_int) // CHECK-NEXT: 6: CFGScopeEnd(i) -// CHECK-NEXT: 7: CFGScopeEnd(m) // CHECK-NEXT: Preds (1): B3 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] // CHECK-NEXT: 1: return; // CHECK-NEXT: 2: CleanupFunction (cleanup_int) // CHECK-NEXT: 3: CFGScopeEnd(i) -// CHECK-NEXT: 4: CFGScopeEnd(m) // CHECK-NEXT: Preds (1): B3 // CHECK-NEXT: Succs (1): B0 // CHECK: [B3] diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 99a796c360a7f..2f4c2156d23cf 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1,6 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s -// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify=cfg %s -// RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=cfg %s +// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -mllvm --debug-only=LifetimeFacts -verify=cfg %s #include "Inputs/lifetime-analysis.h" @@ -80,11 +79,18 @@ void dangligGslPtrFromTemporary() { } struct DanglingGslPtrField { - MyIntPointer p; // expected-note {{pointer member declared here}} - MyLongPointerFromConversion p2; // expected-note {{pointer member declared here}} - DanglingGslPtrField(int i) : p(&i) {} // TODO - DanglingGslPtrField() : p2(MyLongOwnerWithConversion{}) {} // expected-warning {{initializing pointer member 'p2' to point to a temporary object whose lifetime is shorter than the lifetime of the constructed object}} - DanglingGslPtrField(double) : p(MyIntOwner{}) {} // expected-warning {{initializing pointer member 'p' to point to a temporary object whose lifetime is shorter than the lifetime of the constructed object}} + MyIntPointer p; // expected-note {{pointer member declared here}} \ + // cfg-note 3 {{this field dangles}} + MyLongPointerFromConversion p2; // expected-note {{pointer member declared here}} \ + // cfg-note 2 {{this field dangles}} + + DanglingGslPtrField(int i) : p(&i) {} // cfg-warning {{address of stack memory escapes to a field}} + DanglingGslPtrField() : p2(MyLongOwnerWithConversion{}) {} // expected-warning {{initializing pointer member 'p2' to point to a temporary object whose lifetime is shorter than the lifetime of the constructed object}} \ + // cfg-warning {{address of stack memory escapes to a field}} + DanglingGslPtrField(double) : p(MyIntOwner{}) {} // expected-warning {{initializing pointer member 'p' to point to a temporary object whose lifetime is shorter than the lifetime of the constructed object}} \ + // cfg-warning {{address of stack memory escapes to a field}} + DanglingGslPtrField(MyIntOwner io) : p(io) {} // cfg-warning {{address of stack memory escapes to a field}} + DanglingGslPtrField(MyLongOwnerWithConversion lo) : p2(lo) {} // cfg-warning {{address of stack memory escapes to a field}} }; MyIntPointer danglingGslPtrFromLocal() { @@ -1099,10 +1105,11 @@ struct Foo2 { }; struct Test { - Test(Foo2 foo) : bar(foo.bar.get()), // OK + Test(Foo2 foo) : bar(foo.bar.get()), // OK \ + // FIXME: cfg-warning {{address of stack memory escapes to a field}} storage(std::move(foo.bar)) {}; - Bar* bar; + Bar* bar; // cfg-note {{this field dangles}} std::unique_ptr<Bar> storage; }; diff --git a/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp new file mode 100644 index 0000000000000..c12b9088e9a38 --- /dev/null +++ b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp @@ -0,0 +1,151 @@ +// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -verify %s + +#include "Inputs/lifetime-analysis.h" + +template<int N> struct Dummy {}; +static std::string kGlobal = "GLOBAL"; +void takeString(std::string&& s); + +std::string_view construct_view(const std::string& str [[clang::lifetimebound]]); + +struct CtorInit { + std::string_view view; // expected-note {{this field dangles}} + CtorInit(std::string s) : view(s) {} // expected-warning {{address of stack memory escapes to a field}} +}; + +struct CtorSet { + std::string_view view; // expected-note {{this field dangles}} + CtorSet(std::string s) { view = s; } // expected-warning {{address of stack memory escapes to a field}} +}; + +struct CtorInitLifetimeBound { + std::string_view view; // expected-note {{this field dangles}} + CtorInitLifetimeBound(std::string s) : view(construct_view(s)) {} // expected-warning {{address of stack memory escapes to a field}} +}; + +struct CtorInitButMoved { + std::string_view view; + CtorInitButMoved(std::string s) : view(s) { takeString(std::move(s)); } +}; + +struct CtorInitButMovedOwned { + std::string owned; + std::string_view view; + CtorInitButMovedOwned(std::string s) : view(s), owned(std::move(s)) {} + CtorInitButMovedOwned(Dummy<1>, std::string s) : owned(std::move(s)), view(owned) {} +}; + +struct CtorInitMultipleViews { + std::string_view view1; // expected-note {{this field dangles}} + std::string_view view2; // expected-note {{this field dangles}} + CtorInitMultipleViews(std::string s) : view1(s), // expected-warning {{address of stack memory escapes to a field}} + view2(s) {} // expected-warning {{address of stack memory escapes to a field}} +}; + +struct CtorInitMultipleParams { + std::string_view view1; // expected-note {{this field dangles}} + std::string_view view2; // expected-note {{this field dangles}} + CtorInitMultipleParams(std::string s1, std::string s2) : view1(s1), // expected-warning {{address of stack memory escapes to a field}} + view2(s2) {} // expected-warning {{address of stack memory escapes to a field}} +}; + +struct CtorRefField { + const std::string& str; // expected-note {{this field dangles}} + const std::string_view& view; // expected-note {{this field dangles}} + CtorRefField(std::string s, std::string_view v) : str(s), // expected-warning {{address of stack memory escapes to a field}} + view(v) {} // expected-warning {{address of stack memory escapes to a field}} + CtorRefField(Dummy<1> ok, const std::string& s, const std::string_view& v): str(s), view(v) {} +}; + +struct CtorPointerField { + const char* ptr; // expected-note {{this field dangles}} + CtorPointerField(std::string s) : ptr(s.data()) {} // expected-warning {{address of stack memory escapes to a field}} + CtorPointerField(Dummy<1> ok, const std::string& s) : ptr(s.data()) {} + CtorPointerField(Dummy<2> ok, std::string_view view) : ptr(view.data()) {} +}; + +struct MemberSetters { + std::string_view view; // expected-note 5 {{this field dangles}} + const char* p; // expected-note 5 {{this field dangles}} + + void setWithParam(std::string s) { + view = s; // expected-warning {{address of stack memory escapes to a field}} + p = s.data(); // expected-warning {{address of stack memory escapes to a field}} + } + + void setWithParamAndReturn(std::string s) { + view = s; // expected-warning {{address of stack memory escapes to a field}} + p = s.data(); // expected-warning {{address of stack memory escapes to a field}} + return; + } + + void setWithParamOk(const std::string& s) { + view = s; + p = s.data(); + } + + void setWithParamOkAndReturn(const std::string& s) { + view = s; + p = s.data(); + return; + } + + void setWithLocal() { + std::string s; + view = s; // expected-warning {{address of stack memory escapes to a field}} + p = s.data(); // expected-warning {{address of stack memory escapes to a field}} + } + + void setWithLocalButMoved() { + std::string s; + view = s; + p = s.data(); + takeString(std::move(s)); + } + + void setWithGlobal() { + view = kGlobal; + p = kGlobal.data(); + } + + void setWithLocalThenWithGlobal() { + std::string local; + view = local; + p = local.data(); + + view = kGlobal; + p = kGlobal.data(); + } + + void setWithGlobalThenWithLocal() { + view = kGlobal; + p = kGlobal.data(); + + std::string local; + view = local; // expected-warning {{address of stack memory escapes to a field}} + p = local.data(); // expected-warning {{address of stack memory escapes to a field}} + } + + void use_after_scope() { + { + std::string local; + view = local; // expected-warning {{address of stack memory escapes to a field}} + p = local.data(); // expected-warning {{address of stack memory escapes to a field}} + } + (void)view; + (void)p; + } + + void use_after_scope_saved_after_reassignment() { + { + std::string local; + view = local; + p = local.data(); + } + (void)view; + (void)p; + + view = kGlobal; + p = kGlobal.data(); + } +}; diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp index a45100feb3f28..7e2215b8deedc 100644 --- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp +++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp @@ -27,7 +27,7 @@ MyObj* return_local_addr() { // CHECK-NEXT: Src: [[O_P]] (Decl: p, Type : MyObj *) // CHECK: Expire ([[L_X]] (Path: x)) // CHECK: Expire ({{[0-9]+}} (Path: p)) -// CHECK: OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr, Type : MyObj *)) +// CHECK: OriginEscapes ([[O_RET_VAL]] (Expr: ImplicitCastExpr, Type : MyObj *), via Return) } // Loan Expiration (Automatic Variable, C++) _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
