diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h index 6a90aeb01e638..712e1d42e2966 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h @@ -44,6 +44,8 @@ class Fact { Use, /// A marker for a specific point in the code, for testing. TestPoint, + OriginEscapes, + // An origin that stores a loan escapes the function. }; private: @@ -142,6 +144,23 @@ class ReturnOfOriginFact : public Fact { const OriginManager &OM) const override; }; +class OriginEscapesFact : public Fact { + OriginID OID; + const Stmt *EscapeSource; + +public: + static bool classof(const Fact *F) { + return F->getKind() == Kind::OriginEscapes; + } + + OriginEscapesFact(OriginID OID, const Stmt *Source) + : Fact(Kind::OriginEscapes), OID(OID), EscapeSource(Source) {} + OriginID getEscapedOriginID() const { return OID; } + const Stmt *getEscapeSource() const { return EscapeSource; } + void dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const override; +}; + class UseFact : public Fact { const Expr *UseExpr; // True if this use is a write operation (e.g., left-hand side of assignment). diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h index 5e58abee2bbb3..ec3c130bb6c66 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h @@ -93,6 +93,7 @@ class FactsGenerator : public ConstStmtVisitor { FactManager &FactMgr; AnalysisDeclContext &AC; llvm::SmallVector CurrentBlockFacts; + llvm::SmallVector EscapesInCurrentBlock; // To distinguish between reads and writes for use-after-free checks, this map // stores the `UseFact` for each `DeclRefExpr`. We initially identify all // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 91ffbb169f947..f5b1b3d9b6564 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -42,6 +42,11 @@ class LifetimeSafetyReporter { virtual void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, SourceLocation FreeLoc, Confidence Confidence) {} + + virtual void reportUseAfterReturn(const Expr *IssueExpr, + const Stmt *EscapeStmt, + SourceLocation ExpiryLoc, + Confidence Confidence) {} }; /// The main entry point for the analysis. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5ff4cc4b833d9..6ea5e34cab291 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10730,6 +10730,7 @@ def warn_lifetime_safety_loan_expires_strict : Warning< InGroup, 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">; // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index c443c3a5d4f9b..ae5c62ef04d72 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -21,6 +21,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TimeProfiler.h" @@ -61,10 +62,23 @@ class LifetimeChecker { AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter) : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM), Reporter(Reporter) { - for (const CFGBlock *B : *ADC.getAnalysis()) - for (const Fact *F : FactMgr.getFacts(B)) - if (const auto *EF = F->getAs()) + for (const CFGBlock *B : *ADC.getAnalysis()) { + llvm::SmallVector BlockExpires; + llvm::SmallVector BlockEscapes; + for (const Fact *F : FactMgr.getFacts(B)) { + if (const auto *EF = F->getAs()) { checkExpiry(EF); + BlockExpires.push_back(EF); + } else if (const auto *OEF = F->getAs()) { + BlockEscapes.push_back(OEF); + } + } + if (Reporter) { + for (const OriginEscapesFact *OEF : BlockEscapes) { + checkEscape(OEF, BlockExpires); + } + } + } issuePendingWarnings(); } @@ -106,6 +120,63 @@ class LifetimeChecker { /*ConfidenceLevel=*/CurConfidence}; } + void checkEscape(const OriginEscapesFact *OEF, + llvm::ArrayRef BlockExpires) { + + if (!Reporter) { + return; + } + + OriginID returnedOID = OEF->getEscapedOriginID(); + ProgramPoint PP = OEF; + + LoanSet HeldLoans = LoanPropagation.getLoans(returnedOID, PP); + if (HeldLoans.isEmpty()) { + return; + } + + llvm::SmallSet ExpiredLoansInBlock; + llvm::DenseMap ExpiryLocs; + + for (const ExpireFact *EF : BlockExpires) { + ExpiredLoansInBlock.insert(EF->getLoanID()); + ExpiryLocs[EF->getLoanID()] = EF->getExpiryLoc(); + } + + bool hasExpiredDependency = false; + bool allHeldLoansExpired = true; + LoanID exampleExpiredLoan = LoanID(); + + for (LoanID heldLoan : HeldLoans) { + if (ExpiredLoansInBlock.count(heldLoan)) { + hasExpiredDependency = true; + if (exampleExpiredLoan.Value == LoanID().Value) { + exampleExpiredLoan = heldLoan; + } + } else { + allHeldLoansExpired = false; + } + } + + if (!hasExpiredDependency) { + return; + } + + Confidence FinalConfidence; + if (allHeldLoansExpired) { + FinalConfidence = Confidence::Definite; + } else { + FinalConfidence = Confidence::Maybe; + } + + const Loan &L = FactMgr.getLoanMgr().getLoan(exampleExpiredLoan); + SourceLocation ExpiryLoc = ExpiryLocs[exampleExpiredLoan]; + const Stmt *EscapeSource = OEF->getEscapeSource(); + + Reporter->reportUseAfterReturn(L.IssueExpr, EscapeSource, ExpiryLoc, + FinalConfidence); + } + void issuePendingWarnings() { if (!Reporter) return; diff --git a/clang/lib/Analysis/LifetimeSafety/Dataflow.h b/clang/lib/Analysis/LifetimeSafety/Dataflow.h index 2f7bcb6e5dc81..37720ffa03618 100644 --- a/clang/lib/Analysis/LifetimeSafety/Dataflow.h +++ b/clang/lib/Analysis/LifetimeSafety/Dataflow.h @@ -168,6 +168,8 @@ class DataflowAnalysis { return D->transfer(In, *F->getAs()); case Fact::Kind::ReturnOfOrigin: return D->transfer(In, *F->getAs()); + case Fact::Kind::OriginEscapes: + return D->transfer(In, *F->getAs()); case Fact::Kind::Use: return D->transfer(In, *F->getAs()); case Fact::Kind::TestPoint: @@ -181,6 +183,7 @@ class DataflowAnalysis { Lattice transfer(Lattice In, const ExpireFact &) { return In; } Lattice transfer(Lattice In, const OriginFlowFact &) { return In; } Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; } + Lattice transfer(Lattice In, const OriginEscapesFact &) { return In; } Lattice transfer(Lattice In, const UseFact &) { return In; } Lattice transfer(Lattice In, const TestPointFact &) { return In; } }; diff --git a/clang/lib/Analysis/LifetimeSafety/Facts.cpp b/clang/lib/Analysis/LifetimeSafety/Facts.cpp index 1aea64f864367..29c75959ba0fe 100644 --- a/clang/lib/Analysis/LifetimeSafety/Facts.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Facts.cpp @@ -50,6 +50,14 @@ void ReturnOfOriginFact::dump(llvm::raw_ostream &OS, const LoanManager &, OS << ")\n"; } +void OriginEscapesFact::dump(llvm::raw_ostream &OS, const LoanManager &, + const OriginManager &OM) const { + OS << "OriginEscapes ("; + OM.dump(getEscapedOriginID(), OS); + OS << ")\n"; +} + + void UseFact::dump(llvm::raw_ostream &OS, const LoanManager &, const OriginManager &OM) const { OS << "Use ("; diff --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp index 9b68de107e314..762ed07eb5bb8 100644 --- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp +++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp @@ -58,6 +58,7 @@ void FactsGenerator::run() { // initializations and destructions are processed in the correct sequence. for (const CFGBlock *Block : *AC.getAnalysis()) { CurrentBlockFacts.clear(); + EscapesInCurrentBlock.clear(); for (unsigned I = 0; I < Block->size(); ++I) { const CFGElement &Element = Block->Elements[I]; if (std::optional CS = Element.getAs()) @@ -66,6 +67,8 @@ void FactsGenerator::run() { Element.getAs()) handleDestructor(*DtorOpt); } + CurrentBlockFacts.append(EscapesInCurrentBlock.begin(), + EscapesInCurrentBlock.end()); FactMgr.addBlockFacts(Block, CurrentBlockFacts); } } @@ -166,7 +169,8 @@ void FactsGenerator::VisitReturnStmt(const ReturnStmt *RS) { if (const Expr *RetExpr = RS->getRetValue()) { if (hasOrigin(RetExpr)) { OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr); - CurrentBlockFacts.push_back(FactMgr.createFact(OID)); + EscapesInCurrentBlock.push_back( + FactMgr.createFact(OID, RS)); } } } diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 140b709dbb651..879793575e0b4 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -65,61 +65,61 @@ 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. +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; + + // Avoid reporting multiple unreachable code diagnostics that are + // triggered by the same conditional value. if (PreviousSilenceableCondVal.isValid() && SilenceableCondVal.isValid() && - PreviousSilenceableCondVal == SilenceableCondVal) - return; - PreviousSilenceableCondVal = SilenceableCondVal; + 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; - } + 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. @@ -388,9 +388,9 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, if (BodyCFG->getExit().pred_empty()) return; visitReachableThrows(BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { - if (throwEscapes(S, Throw, Block, BodyCFG)) - EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); - }); + if (throwEscapes(S, Throw, Block, BodyCFG)) + EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); + }); } static bool isNoexcept(const FunctionDecl *FD) { @@ -803,7 +803,7 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, } else if (isa(D)) { if (const FunctionType *FT = - BlockType->getPointeeType()->getAs()) { + BlockType->getPointeeType()->getAs()) { if (FT->getReturnType()->isVoidType()) ReturnsVoid = true; if (FT->getNoReturnAttr()) @@ -815,7 +815,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. @@ -892,7 +892,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor { typedef ConstEvaluatedExprVisitor 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. @@ -1016,7 +1016,7 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, int RemoveDiagKind = -1; const char *FixitStr = S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false") - : (I->Output ? "1" : "0"); + : (I->Output ? "1" : "0"); FixItHint Fixit1, Fixit2; switch (Term ? Term->getStmtClass() : Stmt::DeclStmtClass) { @@ -1124,7 +1124,7 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &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; } @@ -1294,32 +1294,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; } @@ -1335,48 +1335,48 @@ 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 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 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; + } - private: +private: - static const AttributedStmt *asFallThroughAttr(const Stmt *S) { - if (const AttributedStmt *AS = dyn_cast_or_null(S)) { - if (hasSpecificAttr(AS->getAttrs())) - return AS; - } - return nullptr; + static const AttributedStmt *asFallThroughAttr(const Stmt *S) { + if (const AttributedStmt *AS = dyn_cast_or_null(S)) { + if (hasSpecificAttr(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 CS = Elem.getAs()) - 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(B.getLabel())) - if (!isa(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 CS = Elem.getAs()) + 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(B.getLabel())) + if (!isa(SW->getSubStmt())) + return SW->getSubStmt(); - return nullptr; - } + return nullptr; + } - bool FoundSwitchStatements; - AttrStmts FallthroughStmts; - Sema &S; - llvm::SmallPtrSet ReachableBlocks; + bool FoundSwitchStatements; + AttrStmts FallthroughStmts; + Sema &S; + llvm::SmallPtrSet ReachableBlocks; }; } // anonymous namespace @@ -1384,7 +1384,7 @@ static StringRef getFallthroughAttrSpelling(Preprocessor &PP, SourceLocation Loc) { TokenValue FallthroughTokens[] = { tok::l_square, tok::l_square, - PP.getIdentifierInfo("fallthrough"), + PP.getIdentifierInfo("fallthrough"), tok::r_square, tok::r_square }; @@ -1513,7 +1513,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, typedef sema::FunctionScopeInfo::WeakObjectUseMap WeakObjectUseMap; typedef sema::FunctionScopeInfo::WeakUseVector WeakUseVector; typedef std::pair - StmtUsesPair; + StmtUsesPair; ASTContext &Ctx = S.getASTContext(); @@ -1977,7 +1977,7 @@ 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) {} @@ -2075,18 +2075,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; @@ -2132,7 +2132,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()); } @@ -2143,39 +2143,39 @@ 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 @@ -2191,39 +2191,39 @@ 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 @@ -2241,7 +2241,7 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { SourceLocation Loc) override { PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_acquire_requires_negative_cap) - << Kind << LockName << Neg); + << Kind << LockName << Neg); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2423,7 +2423,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 { @@ -2808,6 +2808,17 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { << UseExpr->getEndLoc(); } + void reportUseAfterReturn(const Expr *IssueExpr, const Stmt *EscapeStmt, + SourceLocation ExpiryLoc, Confidence C) override { + S.Diag(IssueExpr->getExprLoc(), + C == Confidence::Definite + ? diag::warn_lifetime_safety_loan_expires_permissive + : diag::warn_lifetime_safety_loan_expires_strict) + << IssueExpr->getEndLoc(); + + S.Diag(EscapeStmt->getBeginLoc(), diag::note_lifetime_safety_returned_here); + } + private: Sema &S; }; @@ -2815,7 +2826,7 @@ class LifetimeSafetyReporterImpl : public LifetimeSafetyReporter { } // namespace clang::lifetimes void clang::sema::AnalysisBasedWarnings::IssueWarnings( - TranslationUnitDecl *TU) { + TranslationUnitDecl *TU) { if (!TU) return; // This is unexpected, give up quietly. @@ -2928,13 +2939,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); } // Install the logical handler.