Author: Utkarsh Saxena Date: 2026-02-23T12:55:42+01:00 New Revision: fd44b06a8dc3a3ef89d6c31268747d714f381610
URL: https://github.com/llvm/llvm-project/commit/fd44b06a8dc3a3ef89d6c31268747d714f381610 DIFF: https://github.com/llvm/llvm-project/commit/fd44b06a8dc3a3ef89d6c31268747d714f381610.diff LOG: [LifetimeSafety] Reorganize diagnostic groups and remove confidence-based warnings (#179309) Reorganized lifetime safety diagnostic groups to be more granular and renamed diagnostic messages for better clarity. - **Diagnostic Group Restructuring**: Split lifetime safety warnings into more specific categories: - `lifetime-safety-use-after-scope` and `lifetime-safety-use-after-scope-moved` for scope-related issues - `lifetime-safety-return-stack-addr` and `lifetime-safety-return-stack-addr-moved` for return address issues - `lifetime-safety-dangling-field` and `lifetime-safety-dangling-field-moved` for field reference issues - Added new umbrella groups `lifetime-safety-validations` and `lifetime-safety-all` - **Diagnostic Message Updates**: Renamed warning diagnostics from generic "loan expires" terminology to more specific messages like "use after scope" and "return stack addr" - **Code Cleanup**: Removed `Confidence` logic (based on possible vs guaranteed control-flow) from diagnostic reporting functions. Added TODO comment to deprecate the `Confidence` enum The new diagnostic group tree is: ``` lifetime-safety-all ├── lifetime-safety │ ├── lifetime-safety-permissive │ │ ├── lifetime-safety-use-after-scope │ │ ├── lifetime-safety-return-stack-addr │ │ └── lifetime-safety-dangling-field │ └── lifetime-safety-strict │ ├── lifetime-safety-use-after-scope-moved │ ├── lifetime-safety-return-stack-addr-moved │ ├── lifetime-safety-dangling-field-moved │ └── lifetime-safety-invalidation ├── lifetime-safety-suggestions │ ├── lifetime-safety-cross-tu-suggestions │ └── lifetime-safety-intra-tu-suggestions └── lifetime-safety-validations └── lifetime-safety-noescape ``` Added: Modified: clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h clang/include/clang/Basic/DiagnosticGroups.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/AnalysisBasedWarnings.cpp clang/test/Sema/warn-lifetime-safety.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 7761a5c24c606..d7aadf4cf04ca 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -33,6 +33,8 @@ namespace clang::lifetimes { +// TODO: Deprecate and remove Confidence as this is no more used as a +// diff erentiator between strict and permissive warnings. /// Enum to track the confidence level of a potential error. enum class Confidence : uint8_t { None, diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index aaf8275a78707..7df83d2a4011f 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -556,36 +556,65 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment, DanglingGsl, ReturnStackAddress]>; -def LifetimeSafetyDanglingField : DiagGroup<"lifetime-safety-dangling-field"> { +// Use-After-Scope +def LifetimeSafetyUseAfterScope : DiagGroup<"lifetime-safety-use-after-scope"> { + code Documentation = [{ +Warning to detect dangling references introduced by use-after-scope. + }]; +} +def LifetimeSafetyUseAfterScopeMoved : DiagGroup<"lifetime-safety-use-after-scope-moved"> { code Documentation = [{ - Warning to detect dangling field references. +Warning to detect dangling references introduced by use-after-scope. +This may contain false-positives, e.g. when the borrowed storage is potentially moved and is not destroyed at scope exit. }]; } -def LifetimeSafetyDanglingFieldStrict : DiagGroup<"lifetime-safety-dangling-field-strict"> { +// Return-Stack-Address (aka Use-After-Return) +def LifetimeSafetyReturnStackAddr : DiagGroup<"lifetime-safety-return-stack-addr"> { + code Documentation = [{ +Warning to detect use-after-return introduced by returning stack address. + }]; +} +def LifetimeSafetyReturnStackAddrMoved : DiagGroup<"lifetime-safety-return-stack-addr-moved"> { code Documentation = [{ - Warning to detect dangling field references. - This may contain false-positives, e.g. when the borrowed storage is potentially moved and is not destroyed at function exit. +Warning to detect use-after-return introduced by returning stack address. +This may contain false-positives, e.g. when the borrowed storage is potentially moved and is not destroyed at function exit. }]; } +// Dangling-Field (aka Escape-To-Field) +def LifetimeSafetyDanglingField : DiagGroup<"lifetime-safety-dangling-field"> { + code Documentation = [{ +Warning to detect dangling field references. + }]; +} +def LifetimeSafetyDanglingFieldMoved : DiagGroup<"lifetime-safety-dangling-field-moved"> { + code Documentation = [{ +Warning to detect dangling field references. +This may contain false-positives, e.g. when the borrowed storage is potentially moved and is not destroyed at function exit. + }]; +} def LifetimeSafetyInvalidation : DiagGroup<"lifetime-safety-invalidation"> { code Documentation = [{ - Warning to detect invalidation of references. +Warning to detect invalidation of references. }]; } def LifetimeSafetyPermissive : DiagGroup<"lifetime-safety-permissive", - [LifetimeSafetyDanglingField]>; + [LifetimeSafetyUseAfterScope, + LifetimeSafetyReturnStackAddr, + LifetimeSafetyDanglingField]>; def LifetimeSafetyStrict : DiagGroup<"lifetime-safety-strict", - [LifetimeSafetyDanglingFieldStrict, + [LifetimeSafetyUseAfterScopeMoved, + LifetimeSafetyReturnStackAddrMoved, + LifetimeSafetyDanglingFieldMoved, LifetimeSafetyInvalidation]>; def LifetimeSafety : DiagGroup<"lifetime-safety", [LifetimeSafetyPermissive, LifetimeSafetyStrict]> { code Documentation = [{ - Warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis. +Warnings to detect use-after-free and related temporal safety bugs based on lifetime safety analysis. }]; } @@ -598,13 +627,31 @@ def LifetimeSafetySuggestions [LifetimeSafetyCrossTUSuggestions, LifetimeSafetyIntraTUSuggestions]> { code Documentation = [{ - Lifetime annotation suggestions for function parameters that should be marked [[clang::lifetimebound]] based on lifetime analysis. +Lifetime annotation suggestions for function parameters that should be marked [[clang::lifetimebound]] based on lifetime analysis. }]; } + def LifetimeSafetyNoescape : DiagGroup<"lifetime-safety-noescape"> { code Documentation = [{ - Detects misuse of [[clang::noescape]] annotation where the parameter escapes (for example, through return). +Detects misuse of [[clang::noescape]] annotation where the parameter escapes (for example, through return). + }]; +} + +def LifetimeSafetyValidations : DiagGroup<"lifetime-safety-validations", + [LifetimeSafetyNoescape]> { + code Documentation = [{ +Verify function implementations adhere to the annotated lifetime contracts through lifetime safety +like verifying [[clang::noescape]] and [[clang::lifetimebound]] (upcoming). + }]; +} + +def LifetimeSafetyAll : DiagGroup<"lifetime-safety-all", + [LifetimeSafety, + LifetimeSafetySuggestions, + LifetimeSafetyValidations]> { + code Documentation = [{ +Turns on all the warnings in the lifetime-safety umbrella. }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5254003f0c21e..68016ec4d58a3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10897,26 +10897,23 @@ def warn_dangling_reference_captured_by_unknown : Warning< "the full-expression">, InGroup<DanglingCapture>; // Diagnostics based on the Lifetime safety analysis. -def warn_lifetime_safety_loan_expires_permissive : Warning< +def warn_lifetime_safety_use_after_scope : Warning< "object whose reference is captured does not live long enough">, - InGroup<LifetimeSafetyPermissive>, DefaultIgnore; -def warn_lifetime_safety_loan_expires_strict : Warning< - "object whose reference is captured may not live long enough">, - InGroup<LifetimeSafetyStrict>, DefaultIgnore; -def warn_lifetime_safety_loan_expires_moved_strict : Warning< + InGroup<LifetimeSafetyUseAfterScope>, DefaultIgnore; +def warn_lifetime_safety_use_after_scope_moved : Warning< "object whose reference is captured may not live long enough. " "This could be false positive as the storage may have been moved later">, - InGroup<LifetimeSafetyStrict>, DefaultIgnore; + InGroup<LifetimeSafetyUseAfterScopeMoved>, DefaultIgnore; -def warn_lifetime_safety_return_stack_addr_permissive +def warn_lifetime_safety_return_stack_addr : Warning<"address of stack memory is returned later">, - InGroup<LifetimeSafetyPermissive>, + InGroup<LifetimeSafetyReturnStackAddr>, DefaultIgnore; -def warn_lifetime_safety_return_stack_addr_moved_strict +def warn_lifetime_safety_return_stack_addr_moved : Warning<"address of stack memory may be returned later. " "This could be false positive as the storage may have been moved. " "Consider moving first and then aliasing later to resolve the issue">, - InGroup<LifetimeSafetyStrict>, + InGroup<LifetimeSafetyReturnStackAddrMoved>, DefaultIgnore; def warn_lifetime_safety_invalidation @@ -10932,7 +10929,7 @@ def warn_lifetime_safety_dangling_field_moved : Warning<"address of stack memory escapes to a field. " "This could be a false positive as the storage may have been moved. " "Consider moving first and then aliasing later to resolve the issue">, - InGroup<LifetimeSafetyDanglingFieldStrict>, + InGroup<LifetimeSafetyDanglingFieldMoved>, DefaultIgnore; def note_lifetime_safety_used_here : Note<"later used here">; diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 45adab94e07da..20c41096501fb 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2862,12 +2862,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportUseAfterFree(const Expr *IssueExpr, const Expr *UseExpr, const Expr *MovedExpr, SourceLocation FreeLoc, - Confidence C) override { + Confidence) override { S.Diag(IssueExpr->getExprLoc(), - MovedExpr ? diag::warn_lifetime_safety_loan_expires_moved_strict - : C == Confidence::Definite - ? diag::warn_lifetime_safety_loan_expires_permissive - : diag::warn_lifetime_safety_loan_expires_strict) + 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) @@ -2879,10 +2877,10 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { void reportUseAfterReturn(const Expr *IssueExpr, const Expr *ReturnExpr, const Expr *MovedExpr, SourceLocation ExpiryLoc, - Confidence C) override { + Confidence) override { S.Diag(IssueExpr->getExprLoc(), - MovedExpr ? diag::warn_lifetime_safety_return_stack_addr_moved_strict - : diag::warn_lifetime_safety_return_stack_addr_permissive) + 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) @@ -3148,17 +3146,14 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true; bool IsLifetimeSafetyDiagnosticEnabled = - !Diags.isIgnored(diag::warn_lifetime_safety_loan_expires_permissive, + !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope, D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_loan_expires_strict, + !Diags.isIgnored(diag::warn_lifetime_safety_use_after_scope_moved, D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_loan_expires_moved_strict, + !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr, D->getBeginLoc()) || - !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_permissive, + !Diags.isIgnored(diag::warn_lifetime_safety_return_stack_addr_moved, D->getBeginLoc()) || - !Diags.isIgnored( - diag::warn_lifetime_safety_return_stack_addr_moved_strict, - D->getBeginLoc()) || !Diags.isIgnored(diag::warn_lifetime_safety_invalidation, D->getBeginLoc()) || !Diags.isIgnored(diag::warn_lifetime_safety_noescape_escapes, diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 8cca9b3402366..097f3279d8e54 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -174,7 +174,7 @@ void potential_if_branch(bool cond) { MyObj* p = &safe; if (cond) { MyObj temp; - p = &temp; // expected-warning {{object whose reference is captured may not live long enough}} + p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} if (!cond) (void)*p; // expected-note {{later used here}} @@ -202,7 +202,7 @@ void definite_potential_together(bool cond) { if (cond) p_definite = &s; // expected-warning {{does not live long enough}} if (cond) - p_maybe = &s; // expected-warning {{may not live long enough}} + p_maybe = &s; // expected-warning {{does not live long enough}} } // expected-note 2 {{destroyed here}} (void)*p_definite; // expected-note {{later used here}} if (!cond) @@ -235,7 +235,7 @@ void potential_due_to_conditional_killing(bool cond) { MyObj* q; { MyObj s; - q = &s; // expected-warning {{may not live long enough}} + q = &s; // expected-warning {{does not live long enough}} } // expected-note {{destroyed here}} if (cond) { // 'q' is conditionally "rescued". 'p' is not. @@ -248,7 +248,7 @@ void potential_for_loop_use_after_loop_body(MyObj safe) { MyObj* p = &safe; for (int i = 0; i < 1; ++i) { MyObj s; - p = &s; // expected-warning {{may not live long enough}} + p = &s; // expected-warning {{does not live long enough}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } @@ -268,7 +268,7 @@ void potential_for_loop_gsl() { View v = safe; for (int i = 0; i < 1; ++i) { MyObj s; - v = s; // expected-warning {{object whose reference is captured may not live long enough}} + v = s; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } @@ -366,7 +366,7 @@ void potential_switch(int mode) { switch (mode) { case 1: { MyObj temp; - p = &temp; // expected-warning {{object whose reference is captured may not live long enough}} + p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} break; // expected-note {{destroyed here}} } case 2: { @@ -430,7 +430,7 @@ void loan_from_previous_iteration(MyObj safe, bool condition) { while (condition) { MyObj x; - p = &x; // expected-warning {{may not live long enough}} + p = &x; // expected-warning {{does not live long enough}} if (condition) q = p; @@ -994,7 +994,7 @@ void conditional_operator_one_unsafe_branch(bool cond) { MyObj* p = &safe; { MyObj temp; - p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}} + p = cond ? &temp // expected-warning {{object whose reference is captured does not live long enough}} : &safe; } // expected-note {{destroyed here}} @@ -1190,7 +1190,7 @@ void range_based_for_not_reference() { { MyObjStorage s; for (MyObj o : s) { // expected-note {{destroyed here}} - v = o; // expected-warning {{object whose reference is captured may not live long enough}} + v = o; // expected-warning {{object whose reference is captured does not live long enough}} } } v.use(); // expected-note {{later used here}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
