[llvm-branch-commits] [clang] Detect dangling fields (PR #177363)
Utkarsh Saxena via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Jan 22 05:58:39 PST 2026
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/177363
None
>From f508e9e405fe97de87cb85015f183dc14ba02109 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 21 Jan 2026 23:28:49 +0000
Subject: [PATCH] Detect dangling fields
---
.../Analysis/Analyses/LifetimeSafety/Facts.h | 47 +-
.../Analyses/LifetimeSafety/FactsGenerator.h | 4 +-
.../Analyses/LifetimeSafety/LifetimeSafety.h | 6 +-
clang/include/clang/Analysis/CFG.h | 5 +
.../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 | 40 +-
.../Analysis/LifetimeSafety/LiveOrigins.cpp | 9 +-
clang/lib/Analysis/LifetimeSafety/Origins.cpp | 14 +
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 | 110 +++
.../Sema/warn-lifetime-safety-dataflow.cpp | 2 +-
18 files changed, 602 insertions(+), 380 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..00c99a747c1d7 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/Facts.h
@@ -138,21 +138,60 @@ class OriginFlowFact : public Fact {
class OriginEscapesFact : public Fact {
OriginID OID;
- const Expr *EscapeExpr;
public:
+ enum class EscapeKind : uint8_t {
+ Return,
+ Field,
+ Global,
+ } 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; }
+};
+
+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;
+};
+
+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;
};
+class GlobalEscapeFact : public OriginEscapesFact {};
+
class UseFact : public Fact {
const Expr *UseExpr;
const OriginList *OList;
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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a2be7ab3791b9..43be07bd38a84 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<LifetimeSafetyPermissive>,
+ 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..874f9121ddd22 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,11 @@ void FactsGenerator::VisitCXXConstructExpr(const CXXConstructExpr *CCE) {
}
}
+void FactsGenerator::handleCXXCtorInitializer(const CXXCtorInitializer *CII) {
+ 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,7 +328,7 @@ 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));
}
}
@@ -341,6 +353,24 @@ void FactsGenerator::handleAssignment(const Expr *LHSExpr,
// from the source origin.
flow(LHSList->peelOuterOrigin(), RHSList, /*Kill=*/true);
}
+ if (const auto *ME_LHS =
+ dyn_cast<MemberExpr>(LHSExpr->IgnoreParenImpCasts())) {
+ OriginList *LHSList = getOriginsList(*ME_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);
+
+ // markUseAsWrite(ME_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 +493,14 @@ void FactsGenerator::handleTemporaryDtor(
}
}
+void FactsGenerator::handleExitBlock() {
+ 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..42d2984d26a9b 100644
--- a/clang/lib/Analysis/LifetimeSafety/Origins.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/Origins.cpp
@@ -138,6 +138,20 @@ OriginList *OriginManager::getOrCreateList(const Expr *E) {
QualType Type = E->getType();
+ if (auto *ME = dyn_cast<MemberExpr>(E)) {
+ const auto* Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ if (Field && isa<CXXThisExpr>(ME->getBase())) {
+ OriginList *Head = nullptr;
+ if (doesDeclHaveStorage(Field)) {
+ Head = createNode(ME, QualType{});
+ Head->setInnerOriginList(getOrCreateList(Field));
+ } else {
+ Head = getOrCreateList(Field);
+ }
+ return ExprToList[E] = Head;
+ }
+ }
+
// Special handling for DeclRefExpr to share origins with the underlying decl.
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
OriginList *Head = nullptr;
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..92375494a14fc
--- /dev/null
+++ b/clang/test/Sema/warn-lifetime-safety-dangling-field.cpp
@@ -0,0 +1,110 @@
+// 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 3 {{this field dangles}}
+ const char* p; // expected-note 3 {{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();
+ }
+};
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++)
More information about the llvm-branch-commits
mailing list