14
14
//
15
15
// ===----------------------------------------------------------------------===//
16
16
17
- #define DEBUG_TYPE " swift-basic "
17
+ #define DEBUG_TYPE " swift-ast "
18
18
#include " swift/AST/DiagnosticConsumer.h"
19
19
#include " swift/AST/DiagnosticEngine.h"
20
+ #include " swift/Basic/Defer.h"
21
+ #include " swift/Basic/SourceManager.h"
20
22
#include " llvm/ADT/StringRef.h"
23
+ #include " llvm/ADT/StringSet.h"
21
24
#include " llvm/Support/Debug.h"
22
25
#include " llvm/Support/raw_ostream.h"
23
26
using namespace swift ;
@@ -28,62 +31,119 @@ llvm::SMLoc DiagnosticConsumer::getRawLoc(SourceLoc loc) {
28
31
return loc.Value ;
29
32
}
30
33
31
- RangeSpecificDiagnosticConsumer::RangeSpecificDiagnosticConsumer (
32
- MutableArrayRef<ConsumerPair> consumers) {
33
- using MapEntry = std::pair<CharSourceRange, unsigned >;
34
+ LLVM_ATTRIBUTE_UNUSED
35
+ static bool hasDuplicateFileNames (
36
+ ArrayRef<FileSpecificDiagnosticConsumer::ConsumerPair> consumers) {
37
+ llvm::StringSet<> seenFiles;
38
+ for (const auto &consumerPair : consumers) {
39
+ if (consumerPair.first .empty ()) {
40
+ // We can handle multiple consumers that aren't associated with any file,
41
+ // because they only collect diagnostics that aren't in any of the special
42
+ // files. This isn't an important use case to support, but also SmallSet
43
+ // doesn't handle empty strings anyway!
44
+ continue ;
45
+ }
34
46
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 );
47
+ bool isUnique = seenFiles.insert (consumerPair.first ).second ;
48
+ if (!isUnique)
49
+ return true ;
50
+ }
51
+ return false ;
52
+ }
53
+
54
+ FileSpecificDiagnosticConsumer::FileSpecificDiagnosticConsumer (
55
+ SmallVectorImpl<ConsumerPair> &consumers)
56
+ : SubConsumers(std::move(consumers)) {
57
+ assert (!SubConsumers.empty () &&
58
+ " don't waste time handling diagnostics that will never get emitted" );
59
+ assert (!hasDuplicateFileNames (SubConsumers) &&
60
+ " having multiple consumers for the same file is not implemented" );
61
+ }
62
+
63
+ void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange (
64
+ SourceManager &SM) {
65
+ // Look up each file's source range and add it to the "map" (to be sorted).
66
+ for (const ConsumerPair &pair : SubConsumers) {
67
+ if (pair.first .empty ())
68
+ continue ;
69
+
70
+ Optional<unsigned > bufferID = SM.getIDForBufferIdentifier (pair.first );
71
+ assert (bufferID.hasValue () && " consumer registered for unknown file" );
72
+ CharSourceRange range = SM.getRangeForBuffer (bufferID.getValue ());
73
+ ConsumersOrderedByRange.emplace_back (range, pair.second .get ());
40
74
}
41
75
42
76
// Sort the "map" by buffer /end/ location, for use with std::lower_bound
43
77
// later. (Sorting by start location would produce the same sort, since the
44
78
// ranges must not be overlapping, but since we need to check end locations
45
79
// later it's consistent to sort by that here.)
46
- std::sort (LocationToConsumerMap.begin (), LocationToConsumerMap.end (),
47
- [](const MapEntry &left, const MapEntry &right) -> bool {
80
+ std::sort (ConsumersOrderedByRange.begin (), ConsumersOrderedByRange.end (),
81
+ [](const ConsumersOrderedByRangeEntry &left,
82
+ const ConsumersOrderedByRangeEntry &right) -> bool {
48
83
auto compare = std::less<const char *>();
49
84
return compare (getRawLoc (left.first .getEnd ()).getPointer (),
50
85
getRawLoc (right.first .getEnd ()).getPointer ());
51
86
});
52
87
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) {
88
+ // Check that the ranges are non-overlapping. If the files really are all
89
+ // distinct, this should be trivially true, but if it's ever not we might end
90
+ // up mis-filing diagnostics.
91
+ assert (ConsumersOrderedByRange.end () ==
92
+ std::adjacent_find (ConsumersOrderedByRange.begin (),
93
+ ConsumersOrderedByRange.end (),
94
+ [](const ConsumersOrderedByRangeEntry &left,
95
+ const ConsumersOrderedByRangeEntry &right) {
58
96
return left.first .overlaps (right.first );
59
97
}) &&
60
- " overlapping ranges given to RangeSpecificDiagnosticConsumer " );
98
+ " overlapping ranges despite having distinct files " );
61
99
}
62
100
63
101
DiagnosticConsumer *
64
- RangeSpecificDiagnosticConsumer::consumerForLocation (SourceLoc loc) const {
65
- // "Find the first range whose end location is greater than or equal to
66
- // 'loc'."
102
+ FileSpecificDiagnosticConsumer::consumerForLocation (SourceManager &SM,
103
+ SourceLoc loc) const {
104
+ // If there's only one consumer, we'll use it no matter what, because...
105
+ // - ...all diagnostics within the file will go to that consumer.
106
+ // - ...all diagnostics not within the file will not be claimed by any
107
+ // consumer, and so will go to all (one) consumers.
108
+ if (SubConsumers.size () == 1 )
109
+ return SubConsumers.front ().second .get ();
110
+
111
+ // Diagnostics with invalid locations always go to every consumer.
112
+ if (loc.isInvalid ())
113
+ return nullptr ;
114
+
115
+ // This map is generated on first use and cached, to allow the
116
+ // FileSpecificDiagnosticConsumer to be set up before the source files are
117
+ // actually loaded.
118
+ if (ConsumersOrderedByRange.empty ()) {
119
+ auto *mutableThis = const_cast <FileSpecificDiagnosticConsumer*>(this );
120
+ mutableThis->computeConsumersOrderedByRange (SM);
121
+ }
122
+
123
+ // This std::lower_bound call is doing a binary search for the first range
124
+ // that /might/ contain 'loc'. Specifically, since the ranges are sorted
125
+ // by end location, it's looking for the first range where the end location
126
+ // is greater than or equal to 'loc'.
67
127
auto possiblyContainingRangeIter =
68
- std::lower_bound (LocationToConsumerMap .begin (),
69
- LocationToConsumerMap .end (),
128
+ std::lower_bound (ConsumersOrderedByRange .begin (),
129
+ ConsumersOrderedByRange .end (),
70
130
loc,
71
- [](const std::pair<CharSourceRange, unsigned > &entry,
131
+ [](const ConsumersOrderedByRangeEntry &entry,
72
132
SourceLoc loc) -> bool {
73
133
auto compare = std::less<const char *>();
74
134
return compare (getRawLoc (entry.first .getEnd ()).getPointer (),
75
135
getRawLoc (loc).getPointer ());
76
136
});
77
137
78
- if (possiblyContainingRangeIter != LocationToConsumerMap .end () &&
138
+ if (possiblyContainingRangeIter != ConsumersOrderedByRange .end () &&
79
139
possiblyContainingRangeIter->first .contains (loc)) {
80
- return SubConsumers[ possiblyContainingRangeIter->second ]. get () ;
140
+ return possiblyContainingRangeIter->second ;
81
141
}
82
142
83
143
return nullptr ;
84
144
}
85
145
86
- void RangeSpecificDiagnosticConsumer ::handleDiagnostic (
146
+ void FileSpecificDiagnosticConsumer ::handleDiagnostic (
87
147
SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind,
88
148
StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
89
149
const DiagnosticInfo &Info) {
@@ -93,7 +153,7 @@ void RangeSpecificDiagnosticConsumer::handleDiagnostic(
93
153
case DiagnosticKind::Error:
94
154
case DiagnosticKind::Warning:
95
155
case DiagnosticKind::Remark:
96
- specificConsumer = consumerForLocation (Loc);
156
+ specificConsumer = consumerForLocation (SM, Loc);
97
157
ConsumerForSubsequentNotes = specificConsumer;
98
158
break ;
99
159
case DiagnosticKind::Note:
@@ -106,18 +166,18 @@ void RangeSpecificDiagnosticConsumer::handleDiagnostic(
106
166
Info);
107
167
} else {
108
168
for (auto &subConsumer : SubConsumers) {
109
- subConsumer->handleDiagnostic (SM, Loc, Kind, FormatString, FormatArgs ,
110
- Info);
169
+ subConsumer. second ->handleDiagnostic (SM, Loc, Kind, FormatString,
170
+ FormatArgs, Info);
111
171
}
112
172
}
113
173
}
114
174
115
- bool RangeSpecificDiagnosticConsumer ::finishProcessing () {
175
+ bool FileSpecificDiagnosticConsumer ::finishProcessing () {
116
176
// Deliberately don't use std::any_of here because we don't want early-exit
117
177
// behavior.
118
178
bool hadError = false ;
119
179
for (auto &subConsumer : SubConsumers)
120
- hadError |= subConsumer->finishProcessing ();
180
+ hadError |= subConsumer. second ->finishProcessing ();
121
181
return hadError;
122
182
}
123
183
0 commit comments