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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of flang/lib/Parser/provenance.cpp