Skip to content

Commit a91f499

Browse files
committed
[clang] Extend diagnose_if to accept more detailed warning information
1 parent 4d4f603 commit a91f499

24 files changed

+353
-180
lines changed

clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ class ClangTidyContext {
7979
this->DiagEngine = DiagEngine;
8080
}
8181

82+
const DiagnosticsEngine* getDiagnosticsEngine() const {
83+
return DiagEngine;
84+
}
85+
8286
~ClangTidyContext();
8387

8488
/// Report any errors detected using this method.

clang-tools-extra/clangd/Diagnostics.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,9 @@ std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
579579
for (auto &Diag : Output) {
580580
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
581581
// Warnings controlled by -Wfoo are better recognized by that name.
582-
StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(Diag.ID);
582+
StringRef Warning = Tidy->getDiagnosticsEngine()
583+
->getDiagnosticIDs()
584+
->getWarningOptionForDiag(Diag.ID);
583585
if (!Warning.empty()) {
584586
Diag.Name = ("-W" + Warning).str();
585587
} else {
@@ -909,7 +911,7 @@ bool isBuiltinDiagnosticSuppressed(unsigned ID,
909911
if (Suppress.contains(normalizeSuppressedCode(CodePtr)))
910912
return true;
911913
}
912-
StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(ID);
914+
StringRef Warning = DiagnosticIDs{}.getWarningOptionForDiag(ID);
913915
if (!Warning.empty() && Suppress.contains(Warning))
914916
return true;
915917
return false;

clang-tools-extra/clangd/ParsedAST.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ void applyWarningOptions(llvm::ArrayRef<std::string> ExtraArgs,
340340
if (Enable) {
341341
if (Diags.getDiagnosticLevel(ID, SourceLocation()) <
342342
DiagnosticsEngine::Warning) {
343-
auto Group = DiagnosticIDs::getGroupForDiag(ID);
343+
auto Group = Diags.getDiagnosticIDs()->getGroupForDiag(ID);
344344
if (!Group || !EnabledGroups(*Group))
345345
continue;
346346
Diags.setSeverity(ID, diag::Severity::Warning, SourceLocation());

clang/include/clang/Basic/Attr.td

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2959,18 +2959,15 @@ def DiagnoseIf : InheritableAttr {
29592959
let Spellings = [GNU<"diagnose_if">];
29602960
let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>;
29612961
let Args = [ExprArgument<"Cond">, StringArgument<"Message">,
2962-
EnumArgument<"DiagnosticType",
2963-
"DiagnosticType",
2964-
["error", "warning"],
2965-
["DT_Error", "DT_Warning"]>,
2962+
EnumArgument<"DefaultSeverity",
2963+
"DefaultSeverity",
2964+
["error", "warning"],
2965+
["DS_error", "DS_warning"]>,
2966+
StringArgument<"WarningGroup", /*optional*/ 1>,
29662967
BoolArgument<"ArgDependent", 0, /*fake*/ 1>,
29672968
DeclArgument<Named, "Parent", 0, /*fake*/ 1>];
29682969
let InheritEvenIfAlreadyPresent = 1;
29692970
let LateParsed = 1;
2970-
let AdditionalMembers = [{
2971-
bool isError() const { return diagnosticType == DT_Error; }
2972-
bool isWarning() const { return diagnosticType == DT_Warning; }
2973-
}];
29742971
let TemplateDependent = 1;
29752972
let Documentation = [DiagnoseIfDocs];
29762973
}

clang/include/clang/Basic/Diagnostic.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,12 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
331331
// Map extensions to warnings or errors?
332332
diag::Severity ExtBehavior = diag::Severity::Ignored;
333333

334-
DiagState()
334+
DiagnosticIDs& DiagIDs;
335+
336+
DiagState(DiagnosticIDs &DiagIDs)
335337
: IgnoreAllWarnings(false), EnableAllWarnings(false),
336338
WarningsAsErrors(false), ErrorsAsFatal(false),
337-
SuppressSystemWarnings(false) {}
339+
SuppressSystemWarnings(false), DiagIDs(DiagIDs) {}
338340

339341
using iterator = llvm::DenseMap<unsigned, DiagnosticMapping>::iterator;
340342
using const_iterator =
@@ -865,7 +867,8 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
865867
/// \param FormatString A fixed diagnostic format string that will be hashed
866868
/// and mapped to a unique DiagID.
867869
template <unsigned N>
868-
unsigned getCustomDiagID(Level L, const char (&FormatString)[N]) {
870+
[[deprecated("Use a CustomDiagDesc instead of a Level")]] unsigned
871+
getCustomDiagID(Level L, const char (&FormatString)[N]) {
869872
return Diags->getCustomDiagID((DiagnosticIDs::Level)L,
870873
StringRef(FormatString, N - 1));
871874
}

clang/include/clang/Basic/DiagnosticCategories.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace clang {
2626
#include "clang/Basic/DiagnosticGroups.inc"
2727
#undef CATEGORY
2828
#undef DIAG_ENTRY
29+
NUM_GROUPS
2930
};
3031
} // end namespace diag
3132
} // end namespace clang

clang/include/clang/Basic/DiagnosticIDs.h

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_BASIC_DIAGNOSTICIDS_H
1515
#define LLVM_CLANG_BASIC_DIAGNOSTICIDS_H
1616

17+
#include "clang/Basic/DiagnosticCategories.h"
1718
#include "clang/Basic/LLVM.h"
1819
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1920
#include "llvm/ADT/StringRef.h"
@@ -80,7 +81,7 @@ namespace clang {
8081
/// to either Ignore (nothing), Remark (emit a remark), Warning
8182
/// (emit a warning) or Error (emit as an error). It allows clients to
8283
/// map ERRORs to Error or Fatal (stop emitting diagnostics after this one).
83-
enum class Severity {
84+
enum class Severity : uint8_t {
8485
// NOTE: 0 means "uncomputed".
8586
Ignored = 1, ///< Do not present this diagnostic, ignore it.
8687
Remark = 2, ///< Present this diagnostic as a remark.
@@ -171,13 +172,61 @@ class DiagnosticMapping {
171172
class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
172173
public:
173174
/// The level of the diagnostic, after it has been through mapping.
174-
enum Level {
175+
enum Level : uint8_t {
175176
Ignored, Note, Remark, Warning, Error, Fatal
176177
};
177178

179+
// Diagnostic classes.
180+
enum Class {
181+
CLASS_NOTE = 0x01,
182+
CLASS_REMARK = 0x02,
183+
CLASS_WARNING = 0x03,
184+
CLASS_EXTENSION = 0x04,
185+
CLASS_ERROR = 0x05
186+
};
187+
188+
class CustomDiagDesc {
189+
diag::Severity DefaultSeverity : 3;
190+
unsigned Class : 3;
191+
unsigned ShowInSystemHeader : 1;
192+
unsigned ShowInSystemMacro : 1;
193+
unsigned HasGroup : 1;
194+
diag::Group Group;
195+
std::string Description;
196+
197+
auto get_as_tuple() const {
198+
return std::tuple(Class, ShowInSystemHeader, ShowInSystemMacro, HasGroup,
199+
Group, std::string_view{Description});
200+
}
201+
202+
public:
203+
CustomDiagDesc(diag::Severity DefaultSeverity, std::string Description,
204+
unsigned Class = CLASS_WARNING,
205+
bool ShowInSystemHeader = false,
206+
bool ShowInSystemMacro = false,
207+
std::optional<diag::Group> Group = std::nullopt)
208+
: DefaultSeverity(DefaultSeverity), Class(Class),
209+
ShowInSystemHeader(ShowInSystemHeader),
210+
ShowInSystemMacro(ShowInSystemMacro), HasGroup(Group != std::nullopt),
211+
Group(Group.value_or(diag::Group{})) {}
212+
213+
friend bool operator==(const CustomDiagDesc &lhs,
214+
const CustomDiagDesc &rhs) {
215+
return lhs.get_as_tuple() == rhs.get_as_tuple();
216+
}
217+
218+
friend bool operator<(const CustomDiagDesc &lhs,
219+
const CustomDiagDesc &rhs) {
220+
return lhs.get_as_tuple() < rhs.get_as_tuple();
221+
}
222+
};
223+
178224
private:
179225
/// Information for uniquing and looking up custom diags.
180226
std::unique_ptr<diag::CustomDiagInfo> CustomDiagInfo;
227+
std::unique_ptr<diag::Severity[]> GroupSeverity =
228+
std::make_unique<diag::Severity[]>(
229+
static_cast<size_t>(diag::Group::NUM_GROUPS));
181230

182231
public:
183232
DiagnosticIDs();
@@ -192,7 +241,27 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
192241
// FIXME: Replace this function with a create-only facilty like
193242
// createCustomDiagIDFromFormatString() to enforce safe usage. At the time of
194243
// writing, nearly all callers of this function were invalid.
195-
unsigned getCustomDiagID(Level L, StringRef FormatString);
244+
unsigned getCustomDiagID(CustomDiagDesc Diag);
245+
246+
[[deprecated("Use a CustomDiagDesc instead of a Level")]] unsigned
247+
getCustomDiagID(Level Level, StringRef Message) {
248+
return getCustomDiagID([&] -> CustomDiagDesc {
249+
switch (Level) {
250+
case DiagnosticIDs::Level::Ignored:
251+
return {diag::Severity::Ignored, std::string(Message), CLASS_WARNING};
252+
case DiagnosticIDs::Level::Note:
253+
return {diag::Severity::Fatal, std::string(Message), CLASS_NOTE};
254+
case DiagnosticIDs::Level::Remark:
255+
return {diag::Severity::Remark, std::string(Message), CLASS_REMARK};
256+
case DiagnosticIDs::Level::Warning:
257+
return {diag::Severity::Warning, std::string(Message), CLASS_WARNING};
258+
case DiagnosticIDs::Level::Error:
259+
return {diag::Severity::Error, std::string(Message), CLASS_ERROR};
260+
case DiagnosticIDs::Level::Fatal:
261+
return {diag::Severity::Fatal, std::string(Message), CLASS_ERROR};
262+
}
263+
}());
264+
}
196265

197266
//===--------------------------------------------------------------------===//
198267
// Diagnostic classification and reporting interfaces.
@@ -204,35 +273,34 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
204273
/// Return true if the unmapped diagnostic levelof the specified
205274
/// diagnostic ID is a Warning or Extension.
206275
///
207-
/// This only works on builtin diagnostics, not custom ones, and is not
208-
/// legal to call on NOTEs.
209-
static bool isBuiltinWarningOrExtension(unsigned DiagID);
276+
/// This is not legal to call on NOTEs.
277+
bool isWarningOrExtension(unsigned DiagID) const;
210278

211279
/// Return true if the specified diagnostic is mapped to errors by
212280
/// default.
213-
static bool isDefaultMappingAsError(unsigned DiagID);
281+
bool isDefaultMappingAsError(unsigned DiagID) const;
214282

215283
/// Get the default mapping for this diagnostic.
216-
static DiagnosticMapping getDefaultMapping(unsigned DiagID);
284+
DiagnosticMapping getDefaultMapping(unsigned DiagID) const;
217285

218-
/// Determine whether the given built-in diagnostic ID is a Note.
219-
static bool isBuiltinNote(unsigned DiagID);
286+
/// Determine whether the given diagnostic ID is a Note.
287+
bool isNote(unsigned DiagID) const;
220288

221-
/// Determine whether the given built-in diagnostic ID is for an
289+
/// Determine whether the given diagnostic ID is for an
222290
/// extension of some sort.
223-
static bool isBuiltinExtensionDiag(unsigned DiagID) {
291+
bool isExtensionDiag(unsigned DiagID) const {
224292
bool ignored;
225-
return isBuiltinExtensionDiag(DiagID, ignored);
293+
return isExtensionDiag(DiagID, ignored);
226294
}
227295

228-
/// Determine whether the given built-in diagnostic ID is for an
296+
/// Determine whether the given diagnostic ID is for an
229297
/// extension of some sort, and whether it is enabled by default.
230298
///
231299
/// This also returns EnabledByDefault, which is set to indicate whether the
232300
/// diagnostic is ignored by default (in which case -pedantic enables it) or
233301
/// treated as a warning/error by default.
234302
///
235-
static bool isBuiltinExtensionDiag(unsigned DiagID, bool &EnabledByDefault);
303+
bool isExtensionDiag(unsigned DiagID, bool &EnabledByDefault) const;
236304

237305
/// Given a group ID, returns the flag that toggles the group.
238306
/// For example, for Group::DeprecatedDeclarations, returns
@@ -242,19 +310,21 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
242310
/// Given a diagnostic group ID, return its documentation.
243311
static StringRef getWarningOptionDocumentation(diag::Group GroupID);
244312

313+
void setGroupSeverity(StringRef Group, diag::Severity);
314+
245315
/// Given a group ID, returns the flag that toggles the group.
246316
/// For example, for "deprecated-declarations", returns
247317
/// Group::DeprecatedDeclarations.
248318
static std::optional<diag::Group> getGroupForWarningOption(StringRef);
249319

250320
/// Return the lowest-level group that contains the specified diagnostic.
251-
static std::optional<diag::Group> getGroupForDiag(unsigned DiagID);
321+
std::optional<diag::Group> getGroupForDiag(unsigned DiagID) const;
252322

253323
/// Return the lowest-level warning option that enables the specified
254324
/// diagnostic.
255325
///
256326
/// If there is no -Wfoo flag that controls the diagnostic, this returns null.
257-
static StringRef getWarningOptionForDiag(unsigned DiagID);
327+
StringRef getWarningOptionForDiag(unsigned DiagID);
258328

259329
/// Return the category number that a specified \p DiagID belongs to,
260330
/// or 0 if no category.
@@ -352,6 +422,8 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
352422
getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
353423
const DiagnosticsEngine &Diag) const LLVM_READONLY;
354424

425+
Class getDiagClass(unsigned DiagID) const;
426+
355427
/// Used to report a diagnostic that is finally fully formed.
356428
///
357429
/// \returns \c true if the diagnostic was emitted, \c false if it was

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2865,9 +2865,15 @@ def ext_constexpr_function_never_constant_expr : ExtWarn<
28652865
"constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
28662866
def err_attr_cond_never_constant_expr : Error<
28672867
"%0 attribute expression never produces a constant expression">;
2868+
def err_diagnose_if_unknown_warning : Error<"unknown warning group">;
28682869
def err_diagnose_if_invalid_diagnostic_type : Error<
28692870
"invalid diagnostic type for 'diagnose_if'; use \"error\" or \"warning\" "
28702871
"instead">;
2872+
def err_diagnose_if_unknown_option : Error<"unknown diagnostic option">;
2873+
def err_diagnose_if_expected_equals : Error<
2874+
"expected '=' after diagnostic option">;
2875+
def err_diagnose_if_unexpected_value : Error<
2876+
"unexpected value; use 'true' or 'false'">;
28712877
def err_constexpr_body_no_return : Error<
28722878
"no return statement in %select{constexpr|consteval}0 function">;
28732879
def err_constexpr_return_missing_expr : Error<

clang/lib/Basic/Diagnostic.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
138138

139139
// Create a DiagState and DiagStatePoint representing diagnostic changes
140140
// through command-line.
141-
DiagStates.emplace_back();
141+
DiagStates.emplace_back(*Diags);
142142
DiagStatesByLoc.appendFirst(&DiagStates.back());
143143
}
144144
}
@@ -167,7 +167,7 @@ DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) {
167167

168168
// Initialize the entry if we added it.
169169
if (Result.second)
170-
Result.first->second = DiagnosticIDs::getDefaultMapping(Diag);
170+
Result.first->second = DiagIDs.getDefaultMapping(Diag);
171171

172172
return Result.first->second;
173173
}
@@ -309,7 +309,8 @@ void DiagnosticsEngine::DiagStateMap::dump(SourceManager &SrcMgr,
309309

310310
for (auto &Mapping : *Transition.State) {
311311
StringRef Option =
312-
DiagnosticIDs::getWarningOptionForDiag(Mapping.first);
312+
SrcMgr.getDiagnostics().Diags->getWarningOptionForDiag(
313+
Mapping.first);
313314
if (!DiagName.empty() && DiagName != Option)
314315
continue;
315316

@@ -353,9 +354,7 @@ void DiagnosticsEngine::PushDiagStatePoint(DiagState *State,
353354

354355
void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map,
355356
SourceLocation L) {
356-
assert(Diag < diag::DIAG_UPPER_LIMIT &&
357-
"Can only map builtin diagnostics");
358-
assert((Diags->isBuiltinWarningOrExtension(Diag) ||
357+
assert((Diags->isWarningOrExtension(Diag) ||
359358
(Map == diag::Severity::Fatal || Map == diag::Severity::Error)) &&
360359
"Cannot map errors into warnings!");
361360
assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location");
@@ -406,6 +405,8 @@ bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor,
406405
if (Diags->getDiagnosticsInGroup(Flavor, Group, GroupDiags))
407406
return true;
408407

408+
Diags->setGroupSeverity(Group, Map);
409+
409410
// Set the mapping.
410411
for (diag::kind Diag : GroupDiags)
411412
setSeverity(Diag, Map, Loc);
@@ -491,7 +492,7 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
491492

492493
// Set the mapping.
493494
for (diag::kind Diag : AllDiags)
494-
if (Diags->isBuiltinWarningOrExtension(Diag))
495+
if (Diags->isWarningOrExtension(Diag))
495496
setSeverity(Diag, Map, Loc);
496497
}
497498

0 commit comments

Comments
 (0)