Skip to content

Commit 71ce9e2

Browse files
authored
Control analysis-based diagnostics with #pragma (#136323)
Previously, analysis-based diagnostics (like -Wconsumed) had to be enabled at file scope in order to be run at the end of each function body. This meant that they did not respect #pragma clang diagnostic enabling or disabling the diagnostic. Now, these pragmas can control the diagnostic emission. Fixes #42199
1 parent 2a9f77f commit 71ce9e2

File tree

7 files changed

+174
-16
lines changed

7 files changed

+174
-16
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ Improvements to Clang's diagnostics
398398
constructors to initialize their non-modifiable members. The diagnostic is
399399
not new; being controlled via a warning group is what's new. Fixes #GH41104
400400

401+
- Analysis-based diagnostics (like ``-Wconsumed`` or ``-Wunreachable-code``)
402+
can now be correctly controlled by ``#pragma clang diagnostic``. #GH42199
401403

402404
- Improved Clang's error recovery for invalid function calls.
403405

clang/include/clang/Sema/AnalysisBasedWarnings.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class QualType;
2525
class Sema;
2626
namespace sema {
2727
class FunctionScopeInfo;
28+
class SemaPPCallbacks;
2829
}
2930

3031
namespace sema {
@@ -33,6 +34,7 @@ class AnalysisBasedWarnings {
3334
public:
3435
class Policy {
3536
friend class AnalysisBasedWarnings;
37+
friend class SemaPPCallbacks;
3638
// The warnings to run.
3739
LLVM_PREFERRED_TYPE(bool)
3840
unsigned enableCheckFallThrough : 1;
@@ -49,14 +51,16 @@ class AnalysisBasedWarnings {
4951

5052
private:
5153
Sema &S;
52-
Policy DefaultPolicy;
5354

5455
class InterProceduralData;
5556
std::unique_ptr<InterProceduralData> IPData;
5657

5758
enum VisitFlag { NotVisited = 0, Visited = 1, Pending = 2 };
5859
llvm::DenseMap<const FunctionDecl*, VisitFlag> VisitedFD;
5960

61+
Policy PolicyOverrides;
62+
void clearOverrides();
63+
6064
/// \name Statistics
6165
/// @{
6266

@@ -103,7 +107,13 @@ class AnalysisBasedWarnings {
103107
// Issue warnings that require whole-translation-unit analysis.
104108
void IssueWarnings(TranslationUnitDecl *D);
105109

106-
Policy getDefaultPolicy() { return DefaultPolicy; }
110+
// Gets the default policy which is in effect at the given source location.
111+
Policy getPolicyInEffectAt(SourceLocation Loc);
112+
113+
// Get the policies we may want to override due to things like #pragma clang
114+
// diagnostic handling. If a caller sets any of these policies to true, that
115+
// will override the policy used to issue warnings.
116+
Policy &getPolicyOverrides() { return PolicyOverrides; }
107117

108118
void PrintStats() const;
109119
};

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,9 +2492,11 @@ class sema::AnalysisBasedWarnings::InterProceduralData {
24922492
CalledOnceInterProceduralData CalledOnceData;
24932493
};
24942494

2495-
static unsigned isEnabled(DiagnosticsEngine &D, unsigned diag) {
2496-
return (unsigned)!D.isIgnored(diag, SourceLocation());
2497-
}
2495+
template <typename... Ts>
2496+
static bool areAnyEnabled(DiagnosticsEngine &D, SourceLocation Loc,
2497+
Ts... Diags) {
2498+
return (!D.isIgnored(Diags, Loc) || ...);
2499+
};
24982500

24992501
sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
25002502
: S(s), IPData(std::make_unique<InterProceduralData>()),
@@ -2503,23 +2505,37 @@ sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
25032505
NumUninitAnalysisVariables(0), MaxUninitAnalysisVariablesPerFunction(0),
25042506
NumUninitAnalysisBlockVisits(0),
25052507
MaxUninitAnalysisBlockVisitsPerFunction(0) {
2508+
}
2509+
2510+
// We need this here for unique_ptr with forward declared class.
2511+
sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default;
25062512

2513+
sema::AnalysisBasedWarnings::Policy
2514+
sema::AnalysisBasedWarnings::getPolicyInEffectAt(SourceLocation Loc) {
25072515
using namespace diag;
25082516
DiagnosticsEngine &D = S.getDiagnostics();
2517+
Policy P;
25092518

2510-
DefaultPolicy.enableCheckUnreachable =
2511-
isEnabled(D, warn_unreachable) || isEnabled(D, warn_unreachable_break) ||
2512-
isEnabled(D, warn_unreachable_return) ||
2513-
isEnabled(D, warn_unreachable_loop_increment);
2519+
// Note: The enabled checks should be kept in sync with the switch in
2520+
// SemaPPCallbacks::PragmaDiagnostic().
2521+
P.enableCheckUnreachable =
2522+
PolicyOverrides.enableCheckUnreachable ||
2523+
areAnyEnabled(D, Loc, warn_unreachable, warn_unreachable_break,
2524+
warn_unreachable_return, warn_unreachable_loop_increment);
25142525

2515-
DefaultPolicy.enableThreadSafetyAnalysis = isEnabled(D, warn_double_lock);
2526+
P.enableThreadSafetyAnalysis = PolicyOverrides.enableThreadSafetyAnalysis ||
2527+
areAnyEnabled(D, Loc, warn_double_lock);
25162528

2517-
DefaultPolicy.enableConsumedAnalysis =
2518-
isEnabled(D, warn_use_in_invalid_state);
2529+
P.enableConsumedAnalysis = PolicyOverrides.enableConsumedAnalysis ||
2530+
areAnyEnabled(D, Loc, warn_use_in_invalid_state);
2531+
return P;
25192532
}
25202533

2521-
// We need this here for unique_ptr with forward declared class.
2522-
sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default;
2534+
void sema::AnalysisBasedWarnings::clearOverrides() {
2535+
PolicyOverrides.enableCheckUnreachable = false;
2536+
PolicyOverrides.enableConsumedAnalysis = false;
2537+
PolicyOverrides.enableThreadSafetyAnalysis = false;
2538+
}
25232539

25242540
static void flushDiagnostics(Sema &S, const sema::FunctionScopeInfo *fscope) {
25252541
for (const auto &D : fscope->PossiblyUnreachableDiags)
@@ -2870,6 +2886,9 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
28702886
AC.getCFG();
28712887
}
28722888

2889+
// Clear any of our policy overrides.
2890+
clearOverrides();
2891+
28732892
// Collect statistics about the CFG if it was built.
28742893
if (S.CollectStats && AC.isCFGBuilt()) {
28752894
++NumFunctionsAnalyzed;

clang/lib/Sema/Sema.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,43 @@ class SemaPPCallbacks : public PPCallbacks {
202202
break;
203203
}
204204
}
205+
void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace,
206+
diag::Severity Mapping, StringRef Str) override {
207+
// If one of the analysis-based diagnostics was enabled while processing
208+
// a function, we want to note it in the analysis-based warnings so they
209+
// can be run at the end of the function body even if the analysis warnings
210+
// are disabled at that point.
211+
SmallVector<diag::kind, 256> GroupDiags;
212+
diag::Flavor Flavor =
213+
Str[1] == 'W' ? diag::Flavor::WarningOrError : diag::Flavor::Remark;
214+
StringRef Group = Str.substr(2);
215+
216+
if (S->PP.getDiagnostics().getDiagnosticIDs()->getDiagnosticsInGroup(
217+
Flavor, Group, GroupDiags))
218+
return;
219+
220+
for (diag::kind K : GroupDiags) {
221+
// Note: the cases in this switch should be kept in sync with the
222+
// diagnostics in AnalysisBasedWarnings::getPolicyInEffectAt().
223+
AnalysisBasedWarnings::Policy &Override =
224+
S->AnalysisWarnings.getPolicyOverrides();
225+
switch (K) {
226+
default: break;
227+
case diag::warn_unreachable:
228+
case diag::warn_unreachable_break:
229+
case diag::warn_unreachable_return:
230+
case diag::warn_unreachable_loop_increment:
231+
Override.enableCheckUnreachable = true;
232+
break;
233+
case diag::warn_double_lock:
234+
Override.enableThreadSafetyAnalysis = true;
235+
break;
236+
case diag::warn_use_in_invalid_state:
237+
Override.enableConsumedAnalysis = true;
238+
break;
239+
}
240+
}
241+
}
205242
};
206243

207244
} // end namespace sema

clang/lib/Sema/SemaDecl.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16150,7 +16150,13 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1615016150
if (FSI->UsesFPIntrin && FD && !FD->hasAttr<StrictFPAttr>())
1615116151
FD->addAttr(StrictFPAttr::CreateImplicit(Context));
1615216152

16153-
sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
16153+
SourceLocation AnalysisLoc;
16154+
if (Body)
16155+
AnalysisLoc = Body->getEndLoc();
16156+
else if (FD)
16157+
AnalysisLoc = FD->getEndLoc();
16158+
sema::AnalysisBasedWarnings::Policy WP =
16159+
AnalysisWarnings.getPolicyInEffectAt(AnalysisLoc);
1615416160
sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr;
1615516161

1615616162
// If we skip function body, we can't tell if a function is a coroutine.

clang/lib/Sema/SemaExpr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16597,7 +16597,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
1659716597
BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0);
1659816598

