Skip to content

Commit 481fb4c

Browse files
committed
Merge pull request #938 from milseman/suppress_warnings_api
API to suppress warnings
2 parents 10c8ce8 + 80270a9 commit 481fb4c

File tree

2 files changed

+185
-82
lines changed

2 files changed

+185
-82
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,77 @@ namespace swift {
378378
return fixItReplaceChars(Start, End, {});
379379
}
380380
};
381+
382+
/// \brief Class to track, map, and remap diagnostic severity and fatality
383+
///
384+
class DiagnosticState {
385+
public:
386+
/// \brief Describes the current behavior to take with a diagnostic
387+
enum class Behavior : uint8_t {
388+
Unspecified,
389+
Ignore,
390+
Note,
391+
Warning,
392+
Error,
393+
Fatal,
394+
};
395+
396+
private:
397+
/// \brief Whether we should continue to emit diagnostics, even after a
398+
/// fatal error
399+
bool showDiagnosticsAfterFatalError = false;
400+
401+
/// \brief Don't emit any warnings
402+
bool ignoreAllWarnings = false;
403+
404+
/// \brief Whether a fatal error has occurred
405+
bool fatalErrorOccurred = false;
406+
407+
/// \brief Whether any error diagnostics have been emitted.
408+
bool anyErrorOccurred = false;
409+
410+
/// \brief Track the previous emitted Behavior, useful for notes
411+
Behavior previousBehavior = Behavior::Unspecified;
412+
413+
/// \brief Track settable, per-diagnostic state that we store
414+
std::vector<Behavior> perDiagnosticState;
415+
416+
public:
417+
DiagnosticState();
418+
419+
/// \brief Figure out the Behavior for the given diagnostic, taking current
420+
/// state such as fatality into account.
421+
Behavior determineBehavior(DiagID);
422+
423+
bool hadAnyError() const { return anyErrorOccurred; }
424+
bool hasFatalErrorOccurred() const { return fatalErrorOccurred; }
425+
426+
void setShowDiagnosticsAfterFatalError(bool val = true) {
427+
showDiagnosticsAfterFatalError = val;
428+
}
429+
430+
/// \brief Whether to skip emitting warnings
431+
void setIgnoreAllWarnings(bool val) { ignoreAllWarnings = val; }
432+
bool getIgnoreAllWarnings() const { return ignoreAllWarnings; }
433+
434+
void resetHadAnyError() {
435+
anyErrorOccurred = false;
436+
fatalErrorOccurred = false;
437+
}
438+
439+
/// Set per-diagnostic behavior
440+
void setDiagnosticBehavior(DiagID id, Behavior behavior) {
441+
perDiagnosticState[(unsigned)id] = behavior;
442+
}
443+
444+
private:
445+
// Make the state movable only
446+
DiagnosticState(const DiagnosticState &) = delete;
447+
const DiagnosticState &operator=(const DiagnosticState &) = delete;
448+
449+
DiagnosticState(DiagnosticState &&) = default;
450+
DiagnosticState &operator=(DiagnosticState &&) = default;
451+
};
381452

