1 //===-- lib/Parser/provenance.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "flang/Parser/provenance.h"
10 #include "flang/Common/idioms.h"
11 #include "llvm/Support/raw_ostream.h"
12 #include <algorithm>
13 #include <utility>
14
15 namespace Fortran::parser {
16
ProvenanceRangeToOffsetMappings()17 ProvenanceRangeToOffsetMappings::ProvenanceRangeToOffsetMappings() {}
~ProvenanceRangeToOffsetMappings()18 ProvenanceRangeToOffsetMappings::~ProvenanceRangeToOffsetMappings() {}
19
Put(ProvenanceRange range,std::size_t offset)20 void ProvenanceRangeToOffsetMappings::Put(
21 ProvenanceRange range, std::size_t offset) {
22 auto fromTo{map_.equal_range(range)};
23 for (auto iter{fromTo.first}; iter != fromTo.second; ++iter) {
24 if (range == iter->first) {
25 iter->second = std::min(offset, iter->second);
26 return;
27 }
28 }
29 if (fromTo.second != map_.end()) {
30 map_.emplace_hint(fromTo.second, range, offset);
31 } else {
32 map_.emplace(range, offset);
33 }
34 }
35
Map(ProvenanceRange range) const36 std::optional<std::size_t> ProvenanceRangeToOffsetMappings::Map(
37 ProvenanceRange range) const {
38 auto fromTo{map_.equal_range(range)};
39 std::optional<std::size_t> result;
40 for (auto iter{fromTo.first}; iter != fromTo.second; ++iter) {
41 ProvenanceRange that{iter->first};
42 if (that.Contains(range)) {
43 std::size_t offset{iter->second + that.MemberOffset(range.start())};
44 if (!result || offset < *result) {
45 result = offset;
46 }
47 }
48 }
49 return result;
50 }
51
operator ()(ProvenanceRange before,ProvenanceRange after) const52 bool ProvenanceRangeToOffsetMappings::WhollyPrecedes::operator()(
53 ProvenanceRange before, ProvenanceRange after) const {
54 return before.start() + before.size() <= after.start();
55 }
56
clear()57 void OffsetToProvenanceMappings::clear() { provenanceMap_.clear(); }
58
swap(OffsetToProvenanceMappings & that)59 void OffsetToProvenanceMappings::swap(OffsetToProvenanceMappings &that) {
60 provenanceMap_.swap(that.provenanceMap_);
61 }
62
shrink_to_fit()63 void OffsetToProvenanceMappings::shrink_to_fit() {
64 provenanceMap_.shrink_to_fit();
65 }
66
SizeInBytes() const67 std::size_t OffsetToProvenanceMappings::SizeInBytes() const {
68 if (provenanceMap_.empty()) {
69 return 0;
70 } else {
71 const ContiguousProvenanceMapping &last{provenanceMap_.back()};
72 return last.start + last.range.size();
73 }
74 }
75
Put(ProvenanceRange range)76 void OffsetToProvenanceMappings::Put(ProvenanceRange range) {
77 if (provenanceMap_.empty()) {
78 provenanceMap_.push_back({0, range});
79 } else {
80 ContiguousProvenanceMapping &last{provenanceMap_.back()};
81 if (!last.range.AnnexIfPredecessor(range)) {
82 provenanceMap_.push_back({last.start + last.range.size(), range});
83 }
84 }
85 }
86
Put(const OffsetToProvenanceMappings & that)87 void OffsetToProvenanceMappings::Put(const OffsetToProvenanceMappings &that) {
88 for (const auto &map : that.provenanceMap_) {
89 Put(map.range);
90 }
91 }
92
Map(std::size_t at) const93 ProvenanceRange OffsetToProvenanceMappings::Map(std::size_t at) const {
94 if (provenanceMap_.empty()) {
95 CHECK(at == 0);
96 return {};
97 }
98 std::size_t low{0}, count{provenanceMap_.size()};
99 while (count > 1) {
100 std::size_t mid{low + (count >> 1)};
101 if (provenanceMap_[mid].start > at) {
102 count = mid - low;
103 } else {
104 count -= mid - low;
105 low = mid;
106 }
107 }
108 std::size_t offset{at - provenanceMap_[low].start};
109 return provenanceMap_[low].range.Suffix(offset);
110 }
111
RemoveLastBytes(std::size_t bytes)112 void OffsetToProvenanceMappings::RemoveLastBytes(std::size_t bytes) {
113 for (; bytes > 0; provenanceMap_.pop_back()) {
114 CHECK(!provenanceMap_.empty());
115 ContiguousProvenanceMapping &last{provenanceMap_.back()};
116 std::size_t chunk{last.range.size()};
117 if (bytes < chunk) {
118 last.range = last.range.Prefix(chunk - bytes);
119 break;
120 }
121 bytes -= chunk;
122 }
123 }
124
Invert(const AllSources & allSources) const125 ProvenanceRangeToOffsetMappings OffsetToProvenanceMappings::Invert(
126 const AllSources &allSources) const {
127 ProvenanceRangeToOffsetMappings result;
128 for (const auto &contig : provenanceMap_) {
129 ProvenanceRange range{contig.range};
130 while (!range.empty()) {
131 ProvenanceRange source{allSources.IntersectionWithSourceFiles(range)};
132 if (source.empty()) {
133 break;
134 }
135 result.Put(
136 source, contig.start + contig.range.MemberOffset(source.start()));
137 Provenance after{source.NextAfter()};
138 if (range.Contains(after)) {
139 range = range.Suffix(range.MemberOffset(after));
140 } else {
141 break;
142 }
143 }
144 }
145 return result;
146 }
147
AllSources()148 AllSources::AllSources() : range_{1, 1} {
149 // Start the origin_ array with a dummy entry that has a forced provenance,
150 // so that provenance offset 0 remains reserved as an uninitialized
151 // value.
152 origin_.emplace_back(range_, std::string{'?'});
153 }
154
~AllSources()155 AllSources::~AllSources() {}
156
operator [](Provenance at) const157 const char &AllSources::operator[](Provenance at) const {
158 const Origin &origin{MapToOrigin(at)};
159 return origin[origin.covers.MemberOffset(at)];
160 }
161
ClearSearchPath()162 void AllSources::ClearSearchPath() { searchPath_.clear(); }
163
AppendSearchPathDirectory(std::string directory)164 void AllSources::AppendSearchPathDirectory(std::string directory) {
165 // gfortran and ifort append to current path, PGI prepends
166 searchPath_.push_back(directory);
167 }
168
Open(std::string path,llvm::raw_ostream & error,std::optional<std::string> && prependPath)169 const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error,
170 std::optional<std::string> &&prependPath) {
171 std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
172 if (prependPath) {
173 // Set to "." for the initial source file; set to the directory name
174 // of the including file for #include "quoted-file" directives &
175 // INCLUDE statements.
176 searchPath_.emplace_front(std::move(*prependPath));
177 }
178 std::optional<std::string> found{LocateSourceFile(path, searchPath_)};
179 if (prependPath) {
180 searchPath_.pop_front();
181 }
182 if (!found) {
183 error << "Source file '" << path << "' was not found";
184 return nullptr;
185 } else if (source->Open(*found, error)) {
186 return ownedSourceFiles_.emplace_back(std::move(source)).get();
187 } else {
188 return nullptr;
189 }
190 }
191
ReadStandardInput(llvm::raw_ostream & error)192 const SourceFile *AllSources::ReadStandardInput(llvm::raw_ostream &error) {
193 std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
194 if (source->ReadStandardInput(error)) {
195 return ownedSourceFiles_.emplace_back(std::move(source)).get();
196 }
197 return nullptr;
198 }
199
AddIncludedFile(const SourceFile & source,ProvenanceRange from,bool isModule)200 ProvenanceRange AllSources::AddIncludedFile(
201 const SourceFile &source, ProvenanceRange from, bool isModule) {
202 ProvenanceRange covers{range_.NextAfter(), source.bytes()};
203 CHECK(range_.AnnexIfPredecessor(covers));
204 CHECK(origin_.back().covers.ImmediatelyPrecedes(covers));
205 origin_.emplace_back(covers, source, from, isModule);
206 return covers;
207 }
208
AddMacroCall(ProvenanceRange def,ProvenanceRange use,const std::string & expansion)209 ProvenanceRange AllSources::AddMacroCall(
210 ProvenanceRange def, ProvenanceRange use, const std::string &expansion) {
211 ProvenanceRange covers{range_.NextAfter(), expansion.size()};
212 CHECK(range_.AnnexIfPredecessor(covers));
213 CHECK(origin_.back().covers.ImmediatelyPrecedes(covers));
214 origin_.emplace_back(covers, def, use, expansion);
215 return covers;
216 }
217
AddCompilerInsertion(std::string text)218 ProvenanceRange AllSources::AddCompilerInsertion(std::string text) {
219 ProvenanceRange covers{range_.NextAfter(), text.size()};
220 CHECK(range_.AnnexIfPredecessor(covers));
221 CHECK(origin_.back().covers.ImmediatelyPrecedes(covers));
222 origin_.emplace_back(covers, text);
223 return covers;
224 }
225
EmitMessage(llvm::raw_ostream & o,const std::optional<ProvenanceRange> & range,const std::string & message,bool echoSourceLine) const226 void AllSources::EmitMessage(llvm::raw_ostream &o,
227 const std::optional<ProvenanceRange> &range, const std::string &message,
228 bool echoSourceLine) const {
229 if (!range) {
230 o << message << '\n';
231 return;
232 }
233 CHECK(IsValid(*range));
234 const Origin &origin{MapToOrigin(range->start())};
235 common::visit(
236 common::visitors{
237 [&](const Inclusion &inc) {
238 o << inc.source.path();
239 std::size_t offset{origin.covers.MemberOffset(range->start())};
240 SourcePosition pos{inc.source.FindOffsetLineAndColumn(offset)};
241 o << ':' << pos.line << ':' << pos.column;
242 o << ": " << message << '\n';
243 if (echoSourceLine) {
244 const char *text{inc.source.content().data() +
245 inc.source.GetLineStartOffset(pos.line)};
246 o << " ";
247 for (const char *p{text}; *p != '\n'; ++p) {
248 o << *p;
249 }
250 o << "\n ";
251 for (int j{1}; j < pos.column; ++j) {
252 char ch{text[j - 1]};
253 o << (ch == '\t' ? '\t' : ' ');
254 }
255 o << '^';
256 if (range->size() > 1) {
257 auto last{range->start() + range->size() - 1};
258 if (&MapToOrigin(last) == &origin) {
259 auto endOffset{origin.covers.MemberOffset(last)};
260 auto endPos{inc.source.FindOffsetLineAndColumn(endOffset)};
261 if (pos.line == endPos.line) {
262 for (int j{pos.column}; j < endPos.column; ++j) {
263 o << '^';
264 }
265 }
266 }
267 }
268 o << '\n';
269 }
270 if (IsValid(origin.replaces)) {
271 EmitMessage(o, origin.replaces,
272 inc.isModule ? "used here"s : "included here"s,
273 echoSourceLine);
274 }
275 },
276 [&](const Macro &mac) {
277 EmitMessage(o, origin.replaces, message, echoSourceLine);
278 EmitMessage(
279 o, mac.definition, "in a macro defined here", echoSourceLine);
280 if (echoSourceLine) {
281 o << "that expanded to:\n " << mac.expansion << "\n ";
282 for (std::size_t j{0};
283 origin.covers.OffsetMember(j) < range->start(); ++j) {
284 o << (mac.expansion[j] == '\t' ? '\t' : ' ');
285 }
286 o << "^\n";
287 }
288 },
289 [&](const CompilerInsertion &) { o << message << '\n'; },
290 },
291 origin.u);
292 }
293
GetSourceFile(Provenance at,std::size_t * offset) const294 const SourceFile *AllSources::GetSourceFile(
295 Provenance at, std::size_t *offset) const {
296 const Origin &origin{MapToOrigin(at)};
297 return common::visit(common::visitors{
298 [&](const Inclusion &inc) {
299 if (offset) {
300 *offset = origin.covers.MemberOffset(at);
301 }
302 return &inc.source;
303 },
304 [&](const Macro &) {
305 return GetSourceFile(
306 origin.replaces.start(), offset);
307 },
308 [offset](const CompilerInsertion &) {
309 if (offset) {
310 *offset = 0;
311 }
312 return static_cast<const SourceFile *>(nullptr);
313 },
314 },
315 origin.u);
316 }
317
GetSource(ProvenanceRange range) const318 const char *AllSources::GetSource(ProvenanceRange range) const {
319 Provenance start{range.start()};
320 const Origin &origin{MapToOrigin(start)};
321 return origin.covers.Contains(range)
322 ? &origin[origin.covers.MemberOffset(start)]
323 : nullptr;
324 }
325
GetSourcePosition(Provenance prov) const326 std::optional<SourcePosition> AllSources::GetSourcePosition(
327 Provenance prov) const {
328 const Origin &origin{MapToOrigin(prov)};
329 return common::visit(
330 common::visitors{
331 [&](const Inclusion &inc) -> std::optional<SourcePosition> {
332 std::size_t offset{origin.covers.MemberOffset(prov)};
333 return inc.source.FindOffsetLineAndColumn(offset);
334 },
335 [&](const Macro &) {
336 return GetSourcePosition(origin.replaces.start());
337 },
338 [](const CompilerInsertion &) -> std::optional<SourcePosition> {
339 return std::nullopt;
340 },
341 },
342 origin.u);
343 }
344
GetFirstFileProvenance() const345 std::optional<ProvenanceRange> AllSources::GetFirstFileProvenance() const {
346 for (const auto &origin : origin_) {
347 if (std::holds_alternative<Inclusion>(origin.u)) {
348 return origin.covers;
349 }
350 }
351 return std::nullopt;
352 }
353
GetPath(Provenance at) const354 std::string AllSources::GetPath(Provenance at) const {
355 const SourceFile *source{GetSourceFile(at)};
356 return source ? source->path() : ""s;
357 }
358
GetLineNumber(Provenance at) const359 int AllSources::GetLineNumber(Provenance at) const {
360 std::size_t offset{0};
361 const SourceFile *source{GetSourceFile(at, &offset)};
362 return source ? source->FindOffsetLineAndColumn(offset).line : 0;
363 }
364
CompilerInsertionProvenance(char ch)365 Provenance AllSources::CompilerInsertionProvenance(char ch) {
366 auto iter{compilerInsertionProvenance_.find(ch)};
367 if (iter != compilerInsertionProvenance_.end()) {
368 return iter->second;
369 }
370 ProvenanceRange newCharRange{AddCompilerInsertion(std::string{ch})};
371 Provenance newCharProvenance{newCharRange.start()};
372 compilerInsertionProvenance_.insert(std::make_pair(ch, newCharProvenance));
373 return newCharProvenance;
374 }
375
IntersectionWithSourceFiles(ProvenanceRange range) const376 ProvenanceRange AllSources::IntersectionWithSourceFiles(
377 ProvenanceRange range) const {
378 if (range.empty()) {
379 return {};
380 } else {
381 const Origin &origin{MapToOrigin(range.start())};
382 if (std::holds_alternative<Inclusion>(origin.u)) {
383 return range.Intersection(origin.covers);
384 } else {
385 auto skip{
386 origin.covers.size() - origin.covers.MemberOffset(range.start())};
387 return IntersectionWithSourceFiles(range.Suffix(skip));
388 }
389 }
390 }
391
Origin(ProvenanceRange r,const SourceFile & source)392 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source)
393 : u{Inclusion{source}}, covers{r} {}
Origin(ProvenanceRange r,const SourceFile & included,ProvenanceRange from,bool isModule)394 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &included,
395 ProvenanceRange from, bool isModule)
396 : u{Inclusion{included, isModule}}, covers{r}, replaces{from} {}
Origin(ProvenanceRange r,ProvenanceRange def,ProvenanceRange use,const std::string & expansion)397 AllSources::Origin::Origin(ProvenanceRange r, ProvenanceRange def,
398 ProvenanceRange use, const std::string &expansion)
399 : u{Macro{def, expansion}}, covers{r}, replaces{use} {}
Origin(ProvenanceRange r,const std::string & text)400 AllSources::Origin::Origin(ProvenanceRange r, const std::string &text)
401 : u{CompilerInsertion{text}}, covers{r} {}
402
operator [](std::size_t n) const403 const char &AllSources::Origin::operator[](std::size_t n) const {
404 return common::visit(
405 common::visitors{
406 [n](const Inclusion &inc) -> const char & {
407 return inc.source.content()[n];
408 },
409 [n](const Macro &mac) -> const char & { return mac.expansion[n]; },
410 [n](const CompilerInsertion &ins) -> const char & {
411 return ins.text[n];
412 },
413 },
414 u);
415 }
416
MapToOrigin(Provenance at) const417 const AllSources::Origin &AllSources::MapToOrigin(Provenance at) const {
418 CHECK(range_.Contains(at));
419 std::size_t low{0}, count{origin_.size()};
420 while (count > 1) {
421 std::size_t mid{low + (count >> 1)};
422 if (at < origin_[mid].covers.start()) {
423 count = mid - low;
424 } else {
425 count -= mid - low;
426 low = mid;
427 }
428 }
429 CHECK(origin_[low].covers.Contains(at));
430 return origin_[low];
431 }
432
GetProvenanceRange(CharBlock cookedRange) const433 std::optional<ProvenanceRange> CookedSource::GetProvenanceRange(
434 CharBlock cookedRange) const {
435 if (!AsCharBlock().Contains(cookedRange)) {
436 return std::nullopt;
437 }
438 ProvenanceRange first{provenanceMap_.Map(cookedRange.begin() - &data_[0])};
439 if (cookedRange.size() <= first.size()) { // always true when empty
440 return first.Prefix(cookedRange.size());
441 }
442 ProvenanceRange last{provenanceMap_.Map(cookedRange.end() - 1 - &data_[0])};
443 if (first.start() <= last.start()) {
444 return {ProvenanceRange{first.start(), last.start() - first.start() + 1}};
445 } else {
446 return std::nullopt;
447 }
448 }
449
GetCharBlock(ProvenanceRange range) const450 std::optional<CharBlock> CookedSource::GetCharBlock(
451 ProvenanceRange range) const {
452 CHECK(!invertedMap_.empty() &&
453 "CompileProvenanceRangeToOffsetMappings not called");
454 if (auto to{invertedMap_.Map(range)}) {
455 return CharBlock{data_.c_str() + *to, range.size()};
456 } else {
457 return std::nullopt;
458 }
459 }
460
BufferedBytes() const461 std::size_t CookedSource::BufferedBytes() const { return buffer_.bytes(); }
462
Marshal(AllCookedSources & allCookedSources)463 void CookedSource::Marshal(AllCookedSources &allCookedSources) {
464 CHECK(provenanceMap_.SizeInBytes() == buffer_.bytes());
465 provenanceMap_.Put(allCookedSources.allSources().AddCompilerInsertion(
466 "(after end of source)"));
467 data_ = buffer_.Marshal();
468 buffer_.clear();
469 allCookedSources.Register(*this);
470 }
471
CompileProvenanceRangeToOffsetMappings(AllSources & allSources)472 void CookedSource::CompileProvenanceRangeToOffsetMappings(
473 AllSources &allSources) {
474 if (invertedMap_.empty()) {
475 invertedMap_ = provenanceMap_.Invert(allSources);
476 }
477 }
478
DumpRange(llvm::raw_ostream & o,const ProvenanceRange & r)479 static void DumpRange(llvm::raw_ostream &o, const ProvenanceRange &r) {
480 o << "[" << r.start().offset() << ".." << r.Last().offset() << "] ("
481 << r.size() << " bytes)";
482 }
483
Dump(llvm::raw_ostream & o) const484 llvm::raw_ostream &ProvenanceRangeToOffsetMappings::Dump(
485 llvm::raw_ostream &o) const {
486 for (const auto &m : map_) {
487 o << "provenances ";
488 DumpRange(o, m.first);
489 o << " -> offsets [" << m.second << ".." << (m.second + m.first.size() - 1)
490 << "]\n";
491 }
492 return o;
493 }
494
Dump(llvm::raw_ostream & o) const495 llvm::raw_ostream &OffsetToProvenanceMappings::Dump(
496 llvm::raw_ostream &o) const {
497 for (const ContiguousProvenanceMapping &m : provenanceMap_) {
498 std::size_t n{m.range.size()};
499 o << "offsets [" << m.start << ".." << (m.start + n - 1)
500 << "] -> provenances ";
501 DumpRange(o, m.range);
502 o << '\n';
503 }
504 return o;
505 }
506
Dump(llvm::raw_ostream & o) const507 llvm::raw_ostream &AllSources::Dump(llvm::raw_ostream &o) const {
508 o << "AllSources range_ ";
509 DumpRange(o, range_);
510 o << '\n';
511 for (const Origin &m : origin_) {
512 o << " ";
513 DumpRange(o, m.covers);
514 o << " -> ";
515 common::visit(common::visitors{
516 [&](const Inclusion &inc) {
517 if (inc.isModule) {
518 o << "module ";
519 }
520 o << "file " << inc.source.path();
521 },
522 [&](const Macro &mac) { o << "macro " << mac.expansion; },
523 [&](const CompilerInsertion &ins) {
524 o << "compiler '" << ins.text << '\'';
525 if (ins.text.length() == 1) {
526 int ch = ins.text[0];
527 o << "(0x";
528 o.write_hex(ch & 0xff) << ")";
529 }
530 },
531 },
532 m.u);
533 if (IsValid(m.replaces)) {
534 o << " replaces ";
535 DumpRange(o, m.replaces);
536 }
537 o << '\n';
538 }
539 return o;
540 }
541
Dump(llvm::raw_ostream & o) const542 llvm::raw_ostream &CookedSource::Dump(llvm::raw_ostream &o) const {
543 o << "CookedSource::provenanceMap_:\n";
544 provenanceMap_.Dump(o);
545 o << "CookedSource::invertedMap_:\n";
546 invertedMap_.Dump(o);
547 return o;
548 }
549
AllCookedSources(AllSources & s)550 AllCookedSources::AllCookedSources(AllSources &s) : allSources_{s} {}
~AllCookedSources()551 AllCookedSources::~AllCookedSources() {}
552
NewCookedSource()553 CookedSource &AllCookedSources::NewCookedSource() {
554 return cooked_.emplace_back();
555 }
556
Find(CharBlock x) const557 const CookedSource *AllCookedSources::Find(CharBlock x) const {
558 auto pair{index_.equal_range(x)};
559 for (auto iter{pair.first}; iter != pair.second; ++iter) {
560 if (iter->second.AsCharBlock().Contains(x)) {
561 return &iter->second;
562 }
563 }
564 return nullptr;
565 }
566
GetProvenanceRange(CharBlock cb) const567 std::optional<ProvenanceRange> AllCookedSources::GetProvenanceRange(
568 CharBlock cb) const {
569 if (const CookedSource * c{Find(cb)}) {
570 return c->GetProvenanceRange(cb);
571 } else {
572 return std::nullopt;
573 }
574 }
575
GetCharBlockFromLineAndColumns(int line,int startColumn,int endColumn) const576 std::optional<CharBlock> AllCookedSources::GetCharBlockFromLineAndColumns(
577 int line, int startColumn, int endColumn) const {
578 // 2nd column is exclusive, meaning it is target column + 1.
579 CHECK(line > 0 && startColumn > 0 && endColumn > 0);
580 CHECK(startColumn < endColumn);
581 auto provenanceStart{allSources_.GetFirstFileProvenance().value().start()};
582 if (auto sourceFile{allSources_.GetSourceFile(provenanceStart)}) {
583 CHECK(line <= static_cast<int>(sourceFile->lines()));
584 return GetCharBlock(ProvenanceRange(sourceFile->GetLineStartOffset(line) +
585 provenanceStart.offset() + startColumn - 1,
586 endColumn - startColumn));
587 }
588 return std::nullopt;
589 }
590
591 std::optional<std::pair<SourcePosition, SourcePosition>>
GetSourcePositionRange(CharBlock cookedRange) const592 AllCookedSources::GetSourcePositionRange(CharBlock cookedRange) const {
593 if (auto range{GetProvenanceRange(cookedRange)}) {
594 if (auto firstOffset{allSources_.GetSourcePosition(range->start())}) {
595 if (auto secondOffset{
596 allSources_.GetSourcePosition(range->start() + range->size())}) {
597 return std::pair{*firstOffset, *secondOffset};
598 }
599 }
600 }
601 return std::nullopt;
602 }
603
GetCharBlock(ProvenanceRange range) const604 std::optional<CharBlock> AllCookedSources::GetCharBlock(
605 ProvenanceRange range) const {
606 for (const auto &c : cooked_) {
607 if (auto result{c.GetCharBlock(range)}) {
608 return result;
609 }
610 }
611 return std::nullopt;
612 }
613
Dump(llvm::raw_ostream & o) const614 void AllCookedSources::Dump(llvm::raw_ostream &o) const {
615 o << "AllSources:\n";
616 allSources_.Dump(o);
617 for (const auto &c : cooked_) {
618 c.Dump(o);
619 }
620 }
621
Precedes(CharBlock x,CharBlock y) const622 bool AllCookedSources::Precedes(CharBlock x, CharBlock y) const {
623 if (const CookedSource * xSource{Find(x)}) {
624 if (xSource->AsCharBlock().Contains(y)) {
625 return x.begin() < y.begin();
626 } else if (const CookedSource * ySource{Find(y)}) {
627 return xSource->number() < ySource->number();
628 } else {
629 return true; // by fiat, all cooked source < anything outside
630 }
631 } else if (Find(y)) {
632 return false;
633 } else {
634 // Both names are compiler-created (SaveTempName).
635 return x < y;
636 }
637 }
638
Register(CookedSource & cooked)639 void AllCookedSources::Register(CookedSource &cooked) {
640 index_.emplace(cooked.AsCharBlock(), cooked);
641 cooked.set_number(static_cast<int>(index_.size()));
642 }
643
644 } // namespace Fortran::parser
645