Skip to content

Commit 02fcbf7

Browse files
author
David Ungar
committed
optional pointer to consumer replaces NullDiagnosticConsumer placeholder
1 parent dc5827c commit 02fcbf7

File tree

5 files changed

+66
-16
lines changed

5 files changed

+66
-16
lines changed

include/swift/AST/DiagnosticConsumer.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,17 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
131131
/// be associated with. An empty string means that a consumer is not
132132
/// associated with any particular buffer, and should only receive diagnostics
133133
/// that are not in any of the other consumers' files.
134+
/// A null pointer for the DiagnosticConsumer means that diagnostics for this
135+
/// file should not be emitted.
134136
using ConsumerPair =
135137
std::pair<std::string, std::unique_ptr<DiagnosticConsumer>>;
136138

137139
private:
138140
/// All consumers owned by this FileSpecificDiagnosticConsumer.
139141
const SmallVector<ConsumerPair, 4> SubConsumers;
140142

143+
/// The DiagnosticConsumer may be empty if those diagnostics are not to be
144+
/// emitted.
141145
using ConsumersOrderedByRangeEntry =
142146
std::pair<CharSourceRange, DiagnosticConsumer *>;
143147

@@ -157,8 +161,9 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
157161
/// Notes are always considered attached to the error, warning, or remark
158162
/// that was most recently emitted.
159163
///
160-
/// If null, Note diagnostics are sent to every consumer.
161-
DiagnosticConsumer *ConsumerForSubsequentNotes = nullptr;
164+
/// If None, Note diagnostics are sent to every consumer.
165+
/// If null, diagnostics are suppressed.
166+
Optional<DiagnosticConsumer *> ConsumerForSubsequentNotes = None;
162167

163168
public:
164169
/// Takes ownership of the DiagnosticConsumers specified in \p consumers.
@@ -178,8 +183,15 @@ class FileSpecificDiagnosticConsumer : public DiagnosticConsumer {
178183

179184
private:
180185
void computeConsumersOrderedByRange(SourceManager &SM);
181-
DiagnosticConsumer *consumerForLocation(SourceManager &SM,
182-
SourceLoc loc) const;
186+
187+
/// Returns nullptr if diagnostic is to be suppressed,
188+
/// None if diagnostic is to be distributed to every consumer,
189+
/// a particular consumer if diagnostic goes there.
190+
///
191+
/// (For Xcode compatibility, batch mode must suppress diagnostics to
192+
/// non-primaries.)
193+
Optional<DiagnosticConsumer *> consumerForLocation(SourceManager &SM,
194+
SourceLoc loc) const;
183195
};
184196

185197
} // end namespace swift

