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 16 namespace Fortran::parser { 17 18 ProvenanceRangeToOffsetMappings::ProvenanceRangeToOffsetMappings() {} 19 ProvenanceRangeToOffsetMappings::~ProvenanceRangeToOffsetMappings() {} 20 21 void 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 37 std::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 53 bool ProvenanceRangeToOffsetMappings::WhollyPrecedes::operator()( 54 ProvenanceRange before, ProvenanceRange after) const { 55 return before.start() + before.size() <= after.start(); 56 } 57 58 void OffsetToProvenanceMappings::clear() { provenanceMap_.clear(); } 59 60 void OffsetToProvenanceMappings::swap(OffsetToProvenanceMappings &that) { 61 provenanceMap_.swap(that.provenanceMap_); 62 } 63 64 void OffsetToProvenanceMappings::shrink_to_fit() { 65 provenanceMap_.shrink_to_fit(); 66 } 67 68 std::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 77 void 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 88 void OffsetToProvenanceMappings::Put(const OffsetToProvenanceMappings &that) { 89 for (const auto &map : that.provenanceMap_) { 90 Put(map.range); 91 } 92 } 93 94 ProvenanceRange 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 113 void 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 126 ProvenanceRangeToOffsetMappings 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 149 AllSources::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 156 AllSources::~AllSources() {} 157 158 const char &AllSources::operator[](Provenance at) const { 159 const Origin &origin{MapToOrigin(at)}; 160 return origin[origin.covers.MemberOffset(at)]; 161 } 162 163 void AllSources::ClearSearchPath() { searchPath_.clear(); } 164 165 void AllSources::AppendSearchPathDirectory(std::string directory) { 166 // gfortran and ifort append to current path, PGI prepends 167 searchPath_.push_back(directory); 168 } 169 170 const 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 180 const 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 201 const 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 209 ProvenanceRange 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 218 ProvenanceRange 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 227 ProvenanceRange 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 235 static 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, true); 242 } 243 o << prefix; 244 if (showColors) { 245 o.resetColor(); 246 } 247 } 248 249 std::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 270 void 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 344 const 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 373 const 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 381 std::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 400 std::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 409 std::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 415 int 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 421 Provenance 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 432 ProvenanceRange 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 448 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source) 449 : u{Inclusion{source}}, covers{r} {} 450 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &included, 451 ProvenanceRange from, bool isModule) 452 : u{Inclusion{included, isModule}}, covers{r}, replaces{from} {} 453 AllSources::Origin::Origin(ProvenanceRange r, ProvenanceRange def, 454 ProvenanceRange use, const std::string &expansion) 455 : u{Macro{def, expansion}}, covers{r}, replaces{use} {} 456 AllSources::Origin::Origin(ProvenanceRange r, const std::string &text) 457 : u{CompilerInsertion{text}}, covers{r} {} 458 459 const 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 473 const 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 489 Provenance 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 497 std::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 523 std::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 534 std::size_t CookedSource::BufferedBytes() const { return buffer_.bytes(); } 535 536 void 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 555 void CookedSource::CompileProvenanceRangeToOffsetMappings( 556 AllSources &allSources) { 557 if (invertedMap_.empty()) { 558 invertedMap_ = provenanceMap_.Invert(allSources); 559 } 560 } 561 562 static void DumpRange(llvm::raw_ostream &o, const ProvenanceRange &r) { 563 o << "[" << r.start().offset() << ".." << r.Last().offset() << "] (" 564 << r.size() << " bytes)"; 565 } 566 567 llvm::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 578 llvm::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 590 llvm::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 630 llvm::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 638 AllCookedSources::AllCookedSources(AllSources &s) : allSources_{s} {} 639 AllCookedSources::~AllCookedSources() {} 640 641 CookedSource &AllCookedSources::NewCookedSource() { 642 return cooked_.emplace_back(allSources_); 643 } 644 645 const 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 655 std::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 664 std::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 679 std::optional<std::pair<SourcePosition, SourcePosition>> 680 AllCookedSources::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 692 std::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 702 void 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 710 bool 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 727 void 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