diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7417fdd71a392..181b3797be9e3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -395,6 +395,9 @@ Improvements to Clang's diagnostics constructors to initialize their non-modifiable members. The diagnostic is not new; being controlled via a warning group is what's new. Fixes #GH41104 +- Analysis-based diagnostics (like ``-Wconsumed`` or ``-Wunreachable-code``) + can now be correctly controlled by ``#pragma clang diagnostic``. #GH42199 + - Improved bit-field diagnostics to consider the type specified by the ``preferred_type`` attribute. These diagnostics are controlled by the flags ``-Wpreferred-type-bitfield-enum-conversion`` and @@ -402,7 +405,6 @@ Improvements to Clang's diagnostics they're only triggered if the authors are already making the choice to use ``preferred_type`` attribute. - Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Sema/AnalysisBasedWarnings.h b/clang/include/clang/Sema/AnalysisBasedWarnings.h index aafe227b84084..4103c3f006a8f 100644 --- a/clang/include/clang/Sema/AnalysisBasedWarnings.h +++ b/clang/include/clang/Sema/AnalysisBasedWarnings.h @@ -25,6 +25,7 @@ class QualType; class Sema; namespace sema { class FunctionScopeInfo; + class SemaPPCallbacks; } namespace sema { @@ -33,6 +34,7 @@ class AnalysisBasedWarnings { public: class Policy { friend class AnalysisBasedWarnings; + friend class SemaPPCallbacks; // The warnings to run. LLVM_PREFERRED_TYPE(bool) unsigned enableCheckFallThrough : 1; @@ -49,7 +51,6 @@ class AnalysisBasedWarnings { private: Sema &S; - Policy DefaultPolicy; class InterProceduralData; std::unique_ptr IPData; @@ -57,6 +58,9 @@ class AnalysisBasedWarnings { enum VisitFlag { NotVisited = 0, Visited = 1, Pending = 2 }; llvm::DenseMap VisitedFD; + Policy PolicyOverrides; + void clearOverrides(); + /// \name Statistics /// @{ @@ -103,7 +107,13 @@ class AnalysisBasedWarnings { // Issue warnings that require whole-translation-unit analysis. void IssueWarnings(TranslationUnitDecl *D); - Policy getDefaultPolicy() { return DefaultPolicy; } + // Gets the default policy which is in effect at the given source location. + Policy getPolicyInEffectAt(SourceLocation Loc); + + // Get the policies we may want to override due to things like #pragma clang + // diagnostic handling. If a caller sets any of these policies to true, that + // will override the policy used to issue warnings. + Policy &getPolicyOverrides() { return PolicyOverrides; } void PrintStats() const; }; diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 32c7ee92466ad..3d8eaf035186b 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2492,9 +2492,11 @@ class sema::AnalysisBasedWarnings::InterProceduralData { CalledOnceInterProceduralData CalledOnceData; }; -static unsigned isEnabled(DiagnosticsEngine &D, unsigned diag) { - return (unsigned)!D.isIgnored(diag, SourceLocation()); -} +template +static bool areAnyEnabled(DiagnosticsEngine &D, SourceLocation Loc, + Ts... Diags) { + return (!D.isIgnored(Diags, Loc) || ...); +}; sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s), IPData(std::make_unique()), @@ -2503,23 +2505,37 @@ sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) NumUninitAnalysisVariables(0), MaxUninitAnalysisVariablesPerFunction(0), NumUninitAnalysisBlockVisits(0), MaxUninitAnalysisBlockVisitsPerFunction(0) { +} + +// We need this here for unique_ptr with forward declared class. +sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default; +sema::AnalysisBasedWarnings::Policy +sema::AnalysisBasedWarnings::getPolicyInEffectAt(SourceLocation Loc) { using namespace diag; DiagnosticsEngine &D = S.getDiagnostics(); + Policy P; - DefaultPolicy.enableCheckUnreachable = - isEnabled(D, warn_unreachable) || isEnabled(D, warn_unreachable_break) || - isEnabled(D, warn_unreachable_return) || - isEnabled(D, warn_unreachable_loop_increment); + // Note: The enabled checks should be kept in sync with the switch in + // SemaPPCallbacks::PragmaDiagnostic(). + P.enableCheckUnreachable = + PolicyOverrides.enableCheckUnreachable || + areAnyEnabled(D, Loc, warn_unreachable, warn_unreachable_break, + warn_unreachable_return, warn_unreachable_loop_increment); - DefaultPolicy.enableThreadSafetyAnalysis = isEnabled(D, warn_double_lock); + P.enableThreadSafetyAnalysis = PolicyOverrides.enableThreadSafetyAnalysis || + areAnyEnabled(D, Loc, warn_double_lock); - DefaultPolicy.enableConsumedAnalysis = - isEnabled(D, warn_use_in_invalid_state); + P.enableConsumedAnalysis = PolicyOverrides.enableConsumedAnalysis || + areAnyEnabled(D, Loc, warn_use_in_invalid_state); + return P; } -// We need this here for unique_ptr with forward declared class. -sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default; +void sema::AnalysisBasedWarnings::clearOverrides() { + PolicyOverrides.enableCheckUnreachable = false; + PolicyOverrides.enableConsumedAnalysis = false; + PolicyOverrides.enableThreadSafetyAnalysis = false; +} static void flushDiagnostics(Sema &S, const sema::FunctionScopeInfo *fscope) { for (const auto &D : fscope->PossiblyUnreachableDiags) @@ -2870,6 +2886,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFG(); } + // Clear any of our policy overrides. + clearOverrides(); + // Collect statistics about the CFG if it was built. if (S.CollectStats && AC.isCFGBuilt()) { ++NumFunctionsAnalyzed; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index d2da9cd1201c2..4039601612c62 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -202,6 +202,43 @@ class SemaPPCallbacks : public PPCallbacks { break; } } + void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, + diag::Severity Mapping, StringRef Str) override { + // If one of the analysis-based diagnostics was enabled while processing + // a function, we want to note it in the analysis-based warnings so they + // can be run at the end of the function body even if the analysis warnings + // are disabled at that point. + SmallVector GroupDiags; + diag::Flavor Flavor = + Str[1] == 'W' ? diag::Flavor::WarningOrError : diag::Flavor::Remark; + StringRef Group = Str.substr(2); + + if (S->PP.getDiagnostics().getDiagnosticIDs()->getDiagnosticsInGroup( + Flavor, Group, GroupDiags)) + return; + + for (diag::kind K : GroupDiags) { + // Note: the cases in this switch should be kept in sync with the + // diagnostics in AnalysisBasedWarnings::getPolicyInEffectAt(). + AnalysisBasedWarnings::Policy &Override = + S->AnalysisWarnings.getPolicyOverrides(); + switch (K) { + default: break; + case diag::warn_unreachable: + case diag::warn_unreachable_break: + case diag::warn_unreachable_return: + case diag::warn_unreachable_loop_increment: + Override.enableCheckUnreachable = true; + break; + case diag::warn_double_lock: + Override.enableThreadSafetyAnalysis = true; + break; + case diag::warn_use_in_invalid_state: + Override.enableConsumedAnalysis = true; + break; + } + } + } }; } // end namespace sema diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 46933c5c43168..d28a2107d58a9 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16150,7 +16150,13 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FSI->UsesFPIntrin && FD && !FD->hasAttr()) FD->addAttr(StrictFPAttr::CreateImplicit(Context)); - sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); + SourceLocation AnalysisLoc; + if (Body) + AnalysisLoc = Body->getEndLoc(); + else if (FD) + AnalysisLoc = FD->getEndLoc(); + sema::AnalysisBasedWarnings::Policy WP = + AnalysisWarnings.getPolicyInEffectAt(AnalysisLoc); sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr; // If we skip function body, we can't tell if a function is a coroutine. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 01a021443c94f..2e6ce17f8bf91 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -16597,7 +16597,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); // Pop the block scope now but keep it alive to the end of this function. - AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy(); + AnalysisBasedWarnings::Policy WP = + AnalysisWarnings.getPolicyInEffectAt(Body->getEndLoc()); PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy); BlockExpr *Result = new (Context) diff --git a/clang/test/Analysis/pragma-diag-control.cpp b/clang/test/Analysis/pragma-diag-control.cpp new file mode 100644 index 0000000000000..470960c030d0f --- /dev/null +++ b/clang/test/Analysis/pragma-diag-control.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Werror=unreachable-code-aggressive %s + +// Test that analysis-based warnings honor #pragma diagnostic controls. + +struct [[clang::consumable(unconsumed)]] Linear { + [[clang::return_typestate(unconsumed)]] + Linear() {} + [[clang::callable_when(consumed)]] + ~Linear() {} +}; + +int a() { + Linear l; + return 0; // No -Wconsumed diagnostic, analysis is not enabled. + return 1; // expected-error {{'return' will never be executed}} +} + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconsumed" +int b() { + Linear l; + return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}} + return 1; // expected-error {{'return' will never be executed}} +} +#pragma clang diagnostic pop + +int c() { +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconsumed" + Linear l; + return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}} + return 1; // expected-error {{'return' will never be executed}} +#pragma clang diagnostic pop +} + +int d() { +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconsumed" +#pragma clang diagnostic ignored "-Wunreachable-code-aggressive" + Linear l; + return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}} + return 1; // Diagnostic is ignored +} +#pragma clang diagnostic pop + +int e() { +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconsumed" +#pragma clang diagnostic ignored "-Wunreachable-code-aggressive" + Linear l; + return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}} + return 1; // Diagnostic is ignored +#pragma clang diagnostic pop +} + +int f() { + Linear l; + return 0; // No -Wconsumed diagnostic, analysis is not enabled + return 1; // expected-error {{'return' will never be executed}} +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code-aggressive" +} +#pragma clang diagnostic pop + +int g() { + Linear l; + return 0; // No -Wconsumed diagnostic, the diagnostic generated at } is not enabled on this line. + return 1; // expected-error {{'return' will never be executed}} +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wconsumed" +} +#pragma clang diagnostic pop + +int h() { +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wconsumed" +#pragma clang diagnostic ignored "-Wunreachable-code-aggressive" +#pragma clang diagnostic pop + + Linear l; + return 0; // No -Wconsumed diagnostic, the diagnostic generated at } is not enabled on this line. + return 1; // expected-error {{'return' will never be executed}} +}