Skip to content

Commit c18c9f5

Browse files
committed
Add a RangeSpecificDiagnosticConsumer
If a top-level diagnostic is in a particular source range, the RangeSpecificDiagnosticConsumer will funnel it and any attached notes to a particular "sub-consumer" designated for that range (intended to be used with whole files). If it's not in a range designated for any sub-consumer, the diagnostic is passed to all registered sub-consumers. This is intended to be used for batch mode, so that diagnostics that are definitely associated with a particular file can be emitted in that file's .dia output, while diagnostics that may be associated with other files (such as those that come from Clang) will still get presented to the user.
1 parent 07c7b5a commit c18c9f5

File tree

5 files changed

+571
-5
lines changed

5 files changed

+571
-5
lines changed

include/swift/AST/DiagnosticConsumer.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_BASIC_DIAGNOSTICCONSUMER_H
2020
#define SWIFT_BASIC_DIAGNOSTICCONSUMER_H
2121

22+
#include "swift/Basic/LLVM.h"
2223
#include "swift/Basic/SourceLoc.h"
2324
#include "llvm/Support/SourceMgr.h"
2425

@@ -111,6 +112,53 @@ class NullDiagnosticConsumer : public DiagnosticConsumer {
111112
ArrayRef<DiagnosticArgument> FormatArgs,
112113
const DiagnosticInfo &Info) override;
113114
};
115+
116+
/// \brief DiagnosticConsumer that funnels diagnostics in certain source ranges
117+
/// to particular sub-consumers.
118+
///
119+
/// Diagnostics that are not in one of the special ranges are emitted into every
120+
/// sub-consumer.
121+
class RangeSpecificDiagnosticConsumer : public DiagnosticConsumer {
122+
public:
123+
using ConsumerPair =
124+
std::pair<CharSourceRange, std::unique_ptr<DiagnosticConsumer>>;
125+
126+
private:
127+
SmallVector<std::unique_ptr<DiagnosticConsumer>, 4> SubConsumers;
128+
129+
/// A "map" sorted by the end locations of the ranges, so that a lookup by
130+
/// position can be done using binary search.
131+
///
132+
/// \see #consumerForLocation
133+
SmallVector<std::pair<CharSourceRange, unsigned>, 4> LocationToConsumerMap;
134+
135+
/// Indicates which consumer to send Note diagnostics too.
136+
///
137+
/// Notes are always considered attached to the error, warning, or remark
138+
/// that was most recently emitted.
139+
///
140+
/// If null, Note diagnostics are sent to every consumer.
141+
DiagnosticConsumer *ConsumerForSubsequentNotes = nullptr;
142+
143+
public:
144+
/// Takes ownership of the DiagnosticConsumers specified in \p consumers and
145+
/// records their association with the CharSourceRanges.
146+
///
147+
/// The ranges must not be overlapping.
148+
explicit RangeSpecificDiagnosticConsumer(
149+
MutableArrayRef<ConsumerPair> consumers);
150+
151+
void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
152+
DiagnosticKind Kind,
153+
StringRef FormatString,
154+
ArrayRef<DiagnosticArgument> FormatArgs,
155+
const DiagnosticInfo &Info) override;
156+
157+
bool finishProcessing() override;
158+
159+
private:
160+
DiagnosticConsumer *consumerForLocation(SourceLoc loc) const;
161+
};
114162

115163
} // end namespace swift
116164

lib/AST/DiagnosticConsumer.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,104 @@
2222
#include "llvm/Support/raw_ostream.h"
2323
using namespace swift;
2424