1659916599
// Pop the block scope now but keep it alive to the end of this function.
16600-
AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
16600+
AnalysisBasedWarnings::Policy WP =
16601+
AnalysisWarnings.getPolicyInEffectAt(Body->getEndLoc());
1660116602
PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy);
1660216603

1660316604
BlockExpr *Result = new (Context)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -Werror=unreachable-code-aggressive %s
2+
3+
// Test that analysis-based warnings honor #pragma diagnostic controls.
4+
5+
struct [[clang::consumable(unconsumed)]] Linear {
6+
[[clang::return_typestate(unconsumed)]]
7+
Linear() {}
8+
[[clang::callable_when(consumed)]]
9+
~Linear() {}
10+
};
11+
12+
int a() {
13+
Linear l;
14+
return 0; // No -Wconsumed diagnostic, analysis is not enabled.
15+
return 1; // expected-error {{'return' will never be executed}}
16+
}
17+
18+
#pragma clang diagnostic push
19+
#pragma clang diagnostic error "-Wconsumed"
20+
int b() {
21+
Linear l;
22+
return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}}
23+
return 1; // expected-error {{'return' will never be executed}}
24+
}
25+
#pragma clang diagnostic pop
26+
27+
int c() {
28+
#pragma clang diagnostic push
29+
#pragma clang diagnostic error "-Wconsumed"
30+
Linear l;
31+
return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}}
32+
return 1; // expected-error {{'return' will never be executed}}
33+
#pragma clang diagnostic pop
34+
}
35+
36+
int d() {
37+
#pragma clang diagnostic push
38+
#pragma clang diagnostic error "-Wconsumed"
39+
#pragma clang diagnostic ignored "-Wunreachable-code-aggressive"
40+
Linear l;
41+
return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}}
42+
return 1; // Diagnostic is ignored
43+
}
44+
#pragma clang diagnostic pop
45+
46+
int e() {
47+
#pragma clang diagnostic push
48+
#pragma clang diagnostic error "-Wconsumed"
49+
#pragma clang diagnostic ignored "-Wunreachable-code-aggressive"
50+
Linear l;
51+
return 0; // expected-error {{invalid invocation of method '~Linear' on object 'l' while it is in the 'unconsumed' state}}
52+
return 1; // Diagnostic is ignored
53+
#pragma clang diagnostic pop
54+
}
55+
56+
int f() {
57+
Linear l;
58+
return 0; // No -Wconsumed diagnostic, analysis is not enabled
59+
return 1; // expected-error {{'return' will never be executed}}
60+
#pragma clang diagnostic push
61+
#pragma clang diagnostic ignored "-Wunreachable-code-aggressive"
62+
}
63+
#pragma clang diagnostic pop
64+
65+
int g() {
66+
Linear l;
67+
return 0; // No -Wconsumed diagnostic, the diagnostic generated at } is not enabled on this line.
68+
return 1; // expected-error {{'return' will never be executed}}
69+
#pragma clang diagnostic push
70+
#pragma clang diagnostic warning "-Wconsumed"
71+
}
72+
#pragma clang diagnostic pop
73+
74+
int h() {
75+
#pragma clang diagnostic push
76+
#pragma clang diagnostic error "-Wconsumed"
77+
#pragma clang diagnostic ignored "-Wunreachable-code-aggressive"
78+
#pragma clang diagnostic pop
79+
80+
Linear l;
81+
return 0; // No -Wconsumed diagnostic, the diagnostic generated at } is not enabled on this line.
82+
return 1; // expected-error {{'return' will never be executed}}
83+
}

0 commit comments

Comments
 (0)