Author: Utkarsh Saxena Date: 2026-03-16T12:20:55+01:00 New Revision: 59db881282bbbc26c33b83f54669921509b75cce
URL: https://github.com/llvm/llvm-project/commit/59db881282bbbc26c33b83f54669921509b75cce DIFF: https://github.com/llvm/llvm-project/commit/59db881282bbbc26c33b83f54669921509b75cce.diff LOG: [LifetimeSafety] Extract Sema helper implementation to separate header (#186492) Improves code organization by separating lifetime safety Sema-specific functionality into its own header file. Added: clang/lib/Sema/SemaLifetimeSafety.h Modified: clang/lib/Sema/AnalysisBasedWarnings.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 2824bf61526b7..e2bd8d7956561 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/AnalysisBasedWarnings.h" +#include "SemaLifetimeSafety.h" #include "TypeLocBuilder.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -31,7 +32,6 @@ #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" #include "clang/Analysis/Analyses/CalledOnceCheck.h" #include "clang/Analysis/Analyses/Consumed.h" -#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h" #include "clang/Analysis/Analyses/ReachableCode.h" #include "clang/Analysis/Analyses/ThreadSafety.h" @@ -2872,205 +2872,6 @@ class CallableVisitor : public DynamicRecursiveASTVisitor { } }; -namespace clang::lifetimes { -namespace { -class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { - -public: - LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} - - void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *MovedExpr, - SourceLocation FreeLoc) override { - S.Diag(IssueExpr->getExprLoc(), - MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved - : diag::warn_lifetime_safety_use_after_scope) - << IssueExpr->getSourceRange(); - if (MovedExpr) - S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) - << MovedExpr->getSourceRange(); - S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here); - S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) - << UseExpr->getSourceRange(); - } - - void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, - const Expr *MovedExpr, - SourceLocation ExpiryLoc) override { - S.Diag(IssueExpr->getExprLoc(), - MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved - : diag::warn_lifetime_safety_return_stack_addr) - << IssueExpr->getSourceRange(); - if (MovedExpr) - S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) - << MovedExpr->getSourceRange(); - S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here) - << ReturnExpr->getSourceRange(); - } - - void reportDanglingField(const Expr *IssueExpr, - const FieldDecl *DanglingField, - const Expr *MovedExpr, - SourceLocation ExpiryLoc) override { - S.Diag(IssueExpr->getExprLoc(), - MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved - : diag::warn_lifetime_safety_dangling_field) - << IssueExpr->getSourceRange(); - if (MovedExpr) - S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) - << MovedExpr->getSourceRange(); - S.Diag(DanglingField->getLocation(), - diag::note_lifetime_safety_dangling_field_here) - << DanglingField->getEndLoc(); - } - - void reportDanglingGlobal(const Expr *IssueExpr, - const VarDecl *DanglingGlobal, - const Expr *MovedExpr, - SourceLocation ExpiryLoc) override { - S.Diag(IssueExpr->getExprLoc(), - MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved - : diag::warn_lifetime_safety_dangling_global) - << IssueExpr->getSourceRange(); - if (MovedExpr) - S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) - << MovedExpr->getSourceRange(); - if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember()) - S.Diag(DanglingGlobal->getLocation(), - diag::note_lifetime_safety_dangling_static_here) - << DanglingGlobal->getEndLoc(); - else - S.Diag(DanglingGlobal->getLocation(), - diag::note_lifetime_safety_dangling_global_here) - << DanglingGlobal->getEndLoc(); - } - - void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, - const Expr *InvalidationExpr) override { - S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_invalidation) - << false << IssueExpr->getSourceRange(); - S.Diag(InvalidationExpr->getExprLoc(), - diag::note_lifetime_safety_invalidated_here) - << InvalidationExpr->getSourceRange(); - S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) - << UseExpr->getSourceRange(); - } - void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, - const Expr *InvalidationExpr) override { - S.Diag(PVD->getSourceRange().getBegin(), - diag::warn_lifetime_safety_invalidation) - << true << PVD->getSourceRange(); - S.Diag(InvalidationExpr->getExprLoc(), - diag::note_lifetime_safety_invalidated_here) - << InvalidationExpr->getSourceRange(); - S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) - << UseExpr->getSourceRange(); - } - - void suggestLifetimeboundToParmVar(SuggestionScope Scope, - const ParmVarDecl *ParmToAnnotate, - const Expr *EscapeExpr) override { - unsigned DiagID = - (Scope == SuggestionScope::CrossTU) - ? diag::warn_lifetime_safety_cross_tu_param_suggestion - : diag::warn_lifetime_safety_intra_tu_param_suggestion; - SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( - ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); - StringRef FixItText = " [[clang::lifetimebound]]"; - if (!ParmToAnnotate->getIdentifier()) { - // For unnamed parameters, placing attributes after the type would be - // parsed as a type attribute, not a parameter attribute. - InsertionPoint = ParmToAnnotate->getBeginLoc(); - FixItText = "[[clang::lifetimebound]] "; - } - S.Diag(ParmToAnnotate->getBeginLoc(), DiagID) - << ParmToAnnotate->getSourceRange() - << FixItHint::CreateInsertion(InsertionPoint, FixItText); - S.Diag(EscapeExpr->getBeginLoc(), - diag::note_lifetime_safety_suggestion_returned_here) - << EscapeExpr->getSourceRange(); - } - - void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, - const CXXMethodDecl *MD, - const Expr *EscapeExpr) override { - unsigned DiagID = (Scope == SuggestionScope::CrossTU) - ? diag::warn_lifetime_safety_cross_tu_this_suggestion - : diag::warn_lifetime_safety_intra_tu_this_suggestion; - const auto MDL = MD->getTypeSourceInfo()->getTypeLoc(); - SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( - MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); - if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>(); - FPT && FPT->hasTrailingReturn()) { - // For trailing return types, 'getEndLoc()' includes the return type - // after '->', placing the attribute in an invalid position. - // Instead use 'getLocalRangeEnd()' which gives the '->' location - // for trailing returns, so find the last token before it. - const auto FTL = MDL.getAs<FunctionTypeLoc>(); - assert(FTL); - InsertionPoint = Lexer::getLocForEndOfToken( - Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(), - S.getLangOpts(), - /*IncludeComments=*/false) - ->getLocation(), - 0, S.getSourceManager(), S.getLangOpts()); - } - S.Diag(InsertionPoint, DiagID) - << MD->getNameInfo().getSourceRange() - << FixItHint::CreateInsertion(InsertionPoint, - " [[clang::lifetimebound]]"); - S.Diag(EscapeExpr->getBeginLoc(), - diag::note_lifetime_safety_suggestion_returned_here) - << EscapeExpr->getSourceRange(); - } - - void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, - const Expr *EscapeExpr) override { - S.Diag(ParmWithNoescape->getBeginLoc(), - diag::warn_lifetime_safety_noescape_escapes) - << ParmWithNoescape->getSourceRange(); - - S.Diag(EscapeExpr->getBeginLoc(), - diag::note_lifetime_safety_suggestion_returned_here) - << EscapeExpr->getSourceRange(); - } - - void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, - const FieldDecl *EscapeField) override { - S.Diag(ParmWithNoescape->getBeginLoc(), - diag::warn_lifetime_safety_noescape_escapes) - << ParmWithNoescape->getSourceRange(); - - S.Diag(EscapeField->getLocation(), - diag::note_lifetime_safety_escapes_to_field_here) - << EscapeField->getEndLoc(); - } - - void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, - const VarDecl *EscapeGlobal) override { - S.Diag(ParmWithNoescape->getBeginLoc(), - diag::warn_lifetime_safety_noescape_escapes) - << ParmWithNoescape->getSourceRange(); - if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember()) - S.Diag(EscapeGlobal->getLocation(), - diag::note_lifetime_safety_escapes_to_static_storage_here) - << EscapeGlobal->getEndLoc(); - else - S.Diag(EscapeGlobal->getLocation(), - diag::note_lifetime_safety_escapes_to_global_here) - << EscapeGlobal->getEndLoc(); - } - - void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override { - S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD)); - } - -private: - Sema &S; -}; -} // namespace -} // namespace clang::lifetimes - static void LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU, clang::lifetimes::LifetimeSafetyStats &LSStats) { @@ -3201,23 +3002,10 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFGBuildOptions().AddCXXNewAllocator = false; AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true; - bool IsLifetimeSafetyDiagnosticEnabled = - !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope, - D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope_moved, - D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr, - D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_moved, - D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_invalidation, - D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_noescape_escapes, - D->getBeginLoc()); bool EnableLifetimeSafetyAnalysis = S.getLangOpts().EnableLifetimeSafety && !S.getLangOpts().EnableLifetimeSafetyTUAnalysis && - IsLifetimeSafetyDiagnosticEnabled; + lifetimes::IsLifetimeSafetyDiagnosticEnabled(S, D); // Force that certain expressions appear as CFGElements in the CFG. This // is used to speed up various analyses. diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h new file mode 100644 index 0000000000000..e6f7e3d929f61 --- /dev/null +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -0,0 +1,238 @@ +//===--- SemaLifetimeSafety.h - Sema support for lifetime safety =---------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the Sema-specific implementation for lifetime safety +// analysis. It provides diagnostic reporting and helper functions that bridge +// the lifetime safety analysis framework with Sema's diagnostic engine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H +#define LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H + +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/Sema.h" + +namespace clang::lifetimes { + +inline bool IsLifetimeSafetyDiagnosticEnabled(Sema &S, const Decl *D) { + DiagnosticsEngine &Diags = S.getDiagnostics(); + return !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope, + D->getBeginLoc()) || + !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope_moved, + D->getBeginLoc()) || + !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr, + D->getBeginLoc()) || + !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_moved, + D->getBeginLoc()) || + !Diags.isIgnored(diag::warn_lifetime_safety_invalidation, + D->getBeginLoc()) || + !Diags.isIgnored(diag::warn_lifetime_safety_noescape_escapes, + D->getBeginLoc()); +} + +class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { + +public: + LifetimeSafetySemaHelperImpl(Sema &S) : S(S) {} + + void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, + const Expr *MovedExpr, + SourceLocation FreeLoc) override { + S.Diag(IssueExpr->getExprLoc(), + MovedExpr ? diag::warn_lifetime_safety_use_after_scope_moved + : diag::warn_lifetime_safety_use_after_scope) + << IssueExpr->getSourceRange(); + if (MovedExpr) + S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) + << MovedExpr->getSourceRange(); + S.Diag(FreeLoc, diag::note_lifetime_safety_destroyed_here); + S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) + << UseExpr->getSourceRange(); + } + + void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, + const Expr *MovedExpr, + SourceLocation ExpiryLoc) override { + S.Diag(IssueExpr->getExprLoc(), + MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved + : diag::warn_lifetime_safety_return_stack_addr) + << IssueExpr->getSourceRange(); + if (MovedExpr) + S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) + << MovedExpr->getSourceRange(); + S.Diag(ReturnExpr->getExprLoc(), diag::note_lifetime_safety_returned_here) + << ReturnExpr->getSourceRange(); + } + + void reportDanglingField(const Expr *IssueExpr, + const FieldDecl *DanglingField, + const Expr *MovedExpr, + SourceLocation ExpiryLoc) override { + S.Diag(IssueExpr->getExprLoc(), + MovedExpr ? diag::warn_lifetime_safety_dangling_field_moved + : diag::warn_lifetime_safety_dangling_field) + << IssueExpr->getSourceRange(); + if (MovedExpr) + S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) + << MovedExpr->getSourceRange(); + S.Diag(DanglingField->getLocation(), + diag::note_lifetime_safety_dangling_field_here) + << DanglingField->getEndLoc(); + } + + void reportDanglingGlobal(const Expr *IssueExpr, + const VarDecl *DanglingGlobal, + const Expr *MovedExpr, + SourceLocation ExpiryLoc) override { + S.Diag(IssueExpr->getExprLoc(), + MovedExpr ? diag::warn_lifetime_safety_dangling_global_moved + : diag::warn_lifetime_safety_dangling_global) + << IssueExpr->getSourceRange(); + if (MovedExpr) + S.Diag(MovedExpr->getExprLoc(), diag::note_lifetime_safety_moved_here) + << MovedExpr->getSourceRange(); + if (DanglingGlobal->isStaticLocal() || DanglingGlobal->isStaticDataMember()) + S.Diag(DanglingGlobal->getLocation(), + diag::note_lifetime_safety_dangling_static_here) + << DanglingGlobal->getEndLoc(); + else + S.Diag(DanglingGlobal->getLocation(), + diag::note_lifetime_safety_dangling_global_here) + << DanglingGlobal->getEndLoc(); + } + + void reportUseAfterInvalidation(const Expr *IssueExpr, const Expr *UseExpr, + const Expr *InvalidationExpr) override { + S.Diag(IssueExpr->getExprLoc(), diag::warn_lifetime_safety_invalidation) + << false << IssueExpr->getSourceRange(); + S.Diag(InvalidationExpr->getExprLoc(), + diag::note_lifetime_safety_invalidated_here) + << InvalidationExpr->getSourceRange(); + S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) + << UseExpr->getSourceRange(); + } + void reportUseAfterInvalidation(const ParmVarDecl *PVD, const Expr *UseExpr, + const Expr *InvalidationExpr) override { + S.Diag(PVD->getSourceRange().getBegin(), + diag::warn_lifetime_safety_invalidation) + << true << PVD->getSourceRange(); + S.Diag(InvalidationExpr->getExprLoc(), + diag::note_lifetime_safety_invalidated_here) + << InvalidationExpr->getSourceRange(); + S.Diag(UseExpr->getExprLoc(), diag::note_lifetime_safety_used_here) + << UseExpr->getSourceRange(); + } + + void suggestLifetimeboundToParmVar(SuggestionScope Scope, + const ParmVarDecl *ParmToAnnotate, + const Expr *EscapeExpr) override { + unsigned DiagID = + (Scope == SuggestionScope::CrossTU) + ? diag::warn_lifetime_safety_cross_tu_param_suggestion + : diag::warn_lifetime_safety_intra_tu_param_suggestion; + SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( + ParmToAnnotate->getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); + StringRef FixItText = " [[clang::lifetimebound]]"; + if (!ParmToAnnotate->getIdentifier()) { + // For unnamed parameters, placing attributes after the type would be + // parsed as a type attribute, not a parameter attribute. + InsertionPoint = ParmToAnnotate->getBeginLoc(); + FixItText = "[[clang::lifetimebound]] "; + } + S.Diag(ParmToAnnotate->getBeginLoc(), DiagID) + << ParmToAnnotate->getSourceRange() + << FixItHint::CreateInsertion(InsertionPoint, FixItText); + S.Diag(EscapeExpr->getBeginLoc(), + diag::note_lifetime_safety_suggestion_returned_here) + << EscapeExpr->getSourceRange(); + } + + void suggestLifetimeboundToImplicitThis(SuggestionScope Scope, + const CXXMethodDecl *MD, + const Expr *EscapeExpr) override { + unsigned DiagID = (Scope == SuggestionScope::CrossTU) + ? diag::warn_lifetime_safety_cross_tu_this_suggestion + : diag::warn_lifetime_safety_intra_tu_this_suggestion; + const auto MDL = MD->getTypeSourceInfo()->getTypeLoc(); + SourceLocation InsertionPoint = Lexer::getLocForEndOfToken( + MDL.getEndLoc(), 0, S.getSourceManager(), S.getLangOpts()); + if (const auto *FPT = MD->getType()->getAs<FunctionProtoType>(); + FPT && FPT->hasTrailingReturn()) { + // For trailing return types, 'getEndLoc()' includes the return type + // after '->', placing the attribute in an invalid position. + // Instead use 'getLocalRangeEnd()' which gives the '->' location + // for trailing returns, so find the last token before it. + const auto FTL = MDL.getAs<FunctionTypeLoc>(); + assert(FTL); + InsertionPoint = Lexer::getLocForEndOfToken( + Lexer::findPreviousToken(FTL.getLocalRangeEnd(), S.getSourceManager(), + S.getLangOpts(), + /*IncludeComments=*/false) + ->getLocation(), + 0, S.getSourceManager(), S.getLangOpts()); + } + S.Diag(InsertionPoint, DiagID) + << MD->getNameInfo().getSourceRange() + << FixItHint::CreateInsertion(InsertionPoint, + " [[clang::lifetimebound]]"); + S.Diag(EscapeExpr->getBeginLoc(), + diag::note_lifetime_safety_suggestion_returned_here) + << EscapeExpr->getSourceRange(); + } + + void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, + const Expr *EscapeExpr) override { + S.Diag(ParmWithNoescape->getBeginLoc(), + diag::warn_lifetime_safety_noescape_escapes) + << ParmWithNoescape->getSourceRange(); + + S.Diag(EscapeExpr->getBeginLoc(), + diag::note_lifetime_safety_suggestion_returned_here) + << EscapeExpr->getSourceRange(); + } + + void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, + const FieldDecl *EscapeField) override { + S.Diag(ParmWithNoescape->getBeginLoc(), + diag::warn_lifetime_safety_noescape_escapes) + << ParmWithNoescape->getSourceRange(); + + S.Diag(EscapeField->getLocation(), + diag::note_lifetime_safety_escapes_to_field_here) + << EscapeField->getEndLoc(); + } + + void reportNoescapeViolation(const ParmVarDecl *ParmWithNoescape, + const VarDecl *EscapeGlobal) override { + S.Diag(ParmWithNoescape->getBeginLoc(), + diag::warn_lifetime_safety_noescape_escapes) + << ParmWithNoescape->getSourceRange(); + if (EscapeGlobal->isStaticLocal() || EscapeGlobal->isStaticDataMember()) + S.Diag(EscapeGlobal->getLocation(), + diag::note_lifetime_safety_escapes_to_static_storage_here) + << EscapeGlobal->getEndLoc(); + else + S.Diag(EscapeGlobal->getLocation(), + diag::note_lifetime_safety_escapes_to_global_here) + << EscapeGlobal->getEndLoc(); + } + + void addLifetimeBoundToImplicitThis(const CXXMethodDecl *MD) override { + S.addLifetimeBoundToImplicitThis(const_cast<CXXMethodDecl *>(MD)); + } + +private: + Sema &S; +}; + +} // namespace clang::lifetimes + +#endif // LLVM_CLANG_LIB_SEMA_SEMALIFETIMESAFETY_H _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