382453
/// \brief Class responsible for formatting diagnostics and presenting them
383454
/// to the user.
@@ -390,19 +461,7 @@ namespace swift {
390461
/// emitting diagnostics.
391462
SmallVector<DiagnosticConsumer *, 2> Consumers;
392463

393-
/// HadAnyError - True if any error diagnostics have been emitted.
394-
bool HadAnyError;
395-
396-
enum class FatalErrorState {
397-
None,
398-
JustEmitted,
399-
Fatal
400-
};
401-
402-
/// Sticky flag set to \c true when a fatal error is emitted.
403-
FatalErrorState FatalState = FatalErrorState::None;
404-
405-
bool ShowDiagnosticsAfterFatalError = false;
464+
DiagnosticState state;
406465

407466
/// \brief The currently active diagnostic, if there is one.
408467
Optional<Diagnostic> ActiveDiagnostic;
@@ -424,25 +483,32 @@ namespace swift {
424483

425484
public:
426485
explicit DiagnosticEngine(SourceManager &SourceMgr)
427-
: SourceMgr(SourceMgr), HadAnyError(false), ActiveDiagnostic() {
486+
: SourceMgr(SourceMgr), ActiveDiagnostic() {
428487
}
429488

430489
/// hadAnyError - return true if any *error* diagnostics have been emitted.
431-
bool hadAnyError() const {
432-
return HadAnyError;
433-
}
490+
bool hadAnyError() const { return state.hadAnyError(); }
434491

435492
bool hasFatalErrorOccurred() const {
436-
return FatalState != FatalErrorState::None;
493+
return state.hasFatalErrorOccurred();
437494
}
438495

439-
void setShowDiagnosticsAfterFatalError(bool Val = true) {
440-
ShowDiagnosticsAfterFatalError = Val;
496+
void setShowDiagnosticsAfterFatalError(bool val = true) {
497+
state.setShowDiagnosticsAfterFatalError(val);
498+
}
499+
500+
/// \brief Whether to skip emitting warnings
501+
void setIgnoreAllWarnings(bool val) { state.setIgnoreAllWarnings(val); }
502+
bool getIgnoreAllWarnings() const {
503+
return state.getIgnoreAllWarnings();
504+
}
505+
506+
void ignoreDiagnostic(DiagID id) {
507+
state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore);
441508
}
442509

443510
void resetHadAnyError() {
444-
HadAnyError = false;
445-
FatalState = FatalErrorState::None;
511+
state.resetHadAnyError();
446512
}
447513

448514
/// \brief Add an additional DiagnosticConsumer to receive diagnostics.
@@ -572,10 +638,7 @@ namespace swift {
572638

573639
/// \returns true if diagnostic is marked with PointsToFirstBadToken
574640
/// option.
575-
bool isDiagnosticPointsToFirstBadToken(DiagID ID) const;
576-
577-
/// \returns true if diagnostic is marked as fatal.
578-
bool isDiagnosticFatal(DiagID ID) const;
641+
bool isDiagnosticPointsToFirstBadToken(DiagID id) const;
579642

580643
private:
581644
/// \brief Flush the active diagnostic.

lib/AST/DiagnosticEngine.cpp

Lines changed: 96 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
#include "llvm/ADT/SmallString.h"
2929
#include "llvm/ADT/Twine.h"
3030
#include "llvm/Support/raw_ostream.h"
31+
3132
using namespace swift;
3233

34+
namespace {
3335
enum class DiagnosticOptions {
3436
/// No options.
3537
none,
@@ -46,29 +48,48 @@ enum class DiagnosticOptions {
4648
Fatal,
4749
};
4850

49-
struct StoredDiagnosticInfo {
50-
/// \brief The kind of diagnostic we're dealing with.
51-
DiagnosticKind Kind;
52-
53-
DiagnosticOptions Options;
51+
// Reproduce the DiagIDs, as we want both the size and access to the raw ids
52+
// themselves.
53+
enum LocalDiagID : uint32_t {
54+
#define DIAG(KIND, ID, Category, Options, Text, Signature) ID,
55+
#include "swift/AST/DiagnosticsAll.def"
56+
NumDiags
57+
};
58+
}
5459

55-
// FIXME: Category
56-
57-
/// \brief Text associated with the diagnostic
58-
const char *Text;
60+
static const char *DiagnosticStrings[] = {
61+
#define ERROR(ID, Category, Options, Text, Signature) Text,
62+
#define WARNING(ID, Category, Options, Text, Signature) Text,
63+
#define NOTE(ID, Category, Options, Text, Signature) Text,
64+
#include "swift/AST/DiagnosticsAll.def"
65+
"<not a diagnostic>",
5966
};
6067

61-
static StoredDiagnosticInfo StoredDiagnosticInfos[] = {
62-
#define ERROR(ID,Category,Options,Text,Signature) \
63-
{ DiagnosticKind::Error, DiagnosticOptions::Options, Text },
64-
#define WARNING(ID,Category,Options,Text,Signature) \
65-
{ DiagnosticKind::Warning, DiagnosticOptions::Options, Text },
66-
#define NOTE(ID,Category,Options,Text,Signature) \
67-
{ DiagnosticKind::Note, DiagnosticOptions::Options, Text },
68+
static bool DiagnosticPointsToFirstBadToken[] = {
69+
#define ERROR(ID, Category, Options, Text, Signature) \
70+
DiagnosticOptions::Options == DiagnosticOptions::PointsToFirstBadToken,
71+
#define WARNING(ID, Category, Options, Text, Signature) \
72+
DiagnosticOptions::Options == DiagnosticOptions::PointsToFirstBadToken,
73+
#define NOTE(ID, Category, Options, Text, Signature) \
74+
DiagnosticOptions::Options == DiagnosticOptions::PointsToFirstBadToken,
6875
#include "swift/AST/DiagnosticsAll.def"
69-
{ DiagnosticKind::Error, DiagnosticOptions::none, "<not a diagnostic>" }
76+
"<not a diagnostic>",
7077
};
7178

79+
DiagnosticState::DiagnosticState() {
80+
// Initialize our per-diagnostic state to the default
81+
perDiagnosticState.resize(LocalDiagID::NumDiags);
82+
#define ERROR(ID, Category, Options, Text, Signature) \
83+
perDiagnosticState[LocalDiagID::ID] = \
84+
DiagnosticOptions::Options == DiagnosticOptions::Fatal ? Behavior::Fatal \
85+
: Behavior::Error;
86+
#define WARNING(ID, Category, Options, Text, Signature) \
87+
perDiagnosticState[LocalDiagID::ID] = Behavior::Warning;
88+
#define NOTE(ID, Category, Options, Text, Signature) \
89+
perDiagnosticState[LocalDiagID::ID] = Behavior::Note;
90+
#include "swift/AST/DiagnosticsAll.def"
91+
}
92+
7293
static CharSourceRange toCharSourceRange(SourceManager &SM, SourceRange SR) {
7394
return CharSourceRange(SM, SR.Start, Lexer::getLocForEndOfToken(SM, SR.End));
7495
}
@@ -175,15 +196,7 @@ void InFlightDiagnostic::flush() {
175196
}
176197

177198
bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const {
178-
const StoredDiagnosticInfo &StoredInfo =
179-
StoredDiagnosticInfos[(unsigned) ID];
180-
return StoredInfo.Options == DiagnosticOptions::PointsToFirstBadToken;
181-
}
182-
183-
bool DiagnosticEngine::isDiagnosticFatal(DiagID ID) const {
184-
const StoredDiagnosticInfo &StoredInfo =
185-
StoredDiagnosticInfos[(unsigned) ID];
186-
return StoredInfo.Options == DiagnosticOptions::Fatal;
199+
return DiagnosticPointsToFirstBadToken[(unsigned)ID];
187200
}
188201

189202
/// \brief Skip forward to one of the given delimiters.
@@ -443,12 +456,63 @@ static void formatDiagnosticText(StringRef InText,
443456
(void)Result;
444457
assert(ArgIndex < Args.size() && "Out-of-range argument index");
445458
InText = InText.substr(Length);
446-
459+
447460
// Convert the argument to a string.
448461
formatDiagnosticArgument(Modifier, ModifierArguments, Args, ArgIndex, Out);
449462
}
450463
}
451464

465+
static DiagnosticKind toDiagnosticKind(DiagnosticState::Behavior behavior) {
466+
switch (behavior) {
467+
case DiagnosticState::Behavior::Unspecified:
468+
llvm_unreachable("unspecified behavior");
469+
case DiagnosticState::Behavior::Ignore:
470+
llvm_unreachable("trying to map an ignored diagnostic");
471+
case DiagnosticState::Behavior::Error:
472+
case DiagnosticState::Behavior::Fatal:
473+
return DiagnosticKind::Error;
474+
case DiagnosticState::Behavior::Note:
475+
return DiagnosticKind::Note;
476+
case DiagnosticState::Behavior::Warning:
477+
return DiagnosticKind::Warning;
478+
}
479+
}
480+
481+
DiagnosticState::Behavior DiagnosticState::determineBehavior(DiagID id) {
482+
auto set = [this](DiagnosticState::Behavior lvl) {
483+
if (lvl == Behavior::Fatal) {
484+
fatalErrorOccurred = true;
485+
anyErrorOccurred = true;
486+
} else if (lvl == Behavior::Error) {
487+
anyErrorOccurred = true;
488+
}
489+
490+
previousBehavior = lvl;
491+
return lvl;
492+
};
493+
494+
auto behavior = perDiagnosticState[(unsigned)id];
495+
496+
// Notes relating to ignored diagnostics should also be ignored
497+
if (previousBehavior == Behavior::Ignore && behavior == Behavior::Note)
498+
return set(Behavior::Ignore);
499+
500+
// Suppress diagnostics when in a fatal state, except for follow-on notes
501+
// about the original fatal diag
502+
if (fatalErrorOccurred) {
503+
bool emitAnyways = showDiagnosticsAfterFatalError ||
504+
(behavior == Behavior::Note &&
505+
previousBehavior != Behavior::Ignore);
506+
if (!emitAnyways)
507+
return set(Behavior::Ignore);
508+
}
509+
510+
if (behavior == Behavior::Warning && ignoreAllWarnings)
511+
return set(Behavior::Ignore);
512+
513+
return set(behavior);
514+
}
515+
452516
void DiagnosticEngine::flushActiveDiagnostic() {
453517
assert(ActiveDiagnostic && "No active diagnostic to flush");
454518
if (TransactionCount == 0) {
@@ -467,33 +531,9 @@ void DiagnosticEngine::emitTentativeDiagnostics() {
467531
}
468532

469533
void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
470-
const StoredDiagnosticInfo &StoredInfo
471-
= StoredDiagnosticInfos[(unsigned)diagnostic.getID()];
472-
473-
if (FatalState != FatalErrorState::None) {
474-
bool shouldIgnore = true;
475-
if (StoredInfo.Kind == DiagnosticKind::Note)
476-
shouldIgnore = (FatalState == FatalErrorState::Fatal);
477-
else
478-
FatalState = FatalErrorState::Fatal;
479-
480-
if (shouldIgnore && !ShowDiagnosticsAfterFatalError) {
481-
return;
482-
}
483-
}
484-
485-
// Check whether this is an error.
486-
switch (StoredInfo.Kind) {
487-
case DiagnosticKind::Error:
488-
HadAnyError = true;
489-
if (isDiagnosticFatal(diagnostic.getID()))
490-
FatalState = FatalErrorState::JustEmitted;
491-
break;
492-
493-
case DiagnosticKind::Note:
494-
case DiagnosticKind::Warning:
495-
break;
496-
}
534+
auto behavior = state.determineBehavior(diagnostic.getID());
535+
if (behavior == DiagnosticState::Behavior::Ignore)
536+
return;
497537

498538
// Figure out the source location.
499539
SourceLoc loc = diagnostic.getLoc();
@@ -613,7 +653,7 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
613653
llvm::SmallString<256> Text;
614654
{
615655
llvm::raw_svector_ostream Out(Text);
616-
formatDiagnosticText(StoredInfo.Text, diagnostic.getArgs(), Out);
656+
formatDiagnosticText(DiagnosticStrings[(unsigned)diagnostic.getID()], diagnostic.getArgs(), Out);
617657
}
618658

619659
// Pass the diagnostic off to the consumer.
@@ -622,7 +662,7 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
622662
Info.Ranges = diagnostic.getRanges();
623663
Info.FixIts = diagnostic.getFixIts();
624664
for (auto &Consumer : Consumers) {
625-
Consumer->handleDiagnostic(SourceMgr, loc, StoredInfo.Kind, Text, Info);
665+
Consumer->handleDiagnostic(SourceMgr, loc, toDiagnosticKind(behavior), Text, Info);
626666
}
627667
}
628668

0 commit comments

Comments
 (0)