include/swift/Frontend/FrontendInputsAndOutputs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ class FrontendInputsAndOutputs {
104104
bool
105105
forEachPrimaryInput(llvm::function_ref<bool(const InputFile &)> fn) const;
106106

107+
/// If \p fn returns true, exit early and return true.
108+
bool
109+
forEachNonPrimaryInput(llvm::function_ref<bool(const InputFile &)> fn) const;
110+
107111
unsigned primaryInputCount() const { return PrimaryInputsInOrder.size(); }
108112

109113
// Primary count readers:
@@ -211,6 +215,10 @@ class FrontendInputsAndOutputs {
211215
bool forEachInputProducingSupplementaryOutput(
212216
llvm::function_ref<bool(const InputFile &)> fn) const;
213217

218+
/// If \p fn returns true, exit early and return true.
219+
bool forEachInputNotProducingSupplementaryOutput(
220+
llvm::function_ref<bool(const InputFile &)> fn) const;
221+
214222
/// Assumes there is not more than one primary input file, if any.
215223
/// Otherwise, you would need to call getPrimarySpecificPathsForPrimary
216224
/// to tell it which primary input you wanted the outputs for.

lib/AST/DiagnosticConsumer.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange(
7171
Optional<unsigned> bufferID = SM.getIDForBufferIdentifier(pair.first);
7272
assert(bufferID.hasValue() && "consumer registered for unknown file");
7373
CharSourceRange range = SM.getRangeForBuffer(bufferID.getValue());
74-
ConsumersOrderedByRange.emplace_back(range, pair.second.get());
74+
ConsumersOrderedByRange.emplace_back(range, pair.second ? pair.second.get()
75+
: nullptr);
7576
}
7677

7778
// Sort the "map" by buffer /end/ location, for use with std::lower_bound
@@ -99,7 +100,7 @@ void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange(
99100
"overlapping ranges despite having distinct files");
100101
}
101102

102-
DiagnosticConsumer *
103+
Optional<DiagnosticConsumer *>
103104
FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM,
104105
SourceLoc loc) const {
105106
// If there's only one consumer, we'll use it no matter what, because...
@@ -111,7 +112,7 @@ FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM,
111112

112113
// Diagnostics with invalid locations always go to every consumer.
113114
if (loc.isInvalid())
114-
return nullptr;
115+
return None;
115116

116117
// This map is generated on first use and cached, to allow the
117118
// FileSpecificDiagnosticConsumer to be set up before the source files are
@@ -121,15 +122,15 @@ FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM,
121122
// It's possible to get here while a bridging header PCH is being
122123
// attached-to, if there's some sort of AST-reader warning or error, which
123124
// happens before CompilerInstance::setUpInputs(), at which point _no_
124-
// source buffers are loaded in yet. In that case we return nullptr, rather
125+
// source buffers are loaded in yet. In that case we return None, rather
125126
// than trying to build a nonsensical map (and actually crashing since we
126127
// can't find buffers for the inputs).
127128
assert(!SubConsumers.empty());
128129
if (!SM.getIDForBufferIdentifier(SubConsumers.begin()->first).hasValue()) {
129130
assert(llvm::none_of(SubConsumers, [&](const ConsumerPair &pair) {
130131
return SM.getIDForBufferIdentifier(pair.first).hasValue();
131132
}));
132-
return nullptr;
133+
return None;
133134
}
134135
auto *mutableThis = const_cast<FileSpecificDiagnosticConsumer*>(this);
135136
mutableThis->computeConsumersOrderedByRange(SM);
@@ -152,18 +153,21 @@ FileSpecificDiagnosticConsumer::consumerForLocation(SourceManager &SM,
152153

153154
if (possiblyContainingRangeIter != ConsumersOrderedByRange.end() &&
154155
possiblyContainingRangeIter->first.contains(loc)) {
155-
return possiblyContainingRangeIter->second;
156+
DiagnosticConsumer *consumerIfDiagnosticIsToBeEmitted =
157+
possiblyContainingRangeIter->second;
158+
return consumerIfDiagnosticIsToBeEmitted ? consumerIfDiagnosticIsToBeEmitted
159+
: nullptr;
156160
}
157161

158-
return nullptr;
162+
return None;
159163
}
160164

161165
void FileSpecificDiagnosticConsumer::handleDiagnostic(
162166
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
163167
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
164168
const DiagnosticInfo &Info) {
165169

166-
DiagnosticConsumer *specificConsumer;
170+
Optional<DiagnosticConsumer *> specificConsumer;
167171
switch (Kind) {
168172
case DiagnosticKind::Error:
169173
case DiagnosticKind::Warning:
@@ -176,14 +180,15 @@ void FileSpecificDiagnosticConsumer::handleDiagnostic(
176180
break;
177181
}
178182

179-
if (specificConsumer) {
180-
specificConsumer->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs,
181-
Info);
182-
} else {
183+
if (!specificConsumer.hasValue()) {
183184
for (auto &subConsumer : SubConsumers) {
184185
subConsumer.second->handleDiagnostic(SM, Loc, Kind, FormatString,
185186
FormatArgs, Info);
186187
}
188+
} else if (DiagnosticConsumer *c = specificConsumer.getValue())
189+
c->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs, Info);
190+
else {
191+
/// suppress non-primary diagnostic in batch mode
187192
}
188193
}
189194

lib/Frontend/FrontendInputsAndOutputs.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ bool FrontendInputsAndOutputs::forEachPrimaryInput(
9797
return false;
9898
}
9999

100+
bool FrontendInputsAndOutputs::forEachNonPrimaryInput(
101+
llvm::function_ref<bool(const InputFile &)> fn) const {
102+
return forEachInput([&](const InputFile &f) -> bool {
103+
return f.isPrimary() ? false : fn(f);
104+
});
105+
}
106+
100107
void FrontendInputsAndOutputs::assertMustNotBeMoreThanOnePrimaryInput() const {
101108
assert(!hasMultiplePrimaryInputs() &&
102109
"have not implemented >1 primary input yet");
@@ -364,6 +371,11 @@ bool FrontendInputsAndOutputs::forEachInputProducingSupplementaryOutput(
364371
: hasInputs() ? fn(firstInput()) : false;
365372
}
366373

374+
bool FrontendInputsAndOutputs::forEachInputNotProducingSupplementaryOutput(
375+
llvm::function_ref<bool(const InputFile &)> fn) const {
376+
return hasPrimaryInputs() ? forEachNonPrimaryInput(fn) : false;
377+
}
378+
367379
bool FrontendInputsAndOutputs::hasSupplementaryOutputPath(
368380
llvm::function_ref<const std::string &(const SupplementaryOutputPaths &)>
369381
extractorFn) const {

lib/FrontendTool/FrontendTool.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,19 @@ createDispatchingDiagnosticConsumerIfNeeded(
15271527
subConsumers.emplace_back(input.file(), std::move(subConsumer));
15281528
return false;
15291529
});
1530+
// For batch mode, the compiler must swallow diagnostics pertaining to
1531+
// non-primary files in order to avoid Xcode showing the same diagnostic
1532+
// multiple times. So, create a diagnostic "eater" for those non-primary
1533+
// files.
1534+
// To avoid introducing bugs into WMO or single-file modes, test for multiple
1535+
// primaries.
1536+
if (inputsAndOutputs.hasMultiplePrimaryInputs()) {
1537+
inputsAndOutputs.forEachInputNotProducingSupplementaryOutput(
1538+
[&](const InputFile &input) -> bool {
1539+
subConsumers.emplace_back(input.file(), nullptr);
1540+
return false;
1541+
});
1542+
}
15301543

15311544
if (subConsumers.empty())
15321545
return nullptr;

0 commit comments

Comments
 (0)