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 17 ProvenanceRangeToOffsetMappings::ProvenanceRangeToOffsetMappings() {} 18 ProvenanceRangeToOffsetMappings::~ProvenanceRangeToOffsetMappings() {} 19 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 36 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 52 bool ProvenanceRangeToOffsetMappings::WhollyPrecedes::operator()( 53 ProvenanceRange before, ProvenanceRange after) const { 54 return before.start() + before.size() <= after.start(); 55 } 56 57 void OffsetToProvenanceMappings::clear() { provenanceMap_.clear(); } 58 59 void OffsetToProvenanceMappings::swap(OffsetToProvenanceMappings &that) { 60 provenanceMap_.swap(that.provenanceMap_); 61 } 62 63 void OffsetToProvenanceMappings::shrink_to_fit() { 64 provenanceMap_.shrink_to_fit(); 65 } 66 67 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 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 87 void OffsetToProvenanceMappings::Put(const OffsetToProvenanceMappings &that) { 88 for (const auto &map : that.provenanceMap_) { 89 Put(map.range); 90 } 91 } 92 93 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 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 125 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 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 155 AllSources::~AllSources() {} 156 157 const char &AllSources::operator[](Provenance at) const { 158 const Origin &origin{MapToOrigin(at)}; 159 return origin[origin.covers.MemberOffset(at)]; 160 } 161 162 void AllSources::ClearSearchPath() { searchPath_.clear(); } 163 164 void AllSources::AppendSearchPathDirectory(std::string directory) { 165 // gfortran and ifort append to current path, PGI prepends 166 searchPath_.push_back(directory); 167 } 168 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 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 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 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 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 226 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 std::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 294 const SourceFile *AllSources::GetSourceFile( 295 Provenance at, std::size_t *offset) const { 296 const Origin &origin{MapToOrigin(at)}; 297 return std::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(origin.replaces.start(), offset); 306 }, 307 [offset](const CompilerInsertion &) { 308 if (offset) { 309 *offset = 0; 310 } 311 return static_cast<const SourceFile *>(nullptr); 312 }, 313 }, 314 origin.u); 315 } 316 317 const char *AllSources::GetSource(ProvenanceRange range) const { 318 Provenance start{range.start()}; 319 const Origin &origin{MapToOrigin(start)}; 320 return origin.covers.Contains(range) 321 ? &origin[origin.covers.MemberOffset(start)] 322 : nullptr; 323 } 324 325 std::optional<SourcePosition> AllSources::GetSourcePosition( 326 Provenance prov) const { 327 const Origin &origin{MapToOrigin(prov)}; 328 return std::visit( 329 common::visitors{ 330 [&](const Inclusion &inc) -> std::optional<SourcePosition> { 331 std::size_t offset{origin.covers.MemberOffset(prov)}; 332 return inc.source.FindOffsetLineAndColumn(offset); 333 }, 334 [&](const Macro &) { 335 return GetSourcePosition(origin.replaces.start()); 336 }, 337 [](const CompilerInsertion &) -> std::optional<SourcePosition> { 338 return std::nullopt; 339 }, 340 }, 341 origin.u); 342 } 343 344 std::optional<ProvenanceRange> AllSources::GetFirstFileProvenance() const { 345 for (const auto &origin : origin_) { 346 if (std::holds_alternative<Inclusion>(origin.u)) { 347 return origin.covers; 348 } 349 } 350 return std::nullopt; 351 } 352 353 std::string AllSources::GetPath(Provenance at) const { 354 const SourceFile *source{GetSourceFile(at)}; 355 return source ? source->path() : ""s; 356 } 357 358 int AllSources::GetLineNumber(Provenance at) const { 359 std::size_t offset{0}; 360 const SourceFile *source{GetSourceFile(at, &offset)}; 361 return source ? source->FindOffsetLineAndColumn(offset).line : 0; 362 } 363 364 Provenance AllSources::CompilerInsertionProvenance(char ch) { 365 auto iter{compilerInsertionProvenance_.find(ch)}; 366 if (iter != compilerInsertionProvenance_.end()) { 367 return iter->second; 368 } 369 ProvenanceRange newCharRange{AddCompilerInsertion(std::string{ch})}; 370 Provenance newCharProvenance{newCharRange.start()}; 371 compilerInsertionProvenance_.insert(std::make_pair(ch, newCharProvenance)); 372 return newCharProvenance; 373 } 374 375 ProvenanceRange AllSources::IntersectionWithSourceFiles( 376 ProvenanceRange range) const { 377 if (range.empty()) { 378 return {}; 379 } else { 380 const Origin &origin{MapToOrigin(range.start())}; 381 if (std::holds_alternative<Inclusion>(origin.u)) { 382 return range.Intersection(origin.covers); 383 } else { 384 auto skip{ 385 origin.covers.size() - origin.covers.MemberOffset(range.start())}; 386 return IntersectionWithSourceFiles(range.Suffix(skip)); 387 } 388 } 389 } 390 391 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source) 392 : u{Inclusion{source}}, covers{r} {} 393 AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &included, 394 ProvenanceRange from, bool isModule) 395 : u{Inclusion{included, isModule}}, covers{r}, replaces{from} {} 396 AllSources::Origin::Origin(ProvenanceRange r, ProvenanceRange def, 397 ProvenanceRange use, const std::string &expansion) 398 : u{Macro{def, expansion}}, covers{r}, replaces{use} {} 399 AllSources::Origin::Origin(ProvenanceRange r, const std::string &text) 400 : u{CompilerInsertion{text}}, covers{r} {} 401 402 const char &AllSources::Origin::operator[](std::size_t n) const { 403 return std::visit( 404 common::visitors{ 405 [n](const Inclusion &inc) -> const char & { 406 return inc.source.content()[n]; 407 }, 408 [n](const Macro &mac) -> const char & { return mac.expansion[n]; }, 409 [n](const CompilerInsertion &ins) -> const char & { 410 return ins.text[n]; 411 }, 412 }, 413 u); 414 } 415 416 const AllSources::Origin &AllSources::MapToOrigin(Provenance at) const { 417 CHECK(range_.Contains(at)); 418 std::size_t low{0}, count{origin_.size()}; 419 while (count > 1) { 420 std::size_t mid{low + (count >> 1)}; 421 if (at < origin_[mid].covers.start()) { 422 count = mid - low; 423 } else { 424 count -= mid - low; 425 low = mid; 426 } 427 } 428 CHECK(origin_[low].covers.Contains(at)); 429 return origin_[low]; 430 } 431 432 std::optional<ProvenanceRange> CookedSource::GetProvenanceRange( 433 CharBlock cookedRange) const { 434 if (!AsCharBlock().Contains(cookedRange)) { 435 return std::nullopt; 436 } 437 ProvenanceRange first{provenanceMap_.Map(cookedRange.begin() - &data_[0])}; 438 if (cookedRange.size() <= first.size()) { // always true when empty 439 return first.Prefix(cookedRange.size()); 440 } 441 ProvenanceRange last{provenanceMap_.Map(cookedRange.end() - 1 - &data_[0])}; 442 if (first.start() <= last.start()) { 443 return {ProvenanceRange{first.start(), last.start() - first.start() + 1}}; 444 } else { 445 return std::nullopt; 446 } 447 } 448 449 std::optional<CharBlock> CookedSource::GetCharBlock( 450 ProvenanceRange range) const { 451 CHECK(!invertedMap_.empty() && 452 "CompileProvenanceRangeToOffsetMappings not called"); 453 if (auto to{invertedMap_.Map(range)}) { 454 return CharBlock{data_.c_str() + *to, range.size()}; 455 } else { 456 return std::nullopt; 457 } 458 } 459 460 std::size_t CookedSource::BufferedBytes() const { return buffer_.bytes(); } 461 462 void CookedSource::Marshal(AllCookedSources &allCookedSources) { 463 CHECK(provenanceMap_.SizeInBytes() == buffer_.bytes()); 464 provenanceMap_.Put(allCookedSources.allSources().AddCompilerInsertion( 465 "(after end of source)")); 466 data_ = buffer_.Marshal(); 467 buffer_.clear(); 468 allCookedSources.Register(*this); 469 } 470 471 void CookedSource::CompileProvenanceRangeToOffsetMappings( 472 AllSources &allSources) { 473 if (invertedMap_.empty()) { 474 invertedMap_ = provenanceMap_.Invert(allSources); 475 } 476 } 477 478 static void DumpRange(llvm::raw_ostream &o, const ProvenanceRange &r) { 479 o << "[" << r.start().offset() << ".." << r.Last().offset() << "] (" 480 << r.size() << " bytes)"; 481 } 482 483 llvm::raw_ostream &ProvenanceRangeToOffsetMappings::Dump( 484 llvm::raw_ostream &o) const { 485 for (const auto &m : map_) { 486 o << "provenances "; 487 DumpRange(o, m.first); 488 o << " -> offsets [" << m.second << ".." << (m.second + m.first.size() - 1) 489 << "]\n"; 490 } 491 return o; 492 } 493 494 llvm::raw_ostream &OffsetToProvenanceMappings::Dump( 495 llvm::raw_ostream &o) const { 496 for (const ContiguousProvenanceMapping &m : provenanceMap_) { 497 std::size_t n{m.range.size()}; 498 o << "offsets [" << m.start << ".." << (m.start + n - 1) 499 << "] -> provenances "; 500 DumpRange(o, m.range); 501 o << '\n'; 502 } 503 return o; 504 } 505 506 llvm::raw_ostream &AllSources::Dump(llvm::raw_ostream &o) const { 507 o << "AllSources range_ "; 508 DumpRange(o, range_); 509 o << '\n'; 510 for (const Origin &m : origin_) { 511 o << " "; 512 DumpRange(o, m.covers); 513 o << " -> "; 514 std::visit(common::visitors{ 515 [&](const Inclusion &inc) { 516 if (inc.isModule) { 517 o << "module "; 518 } 519 o << "file " << inc.source.path(); 520 }, 521 [&](const Macro &mac) { o << "macro " << mac.expansion; }, 522 [&](const CompilerInsertion &ins) { 523 o << "compiler '" << ins.text << '\''; 524 if (ins.text.length() == 1) { 525 int ch = ins.text[0]; 526 o << "(0x"; 527 o.write_hex(ch & 0xff) << ")"; 528 } 529 }, 530 }, 531 m.u); 532 if (IsValid(m.replaces)) { 533 o << " replaces "; 534 DumpRange(o, m.replaces); 535 } 536 o << '\n'; 537 } 538 return o; 539 } 540 541 llvm::raw_ostream &CookedSource::Dump(llvm::raw_ostream &o) const { 542 o << "CookedSource::provenanceMap_:\n"; 543 provenanceMap_.Dump(o); 544 o << "CookedSource::invertedMap_:\n"; 545 invertedMap_.Dump(o); 546 return o; 547 } 548 549 AllCookedSources::AllCookedSources(AllSources &s) : allSources_{s} {} 550 AllCookedSources::~AllCookedSources() {} 551 552 CookedSource &AllCookedSources::NewCookedSource() { 553 return cooked_.emplace_back(); 554 } 555 556 const CookedSource *AllCookedSources::Find(CharBlock x) const { 557 auto pair{index_.equal_range(x)}; 558 for (auto iter{pair.first}; iter != pair.second; ++iter) { 559 if (iter->second.AsCharBlock().Contains(x)) { 560 return &iter->second; 561 } 562 } 563 return nullptr; 564 } 565 566 std::optional<ProvenanceRange> AllCookedSources::GetProvenanceRange( 567 CharBlock cb) const { 568 if (const CookedSource * c{Find(cb)}) { 569 return c->GetProvenanceRange(cb); 570 } else { 571 return std::nullopt; 572 } 573 } 574 575 std::optional<CharBlock> AllCookedSources::GetCharBlockFromLineAndColumns( 576 int line, int startColumn, int endColumn) const { 577 // 2nd column is exclusive, meaning it is target column + 1. 578 CHECK(line > 0 && startColumn > 0 && endColumn > 0); 579 CHECK(startColumn < endColumn); 580 auto provenanceStart{allSources_.GetFirstFileProvenance().value().start()}; 581 if (auto sourceFile{allSources_.GetSourceFile(provenanceStart)}) { 582 CHECK(line <= static_cast<int>(sourceFile->lines())); 583 return GetCharBlock(ProvenanceRange(sourceFile->GetLineStartOffset(line) + 584 provenanceStart.offset() + startColumn - 1, 585 endColumn - startColumn)); 586 } 587 return std::nullopt; 588 } 589 590 std::optional<std::pair<SourcePosition, SourcePosition>> 591 AllCookedSources::GetSourcePositionRange(CharBlock cookedRange) const { 592 if (auto range{GetProvenanceRange(cookedRange)}) { 593 if (auto firstOffset{allSources_.GetSourcePosition(range->start())}) { 594 if (auto secondOffset{ 595 allSources_.GetSourcePosition(range->start() + range->size())}) { 596 return std::pair{*firstOffset, *secondOffset}; 597 } 598 } 599 } 600 return std::nullopt; 601 } 602 603 std::optional<CharBlock> AllCookedSources::GetCharBlock( 604 ProvenanceRange range) const { 605 for (const auto &c : cooked_) { 606 if (auto result{c.GetCharBlock(range)}) { 607 return result; 608 } 609 } 610 return std::nullopt; 611 } 612 613 void AllCookedSources::Dump(llvm::raw_ostream &o) const { 614 o << "AllSources:\n"; 615 allSources_.Dump(o); 616 for (const auto &c : cooked_) { 617 c.Dump(o); 618 } 619 } 620 621 bool AllCookedSources::Precedes(CharBlock x, CharBlock y) const { 622 if (const CookedSource * xSource{Find(x)}) { 623 if (xSource->AsCharBlock().Contains(y)) { 624 return x.begin() < y.begin(); 625 } else if (const CookedSource * ySource{Find(y)}) { 626 return xSource->number() < ySource->number(); 627 } else { 628 return true; // by fiat, all cooked source < anything outside 629 } 630 } else if (Find(y)) { 631 return false; 632 } else { 633 // Both names are compiler-created (SaveTempName). 634 return x < y; 635 } 636 } 637 638 void AllCookedSources::Register(CookedSource &cooked) { 639 index_.emplace(cooked.AsCharBlock(), cooked); 640 cooked.set_number(static_cast<int>(index_.size())); 641 } 642 643 } // namespace Fortran::parser 644