Optional<FileSpecificDiagnosticConsumer::Subconsumer *> FileSpecificDiagnosticConsumer::subconsumerForLocation(SourceManager &SM, SourceLoc loc) { // Diagnostics with invalid locations always go to every consumer. if (loc.isInvalid()) return None; // What if a there's a FileSpecificDiagnosticConsumer but there are no // subconsumers in it? (This situation occurs for the fix-its // FileSpecificDiagnosticConsumer.) In such a case, bail out now. if (Subconsumers.empty()) return None; // This map is generated on first use and cached, to allow the // FileSpecificDiagnosticConsumer to be set up before the source files are // actually loaded. if (ConsumersOrderedByRange.empty()) { // It's possible to get here while a bridging header PCH is being // attached-to, if there's some sort of AST-reader warning or error, which // happens before CompilerInstance::setUpInputs(), at which point _no_ // source buffers are loaded in yet. In that case we return None, rather // than trying to build a nonsensical map (and actually crashing since we // can't find buffers for the inputs). assert(!Subconsumers.empty()); if (!SM.getIDForBufferIdentifier(Subconsumers.begin()->getInputFileName()) .hasValue()) { assert(llvm::none_of(Subconsumers, [&](const Subconsumer &subconsumer) { return SM.getIDForBufferIdentifier(subconsumer.getInputFileName()) .hasValue(); })); return None; } auto *mutableThis = const_cast<FileSpecificDiagnosticConsumer*>(this); mutableThis->computeConsumersOrderedByRange(SM); } // This std::lower_bound call is doing a binary search for the first range // that /might/ contain 'loc'. Specifically, since the ranges are sorted // by end location, it's looking for the first range where the end location // is greater than or equal to 'loc'. const ConsumerAndRange *possiblyContainingRangeIter = std::lower_bound( ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), loc, [](const ConsumerAndRange &entry, SourceLoc loc) -> bool { return entry.endsAfter(loc); }); if (possiblyContainingRangeIter != ConsumersOrderedByRange.end() && possiblyContainingRangeIter->contains(loc)) { auto *consumerAndRangeForLocation = const_cast<ConsumerAndRange *>(possiblyContainingRangeIter); return &(*this)[*consumerAndRangeForLocation]; } return None; }
Optional<FileSpecificDiagnosticConsumer::ConsumerSpecificInformation *> FileSpecificDiagnosticConsumer::consumerSpecificInformationForLocation( SourceManager &SM, SourceLoc loc) const { // Diagnostics with invalid locations always go to every consumer. if (loc.isInvalid()) return None; // This map is generated on first use and cached, to allow the // FileSpecificDiagnosticConsumer to be set up before the source files are // actually loaded. if (ConsumersOrderedByRange.empty()) { // It's possible to get here while a bridging header PCH is being // attached-to, if there's some sort of AST-reader warning or error, which // happens before CompilerInstance::setUpInputs(), at which point _no_ // source buffers are loaded in yet. In that case we return None, rather // than trying to build a nonsensical map (and actually crashing since we // can't find buffers for the inputs). assert(!SubConsumers.empty()); if (!SM.getIDForBufferIdentifier(SubConsumers.begin()->first).hasValue()) { assert(llvm::none_of(SubConsumers, [&](const ConsumerPair &pair) { return SM.getIDForBufferIdentifier(pair.first).hasValue(); })); return None; } auto *mutableThis = const_cast<FileSpecificDiagnosticConsumer*>(this); mutableThis->computeConsumersOrderedByRange(SM); } // This std::lower_bound call is doing a binary search for the first range // that /might/ contain 'loc'. Specifically, since the ranges are sorted // by end location, it's looking for the first range where the end location // is greater than or equal to 'loc'. const ConsumerSpecificInformation *possiblyContainingRangeIter = std::lower_bound( ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), loc, [](const ConsumerSpecificInformation &entry, SourceLoc loc) -> bool { auto compare = std::less<const char *>(); return compare(getRawLoc(entry.range.getEnd()).getPointer(), getRawLoc(loc).getPointer()); }); if (possiblyContainingRangeIter != ConsumersOrderedByRange.end() && possiblyContainingRangeIter->range.contains(loc)) { return const_cast<ConsumerSpecificInformation *>( possiblyContainingRangeIter); } return None; }
void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange( SourceManager &SM) { // Look up each file's source range and add it to the "map" (to be sorted). for (const unsigned subconsumerIndex: indices(Subconsumers)) { const Subconsumer &subconsumer = Subconsumers[subconsumerIndex]; if (subconsumer.getInputFileName().empty()) continue; Optional<unsigned> bufferID = SM.getIDForBufferIdentifier(subconsumer.getInputFileName()); assert(bufferID.hasValue() && "consumer registered for unknown file"); CharSourceRange range = SM.getRangeForBuffer(bufferID.getValue()); ConsumersOrderedByRange.emplace_back( ConsumerAndRange(range, subconsumerIndex)); } // Sort the "map" by buffer /end/ location, for use with std::lower_bound // later. (Sorting by start location would produce the same sort, since the // ranges must not be overlapping, but since we need to check end locations // later it's consistent to sort by that here.) std::sort(ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end()); // Check that the ranges are non-overlapping. If the files really are all // distinct, this should be trivially true, but if it's ever not we might end // up mis-filing diagnostics. assert(ConsumersOrderedByRange.end() == std::adjacent_find(ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), [](const ConsumerAndRange &left, const ConsumerAndRange &right) { return left.overlaps(right); }) && "overlapping ranges despite having distinct files"); }
void FileSpecificDiagnosticConsumer::computeConsumersOrderedByRange( SourceManager &SM) { // Look up each file's source range and add it to the "map" (to be sorted). for (const ConsumerPair &pair : SubConsumers) { if (pair.first.empty()) continue; Optional<unsigned> bufferID = SM.getIDForBufferIdentifier(pair.first); assert(bufferID.hasValue() && "consumer registered for unknown file"); CharSourceRange range = SM.getRangeForBuffer(bufferID.getValue()); ConsumersOrderedByRange.emplace_back( ConsumerSpecificInformation(range, pair.second.get())); } // Sort the "map" by buffer /end/ location, for use with std::lower_bound // later. (Sorting by start location would produce the same sort, since the // ranges must not be overlapping, but since we need to check end locations // later it's consistent to sort by that here.) std::sort(ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), [](const ConsumerSpecificInformation &left, const ConsumerSpecificInformation &right) -> bool { auto compare = std::less<const char *>(); return compare(getRawLoc(left.range.getEnd()).getPointer(), getRawLoc(right.range.getEnd()).getPointer()); }); // Check that the ranges are non-overlapping. If the files really are all // distinct, this should be trivially true, but if it's ever not we might end // up mis-filing diagnostics. assert(ConsumersOrderedByRange.end() == std::adjacent_find(ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), [](const ConsumerSpecificInformation &left, const ConsumerSpecificInformation &right) { return left.range.overlaps(right.range); }) && "overlapping ranges despite having distinct files"); }