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 
AppendSearchPathDirectory(std::string directory)162 void AllSources::AppendSearchPathDirectory(std::string directory) {
163   // gfortran and ifort append to current path, PGI prepends
164   searchPath_.push_back(directory);
165 }
166 
Open(std::string path,llvm::raw_ostream & error,std::optional<std::string> && prependPath)167 const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error,
168     std::optional<std::string> &&prependPath) {
169   std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
170   if (prependPath) {
171     // Set to "." for the initial source file; set to the directory name
172     // of the including file for #include "quoted-file" directives &
173     // INCLUDE statements.
174     searchPath_.emplace_front(std::move(*prependPath));
175   }
176   std::optional<std::string> found{LocateSourceFile(path, searchPath_)};
177   if (prependPath) {
178     searchPath_.pop_front();
179   }
180   if (!found) {
181     error << "Source file '" << path << "' was not found";
182     return nullptr;
183   } else if (source->Open(*found, error)) {
184     return ownedSourceFiles_.emplace_back(std::move(source)).get();
185   } else {
186     return nullptr;
187   }
188 }
189 
ReadStandardInput(llvm::raw_ostream & error)190 const SourceFile *AllSources::ReadStandardInput(llvm::raw_ostream &error) {
191   std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
192   if (source->ReadStandardInput(error)) {
193     return ownedSourceFiles_.emplace_back(std::move(source)).get();
194   }
195   return nullptr;
196 }
197 
AddIncludedFile(const SourceFile & source,ProvenanceRange from,bool isModule)198 ProvenanceRange AllSources::AddIncludedFile(
199     const SourceFile &source, ProvenanceRange from, bool isModule) {
200   ProvenanceRange covers{range_.NextAfter(), source.bytes()};
201   CHECK(range_.AnnexIfPredecessor(covers));
202   CHECK(origin_.back().covers.ImmediatelyPrecedes(covers));
203   origin_.emplace_back(covers, source, from, isModule);
204   return covers;
205 }
206 
AddMacroCall(ProvenanceRange def,ProvenanceRange use,const std::string & expansion)207 ProvenanceRange AllSources::AddMacroCall(
208     ProvenanceRange def, ProvenanceRange use, const std::string &expansion) {
209   ProvenanceRange covers{range_.NextAfter(), expansion.size()};
210   CHECK(range_.AnnexIfPredecessor(covers));
211   CHECK(origin_.back().covers.ImmediatelyPrecedes(covers));
212   origin_.emplace_back(covers, def, use, expansion);
213   return covers;
214 }
215 
AddCompilerInsertion(std::string text)216 ProvenanceRange AllSources::AddCompilerInsertion(std::string text) {
217   ProvenanceRange covers{range_.NextAfter(), text.size()};
218   CHECK(range_.AnnexIfPredecessor(covers));
219   CHECK(origin_.back().covers.ImmediatelyPrecedes(covers));
220   origin_.emplace_back(covers, text);
221   return covers;
222 }
223 
EmitMessage(llvm::raw_ostream & o,const std::optional<ProvenanceRange> & range,const std::string & message,bool echoSourceLine) const224 void AllSources::EmitMessage(llvm::raw_ostream &o,
225     const std::optional<ProvenanceRange> &range, const std::string &message,
226     bool echoSourceLine) const {
227   if (!range) {
228     o << message << '\n';
229     return;
230   }
231   CHECK(IsValid(*range));
232   const Origin &origin{MapToOrigin(range->start())};
233   std::visit(
234       common::visitors{
235           [&](const Inclusion &inc) {
236             o << inc.source.path();
237             std::size_t offset{origin.covers.MemberOffset(range->start())};
238             SourcePosition pos{inc.source.FindOffsetLineAndColumn(offset)};
239             o << ':' << pos.line << ':' << pos.column;
240             o << ": " << message << '\n';
241             if (echoSourceLine) {
242               const char *text{inc.source.content().data() +
243                   inc.source.GetLineStartOffset(pos.line)};
244               o << "  ";
245               for (const char *p{text}; *p != '\n'; ++p) {
246                 o << *p;
247               }
248               o << "\n  ";
249               for (int j{1}; j < pos.column; ++j) {
250                 char ch{text[j - 1]};
251                 o << (ch == '\t' ? '\t' : ' ');
252               }
253               o << '^';
254               if (range->size() > 1) {
255                 auto last{range->start() + range->size() - 1};
256                 if (&MapToOrigin(last) == &origin) {
257                   auto endOffset{origin.covers.MemberOffset(last)};
258                   auto endPos{inc.source.FindOffsetLineAndColumn(endOffset)};
259                   if (pos.line == endPos.line) {
260                     for (int j{pos.column}; j < endPos.column; ++j) {
261                       o << '^';
262                     }
263                   }
264                 }
265               }
266               o << '\n';
267             }
268             if (IsValid(origin.replaces)) {
269               EmitMessage(o, origin.replaces,
270                   inc.isModule ? "used here"s : "included here"s,
271                   echoSourceLine);
272             }
273           },
274           [&](const Macro &mac) {
275             EmitMessage(o, origin.replaces, message, echoSourceLine);
276             EmitMessage(
277                 o, mac.definition, "in a macro defined here", echoSourceLine);
278             if (echoSourceLine) {
279               o << "that expanded to:\n  " << mac.expansion << "\n  ";
280               for (std::size_t j{0};
281                    origin.covers.OffsetMember(j) < range->start(); ++j) {
282                 o << (mac.expansion[j] == '\t' ? '\t' : ' ');
283               }
284               o << "^\n";
285             }
286           },
287           [&](const CompilerInsertion &) { o << message << '\n'; },
288       },
289       origin.u);
290 }
291 
GetSourceFile(Provenance at,std::size_t * offset) const292 const SourceFile *AllSources::GetSourceFile(
293     Provenance at, std::size_t *offset) const {
294   const Origin &origin{MapToOrigin(at)};
295   return std::visit(common::visitors{
296                         [&](const Inclusion &inc) {
297                           if (offset) {
298                             *offset = origin.covers.MemberOffset(at);
299                           }
300                           return &inc.source;
301                         },
302                         [&](const Macro &) {
303                           return GetSourceFile(origin.replaces.start(), offset);
304                         },
305                         [offset](const CompilerInsertion &) {
306                           if (offset) {
307                             *offset = 0;
308                           }
309                           return static_cast<const SourceFile *>(nullptr);
310                         },
311                     },
312       origin.u);
313 }
314 
GetSource(ProvenanceRange range) const315 const char *AllSources::GetSource(ProvenanceRange range) const {
316   Provenance start{range.start()};
317   const Origin &origin{MapToOrigin(start)};
318   return origin.covers.Contains(range)
319       ? &origin[origin.covers.MemberOffset(start)]
320       : nullptr;
321 }
322 
GetSourcePosition(Provenance prov) const323 std::optional<SourcePosition> AllSources::GetSourcePosition(
324     Provenance prov) const {
325   const Origin &origin{MapToOrigin(prov)};
326   if (const auto *inc{std::get_if<Inclusion>(&origin.u)}) {
327     std::size_t offset{origin.covers.MemberOffset(prov)};
328     return inc->source.FindOffsetLineAndColumn(offset);
329   } else {
330     return std::nullopt;
331   }
332 }
333 
GetFirstFileProvenance() const334 std::optional<ProvenanceRange> AllSources::GetFirstFileProvenance() const {
335   for (const auto &origin : origin_) {
336     if (std::holds_alternative<Inclusion>(origin.u)) {
337       return origin.covers;
338     }
339   }
340   return std::nullopt;
341 }
342 
GetPath(Provenance at) const343 std::string AllSources::GetPath(Provenance at) const {
344   const SourceFile *source{GetSourceFile(at)};
345   return source ? source->path() : ""s;
346 }
347 
GetLineNumber(Provenance at) const348 int AllSources::GetLineNumber(Provenance at) const {
349   std::size_t offset{0};
350   const SourceFile *source{GetSourceFile(at, &offset)};
351   return source ? source->FindOffsetLineAndColumn(offset).line : 0;
352 }
353 
CompilerInsertionProvenance(char ch)354 Provenance AllSources::CompilerInsertionProvenance(char ch) {
355   auto iter{compilerInsertionProvenance_.find(ch)};
356   if (iter != compilerInsertionProvenance_.end()) {
357     return iter->second;
358   }
359   ProvenanceRange newCharRange{AddCompilerInsertion(std::string{ch})};
360   Provenance newCharProvenance{newCharRange.start()};
361   compilerInsertionProvenance_.insert(std::make_pair(ch, newCharProvenance));
362   return newCharProvenance;
363 }
364 
IntersectionWithSourceFiles(ProvenanceRange range) const365 ProvenanceRange AllSources::IntersectionWithSourceFiles(
366     ProvenanceRange range) const {
367   if (range.empty()) {
368     return {};
369   } else {
370     const Origin &origin{MapToOrigin(range.start())};
371     if (std::holds_alternative<Inclusion>(origin.u)) {
372       return range.Intersection(origin.covers);
373     } else {
374       auto skip{
375           origin.covers.size() - origin.covers.MemberOffset(range.start())};
376       return IntersectionWithSourceFiles(range.Suffix(skip));
377     }
378   }
379 }
380 
Origin(ProvenanceRange r,const SourceFile & source)381 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source)
382     : u{Inclusion{source}}, covers{r} {}
Origin(ProvenanceRange r,const SourceFile & included,ProvenanceRange from,bool isModule)383 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &included,
384     ProvenanceRange from, bool isModule)
385     : u{Inclusion{included, isModule}}, covers{r}, replaces{from} {}
Origin(ProvenanceRange r,ProvenanceRange def,ProvenanceRange use,const std::string & expansion)386 AllSources::Origin::Origin(ProvenanceRange r, ProvenanceRange def,
387     ProvenanceRange use, const std::string &expansion)
388     : u{Macro{def, expansion}}, covers{r}, replaces{use} {}
Origin(ProvenanceRange r,const std::string & text)389 AllSources::Origin::Origin(ProvenanceRange r, const std::string &text)
390     : u{CompilerInsertion{text}}, covers{r} {}
391 
operator [](std::size_t n) const392 const char &AllSources::Origin::operator[](std::size_t n) const {
393   return std::visit(
394       common::visitors{
395           [n](const Inclusion &inc) -> const char & {
396             return inc.source.content()[n];
397           },
398           [n](const Macro &mac) -> const char & { return mac.expansion[n]; },
399           [n](const CompilerInsertion &ins) -> const char & {
400             return ins.text[n];
401           },
402       },
403       u);
404 }
405 
MapToOrigin(Provenance at) const406 const AllSources::Origin &AllSources::MapToOrigin(Provenance at) const {
407   CHECK(range_.Contains(at));
408   std::size_t low{0}, count{origin_.size()};
409   while (count > 1) {
410     std::size_t mid{low + (count >> 1)};
411     if (at < origin_[mid].covers.start()) {
412       count = mid - low;
413     } else {
414       count -= mid - low;
415       low = mid;
416     }
417   }
418   CHECK(origin_[low].covers.Contains(at));
419   return origin_[low];
420 }
421 
GetProvenanceRange(CharBlock cookedRange) const422 std::optional<ProvenanceRange> CookedSource::GetProvenanceRange(
423     CharBlock cookedRange) const {
424   if (!AsCharBlock().Contains(cookedRange)) {
425     return std::nullopt;
426   }
427   ProvenanceRange first{provenanceMap_.Map(cookedRange.begin() - &data_[0])};
428   if (cookedRange.size() <= first.size()) {
429     return first.Prefix(cookedRange.size());
430   }
431   ProvenanceRange last{provenanceMap_.Map(cookedRange.end() - &data_[0])};
432   return {ProvenanceRange{first.start(), last.start() - first.start()}};
433 }
434 
GetCharBlock(ProvenanceRange range) const435 std::optional<CharBlock> CookedSource::GetCharBlock(
436     ProvenanceRange range) const {
437   CHECK(!invertedMap_.empty() &&
438       "CompileProvenanceRangeToOffsetMappings not called");
439   if (auto to{invertedMap_.Map(range)}) {
440     return CharBlock{data_.c_str() + *to, range.size()};
441   } else {
442     return std::nullopt;
443   }
444 }
445 
BufferedBytes() const446 std::size_t CookedSource::BufferedBytes() const { return buffer_.bytes(); }
447 
Marshal(AllCookedSources & allCookedSources)448 void CookedSource::Marshal(AllCookedSources &allCookedSources) {
449   CHECK(provenanceMap_.SizeInBytes() == buffer_.bytes());
450   provenanceMap_.Put(allCookedSources.allSources().AddCompilerInsertion(
451       "(after end of source)"));
452   data_ = buffer_.Marshal();
453   buffer_.clear();
454   allCookedSources.Register(*this);
455 }
456 
CompileProvenanceRangeToOffsetMappings(AllSources & allSources)457 void CookedSource::CompileProvenanceRangeToOffsetMappings(
458     AllSources &allSources) {
459   if (invertedMap_.empty()) {
460     invertedMap_ = provenanceMap_.Invert(allSources);
461   }
462 }
463 
DumpRange(llvm::raw_ostream & o,const ProvenanceRange & r)464 static void DumpRange(llvm::raw_ostream &o, const ProvenanceRange &r) {
465   o << "[" << r.start().offset() << ".." << r.Last().offset() << "] ("
466     << r.size() << " bytes)";
467 }
468 
Dump(llvm::raw_ostream & o) const469 llvm::raw_ostream &ProvenanceRangeToOffsetMappings::Dump(
470     llvm::raw_ostream &o) const {
471   for (const auto &m : map_) {
472     o << "provenances ";
473     DumpRange(o, m.first);
474     o << " -> offsets [" << m.second << ".." << (m.second + m.first.size() - 1)
475       << "]\n";
476   }
477   return o;
478 }
479 
Dump(llvm::raw_ostream & o) const480 llvm::raw_ostream &OffsetToProvenanceMappings::Dump(
481     llvm::raw_ostream &o) const {
482   for (const ContiguousProvenanceMapping &m : provenanceMap_) {
483     std::size_t n{m.range.size()};
484     o << "offsets [" << m.start << ".." << (m.start + n - 1)
485       << "] -> provenances ";
486     DumpRange(o, m.range);
487     o << '\n';
488   }
489   return o;
490 }
491 
Dump(llvm::raw_ostream & o) const492 llvm::raw_ostream &AllSources::Dump(llvm::raw_ostream &o) const {
493   o << "AllSources range_ ";
494   DumpRange(o, range_);
495   o << '\n';
496   for (const Origin &m : origin_) {
497     o << "   ";
498     DumpRange(o, m.covers);
499     o << " -> ";
500     std::visit(common::visitors{
501                    [&](const Inclusion &inc) {
502                      if (inc.isModule) {
503                        o << "module ";
504                      }
505                      o << "file " << inc.source.path();
506                    },
507                    [&](const Macro &mac) { o << "macro " << mac.expansion; },
508                    [&](const CompilerInsertion &ins) {
509                      o << "compiler '" << ins.text << '\'';
510                      if (ins.text.length() == 1) {
511                        int ch = ins.text[0];
512                        o << "(0x";
513                        o.write_hex(ch & 0xff) << ")";
514                      }
515                    },
516                },
517         m.u);
518     if (IsValid(m.replaces)) {
519       o << " replaces ";
520       DumpRange(o, m.replaces);
521     }
522     o << '\n';
523   }
524   return o;
525 }
526 
Dump(llvm::raw_ostream & o) const527 llvm::raw_ostream &CookedSource::Dump(llvm::raw_ostream &o) const {
528   o << "CookedSource::provenanceMap_:\n";
529   provenanceMap_.Dump(o);
530   o << "CookedSource::invertedMap_:\n";
531   invertedMap_.Dump(o);
532   return o;
533 }
534 
AllCookedSources(AllSources & s)535 AllCookedSources::AllCookedSources(AllSources &s) : allSources_{s} {}
~AllCookedSources()536 AllCookedSources::~AllCookedSources() {}
537 
NewCookedSource()538 CookedSource &AllCookedSources::NewCookedSource() {
539   return cooked_.emplace_back();
540 }
541 
Find(CharBlock x) const542 const CookedSource *AllCookedSources::Find(CharBlock x) const {
543   auto pair{index_.equal_range(x)};
544   for (auto iter{pair.first}; iter != pair.second; ++iter) {
545     if (iter->second.AsCharBlock().Contains(x)) {
546       return &iter->second;
547     }
548   }
549   return nullptr;
550 }
551 
GetProvenanceRange(CharBlock cb) const552 std::optional<ProvenanceRange> AllCookedSources::GetProvenanceRange(
553     CharBlock cb) const {
554   if (const CookedSource * c{Find(cb)}) {
555     return c->GetProvenanceRange(cb);
556   } else {
557     return std::nullopt;
558   }
559 }
560 
GetCharBlockFromLineAndColumns(int line,int startColumn,int endColumn) const561 std::optional<CharBlock> AllCookedSources::GetCharBlockFromLineAndColumns(
562     int line, int startColumn, int endColumn) const {
563   // 2nd column is exclusive, meaning it is target column + 1.
564   CHECK(line > 0 && startColumn > 0 && endColumn > 0);
565   CHECK(startColumn < endColumn);
566   auto provenanceStart{allSources_.GetFirstFileProvenance().value().start()};
567   if (auto sourceFile{allSources_.GetSourceFile(provenanceStart)}) {
568     CHECK(line <= static_cast<int>(sourceFile->lines()));
569     return GetCharBlock(ProvenanceRange(sourceFile->GetLineStartOffset(line) +
570             provenanceStart.offset() + startColumn - 1,
571         endColumn - startColumn));
572   }
573   return std::nullopt;
574 }
575 
576 std::optional<std::pair<SourcePosition, SourcePosition>>
GetSourcePositionRange(CharBlock cookedRange) const577 AllCookedSources::GetSourcePositionRange(CharBlock cookedRange) const {
578   if (auto range{GetProvenanceRange(cookedRange)}) {
579     if (auto firstOffset{allSources_.GetSourcePosition(range->start())}) {
580       if (auto secondOffset{
581               allSources_.GetSourcePosition(range->start() + range->size())}) {
582         return std::pair{*firstOffset, *secondOffset};
583       }
584     }
585   }
586   return std::nullopt;
587 }
588 
GetCharBlock(ProvenanceRange range) const589 std::optional<CharBlock> AllCookedSources::GetCharBlock(
590     ProvenanceRange range) const {
591   for (const auto &c : cooked_) {
592     if (auto result{c.GetCharBlock(range)}) {
593       return result;
594     }
595   }
596   return nullptr;
597 }
598 
Dump(llvm::raw_ostream & o) const599 void AllCookedSources::Dump(llvm::raw_ostream &o) const {
600   o << "AllSources:\n";
601   allSources_.Dump(o);
602   for (const auto &c : cooked_) {
603     c.Dump(o);
604   }
605 }
606 
Precedes(CharBlock x,CharBlock y) const607 bool AllCookedSources::Precedes(CharBlock x, CharBlock y) const {
608   if (const CookedSource * xSource{Find(x)}) {
609     if (xSource->AsCharBlock().Contains(y)) {
610       return x.begin() < y.begin();
611     } else if (const CookedSource * ySource{Find(y)}) {
612       return xSource->number() < ySource->number();
613     } else {
614       return true; // by fiat, all cooked source < anything outside
615     }
616   } else if (Find(y)) {
617     return false;
618   } else {
619     // Both names are compiler-created (SaveTempName).
620     return x < y;
621   }
622 }
623 
Register(CookedSource & cooked)624 void AllCookedSources::Register(CookedSource &cooked) {
625   index_.emplace(cooked.AsCharBlock(), cooked);
626   cooked.set_number(static_cast<int>(index_.size()));
627 }
628 
629 } // namespace Fortran::parser
630