7
7
// ===----------------------------------------------------------------------===//
8
8
#include " clang/Driver/BoundsSafetyArgs.h"
9
9
#include " clang/Basic/DiagnosticDriver.h"
10
+ #include " clang/Basic/DiagnosticFrontend.h"
10
11
#include " clang/Driver/Options.h"
12
+ #include " llvm/ADT/StringSet.h"
11
13
12
14
using namespace llvm ::opt;
13
15
using namespace clang ::driver::options;
@@ -16,15 +18,101 @@ namespace clang {
16
18
17
19
namespace driver {
18
20
21
+ template <class AllocatorTy = llvm::MallocAllocator>
22
+ static void DiagnoseDisabledBoundsSafetyChecks (
23
+ LangOptions::BoundsSafetyNewChecksMaskIntTy EnabledChecks,
24
+ DiagnosticsEngine *Diags,
25
+ LangOptions::BoundsSafetyNewChecksMaskIntTy
26
+ IndividualChecksExplicitlyDisabled) {
27
+ struct BoundsCheckBatch {
28
+ const char *Name;
29
+ LangOptionsBase::BoundsSafetyNewChecksMaskIntTy Checks;
30
+ };
31
+
32
+ // Batches of checks should be ordered with newest first
33
+ BoundsCheckBatch Batches[] = {
34
+ // We deliberately don't include `all` here because that batch
35
+ // isn't stable over time (unlike batches like `batch_0`) so we
36
+ // don't want to suggest users start using it.
37
+ {" batch_0" , LangOptions::getBoundsSafetyNewChecksMaskForGroup (" batch_0" )},
38
+ {" none" , LangOptions::BS_CHK_None}};
39
+
40
+ ArrayRef<BoundsCheckBatch> BatchesAR (Batches);
41
+ LangOptionsBase::BoundsSafetyNewChecksMaskIntTy DiagnosedDisabledChecks =
42
+ LangOptions::BS_CHK_None;
43
+
44
+ // Loop over all batches except "none"
45
+ for (size_t BatchIdx = 0 ; BatchIdx < BatchesAR.size () - 1 ; ++BatchIdx) {
46
+ auto ChecksInCurrentBatch = BatchesAR[BatchIdx].Checks ;
47
+ auto ChecksInOlderBatch = BatchesAR[BatchIdx + 1 ].Checks ;
48
+ auto ChecksInCurrentBatchOnly = ChecksInCurrentBatch & ~ChecksInOlderBatch;
49
+ auto CurrentBatchName = BatchesAR[BatchIdx].Name ;
50
+
51
+ if ((EnabledChecks & ChecksInCurrentBatchOnly) == ChecksInCurrentBatchOnly)
52
+ continue ; // All checks in batch are enabled. Nothing to diagnose.
53
+
54
+ // Diagnose disabled bounds checks
55
+
56
+ if ((EnabledChecks & ChecksInCurrentBatchOnly) == 0 ) {
57
+ // None of the checks in the current batch are enabled. Diagnose this
58
+ // once for all the checks in the batch.
59
+ if ((DiagnosedDisabledChecks & ChecksInCurrentBatchOnly) !=
60
+ ChecksInCurrentBatchOnly) {
61
+ Diags->Report (diag::warn_bounds_safety_new_checks_none)
62
+ << CurrentBatchName;
63
+ DiagnosedDisabledChecks |= ChecksInCurrentBatchOnly;
64
+ }
65
+ continue ;
66
+ }
67
+
68
+ // Some (but not all) checks are disabled in the current batch. Iterate over
69
+ // each check in the batch and emit a diagnostic for each disabled check
70
+ // in the batch.
71
+ assert (ChecksInCurrentBatch > 0 );
72
+ auto FirstCheckInBatch = 1 << __builtin_ctz (ChecksInCurrentBatch);
73
+ for (size_t CheckBit = FirstCheckInBatch;
74
+ CheckBit <= LangOptions::BS_CHK_MaxMask; CheckBit <<= 1 ) {
75
+ assert (CheckBit != 0 );
76
+ if ((CheckBit & ChecksInCurrentBatch) == 0 )
77
+ continue ; // Check not in batch
78
+
79
+ if (EnabledChecks & CheckBit)
80
+ continue ; // Check is active
81
+
82
+ // Diagnose disabled check
83
+ if (!(DiagnosedDisabledChecks & CheckBit)) {
84
+ size_t CheckNumber = __builtin_ctz (CheckBit);
85
+ // If we always suggested enabling the current batch that
86
+ // could be confusing if the user passed something like
87
+ // `-fbounds-safety-bringup-missing-checks=batch_0
88
+ // -fno-bounds-safety-bringup-missing-checks=access_size`. To avoid
89
+ // this we detect when the check corresponding to `CheckBit` has been
90
+ // explicitly disabled on the command line and in that case we suggeset
91
+ // removing the flag.
92
+ bool SuggestRemovingFlag =
93
+ CheckBit & IndividualChecksExplicitlyDisabled;
94
+ Diags->Report (diag::warn_bounds_safety_new_checks_mixed)
95
+ << CheckNumber << SuggestRemovingFlag << CurrentBatchName;
96
+ DiagnosedDisabledChecks |= CheckBit;
97
+ }
98
+ }
99
+ }
100
+ }
101
+
19
102
LangOptions::BoundsSafetyNewChecksMaskIntTy
20
103
ParseBoundsSafetyNewChecksMaskFromArgs (const llvm::opt::ArgList &Args,
21
104
DiagnosticsEngine *Diags) {
105
+ LangOptions::BoundsSafetyNewChecksMaskIntTy
106
+ IndividualChecksExplicitlyDisabled = LangOptions::BS_CHK_None;
22
107
auto FilteredArgs =
23
108
Args.filtered (OPT_fbounds_safety_bringup_missing_checks_EQ,
24
109
OPT_fno_bounds_safety_bringup_missing_checks_EQ);
25
110
if (FilteredArgs.empty ()) {
26
111
// No flags present. Use the default
27
- return LangOptions::getDefaultBoundsSafetyNewChecksMask ();
112
+ auto Result = LangOptions::getDefaultBoundsSafetyNewChecksMask ();
113
+ DiagnoseDisabledBoundsSafetyChecks (Result, Diags,
114
+ IndividualChecksExplicitlyDisabled);
115
+ return Result;
28
116
}
29
117
30
118
// If flags are present then start with BS_CHK_None as the initial mask and
@@ -35,6 +123,11 @@ ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args,
35
123
// All the options will be applied as masks in the command line order, to make
36
124
// it easier to enable all but certain checks (or disable all but certain
37
125
// checks).
126
+ const auto Batch0Checks =
127
+ LangOptions::getBoundsSafetyNewChecksMaskForGroup (" batch_0" );
128
+ const auto AllChecks =
129
+ LangOptions::getBoundsSafetyNewChecksMaskForGroup (" all" );
130
+ bool Errors = false ;
38
131
for (const Arg *A : FilteredArgs) {
39
132
for (const char *Value : A->getValues ()) {
40
133
std::optional<LangOptions::BoundsSafetyNewChecksMaskIntTy> Mask =
@@ -51,17 +144,16 @@ ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args,
51
144
.Case (" libc_attributes" , LangOptions::BS_CHK_LibCAttributes)
52
145
.Case (" array_subscript_agg" ,
53
146
LangOptions::BS_CHK_ArraySubscriptAgg)
54
- .Case (" all" ,
55
- LangOptions::getBoundsSafetyNewChecksMaskForGroup (" all" ))
56
- .Case (
57
- " batch_0" ,
58
- LangOptions::getBoundsSafetyNewChecksMaskForGroup (" batch_0" ))
147
+ .Case (" all" , AllChecks)
148
+ .Case (" batch_0" , Batch0Checks)
59
149
.Case (" none" , LangOptions::BS_CHK_None)
60
150
.Default (std::nullopt);
151
+
61
152
if (!Mask) {
62
153
if (Diags)
63
154
Diags->Report (diag::err_drv_invalid_value)
64
155
<< A->getSpelling () << Value;
156
+ Errors = true ;
65
157
break ;
66
158
}
67
159
@@ -81,6 +173,7 @@ ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args,
81
173
<< A->getSpelling () << Value
82
174
<< (IsPosFlag ? " -fno-bounds-safety-bringup-missing-checks"
83
175
: " -fbounds-safety-bringup-missing-checks" );
176
+ Errors = true ;
84
177
break ;
85
178
}
86
179
@@ -91,8 +184,24 @@ ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args,
91
184
OPT_fno_bounds_safety_bringup_missing_checks_EQ));
92
185
Result &= ~(*Mask);
93
186
}
187
+
188
+ // Update which checks have been explicitly disabled
189
+ if (__builtin_popcount (*Mask) == 1 ) {
190
+ // A single check was enabled/disabled
191
+ if (IsPosFlag)
192
+ IndividualChecksExplicitlyDisabled &= ~(*Mask);
193
+ else
194
+ IndividualChecksExplicitlyDisabled |= *Mask;
195
+ } else {
196
+ // A batch of checks were enabled/disabled. Any checks in that batch
197
+ // are no longer explicitly set.
198
+ IndividualChecksExplicitlyDisabled &= ~(*Mask);
199
+ }
94
200
}
95
201
}
202
+ if (Diags && !Errors)
203
+ DiagnoseDisabledBoundsSafetyChecks (Result, Diags,
204
+ IndividualChecksExplicitlyDisabled);
96
205
return Result;
97
206
}
98
207
0 commit comments