25-
DiagnosticConsumer::~DiagnosticConsumer() { }
25+
DiagnosticConsumer::~DiagnosticConsumer() = default;
26+
27+
llvm::SMLoc DiagnosticConsumer::getRawLoc(SourceLoc loc) {
28+
return loc.Value;
29+
}
30+
31+
RangeSpecificDiagnosticConsumer::RangeSpecificDiagnosticConsumer(
32+
MutableArrayRef<ConsumerPair> consumers) {
33+
using MapEntry = std::pair<CharSourceRange, unsigned>;
34+
35+
// Split up the ConsumerPairs into the "map" (to be sorted) and the actual
36+
// owning vector (preserving order).
37+
for (ConsumerPair &pair : consumers) {
38+
LocationToConsumerMap.emplace_back(pair.first, SubConsumers.size());
39+
SubConsumers.emplace_back(std::move(pair).second);
40+
}
41+
42+
// Sort the "map" by buffer /end/ location, for use with std::lower_bound
43+
// later. (Sorting by start location would produce the same sort, since the
44+
// ranges must not be overlapping, but since we need to check end locations
45+
// later it's consistent to sort by that here.)
46+
std::sort(LocationToConsumerMap.begin(), LocationToConsumerMap.end(),
47+
[](const MapEntry &left, const MapEntry &right) -> bool {
48+
auto compare = std::less<const char *>();
49+
return compare(getRawLoc(left.first.getEnd()).getPointer(),
50+
getRawLoc(right.first.getEnd()).getPointer());
51+
});
52+
53+
// Check that the ranges are non-overlapping.
54+
assert(LocationToConsumerMap.end() ==
55+
std::adjacent_find(LocationToConsumerMap.begin(),
56+
LocationToConsumerMap.end(),
57+
[](const MapEntry &left, const MapEntry &right) {
58+
return left.first.overlaps(right.first);
59+
}) &&
60+
"overlapping ranges given to RangeSpecificDiagnosticConsumer");
61+
}
62+
63+
DiagnosticConsumer *
64+
RangeSpecificDiagnosticConsumer::consumerForLocation(SourceLoc loc) const {
65+
// "Find the first range whose end location is greater than or equal to
66+
// 'loc'."
67+
auto possiblyContainingRangeIter =
68+
std::lower_bound(LocationToConsumerMap.begin(),
69+
LocationToConsumerMap.end(),
70+
loc,
71+
[](const std::pair<CharSourceRange, unsigned> &entry,
72+
SourceLoc loc) -> bool {
73+
auto compare = std::less<const char *>();
74+
return compare(getRawLoc(entry.first.getEnd()).getPointer(),
75+
getRawLoc(loc).getPointer());
76+
});
77+
78+
if (possiblyContainingRangeIter != LocationToConsumerMap.end() &&
79+
possiblyContainingRangeIter->first.contains(loc)) {
80+
return SubConsumers[possiblyContainingRangeIter->second].get();
81+
}
82+
83+
return nullptr;
84+
}
85+
86+
void RangeSpecificDiagnosticConsumer::handleDiagnostic(
87+
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
88+
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
89+
const DiagnosticInfo &Info) {
90+
91+
DiagnosticConsumer *specificConsumer;
92+
switch (Kind) {
93+
case DiagnosticKind::Error:
94+
case DiagnosticKind::Warning:
95+
case DiagnosticKind::Remark:
96+
specificConsumer = consumerForLocation(Loc);
97+
ConsumerForSubsequentNotes = specificConsumer;
98+
break;
99+
case DiagnosticKind::Note:
100+
specificConsumer = ConsumerForSubsequentNotes;
101+
break;
102+
}
103+
104+
if (specificConsumer) {
105+
specificConsumer->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs,
106+
Info);
107+
} else {
108+
for (auto &subConsumer : SubConsumers) {
109+
subConsumer->handleDiagnostic(SM, Loc, Kind, FormatString, FormatArgs,
110+
Info);
111+
}
112+
}
113+
}
114+
115+
bool RangeSpecificDiagnosticConsumer::finishProcessing() {
116+
// Deliberately don't use std::any_of here because we don't want early-exit
117+
// behavior.
118+
bool hadError = false;
119+
for (auto &subConsumer : SubConsumers)
120+
hadError |= subConsumer->finishProcessing();
121+
return hadError;
122+
}
26123

27124
void NullDiagnosticConsumer::handleDiagnostic(
28125
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,

lib/Frontend/PrintingDiagnosticConsumer.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ namespace {
6363
};
6464
} // end anonymous namespace
6565

66-
llvm::SMLoc DiagnosticConsumer::getRawLoc(SourceLoc loc) {
67-
return loc.Value;
68-
}
69-
7066
void PrintingDiagnosticConsumer::handleDiagnostic(
7167
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
7268
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,

unittests/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_swift_unittest(SwiftASTTests
2+
DiagnosticConsumerTests.cpp
23
SourceLocTests.cpp
34
TestContext.cpp
45
TypeMatchTests.cpp

0 commit comments

Comments
 (0)