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: color, Bold: true); |
242 | } |
243 | o << prefix; |
244 | if (showColors) { |
245 | o.resetColor(); |
246 | } |
247 | } |
248 | |
249 | void AllSources::EmitMessage(llvm::raw_ostream &o, |
250 | const std::optional<ProvenanceRange> &range, const std::string &message, |
251 | const std::string &prefix, llvm::raw_ostream::Colors color, |
252 | bool echoSourceLine) const { |
253 | if (!range) { |
254 | EmitPrefix(o, color, prefix, this->getShowColors()); |
255 | o << message << '\n'; |
256 | return; |
257 | } |
258 | CHECK(IsValid(*range)); |
259 | const Origin &origin{MapToOrigin(range->start())}; |
260 | common::visit( |
261 | common::visitors{ |
262 | [&](const Inclusion &inc) { |
263 | std::size_t offset{origin.covers.MemberOffset(range->start())}; |
264 | SourcePosition pos{inc.source.GetSourcePosition(offset)}; |
265 | o << pos.path << ':' << pos.line << ':' << pos.column << ": " ; |
266 | EmitPrefix(o, color, prefix, this->getShowColors()); |
267 | o << message << '\n'; |
268 | if (echoSourceLine) { |
269 | const char *text{inc.source.content().data() + |
270 | inc.source.GetLineStartOffset(pos.trueLineNumber)}; |
271 | o << " " ; |
272 | for (const char *p{text}; *p != '\n'; ++p) { |
273 | o << *p; |
274 | } |
275 | o << "\n " ; |
276 | for (int j{1}; j < pos.column; ++j) { |
277 | char ch{text[j - 1]}; |
278 | o << (ch == '\t' ? '\t' : ' '); |
279 | } |
280 | o << '^'; |
281 | if (range->size() > 1) { |
282 | auto last{range->start() + range->size() - 1}; |
283 | if (&MapToOrigin(last) == &origin) { |
284 | auto endOffset{origin.covers.MemberOffset(last)}; |
285 | auto endPos{inc.source.GetSourcePosition(endOffset)}; |
286 | if (pos.line == endPos.line) { |
287 | for (int j{pos.column}; j < endPos.column; ++j) { |
288 | o << '^'; |
289 | } |
290 | } |
291 | } |
292 | } |
293 | o << '\n'; |
294 | } |
295 | if (IsValid(origin.replaces)) { |
296 | EmitMessage(o, origin.replaces, |
297 | inc.isModule ? "used here"s : "included here"s , prefix, color, |
298 | echoSourceLine); |
299 | } |
300 | }, |
301 | [&](const Macro &mac) { |
302 | EmitMessage( |
303 | o, origin.replaces, message, prefix, color, echoSourceLine); |
304 | EmitMessage(o, mac.definition, "in a macro defined here" , ""s , |
305 | color, echoSourceLine); |
306 | if (echoSourceLine) { |
307 | o << "that expanded to:\n " << mac.expansion << "\n " ; |
308 | for (std::size_t j{0}; |
309 | origin.covers.OffsetMember(j) < range->start(); ++j) { |
310 | o << (mac.expansion[j] == '\t' ? '\t' : ' '); |
311 | } |
312 | o << "^\n" ; |
313 | } |
314 | }, |
315 | [&](const CompilerInsertion &) { |
316 | EmitPrefix(o, color, prefix, this->getShowColors()); |
317 | o << message << '\n'; |
318 | }, |
319 | }, |
320 | origin.u); |
321 | } |
322 | |
323 | const SourceFile *AllSources::GetSourceFile( |
324 | Provenance at, std::size_t *offset) const { |
325 | const Origin &origin{MapToOrigin(at)}; |
326 | return common::visit(common::visitors{ |
327 | [&](const Inclusion &inc) { |
328 | if (offset) { |
329 | *offset = origin.covers.MemberOffset(at); |
330 | } |
331 | return &inc.source; |
332 | }, |
333 | [&](const Macro &) { |
334 | return GetSourceFile( |
335 | origin.replaces.start(), offset); |
336 | }, |
337 | [offset](const CompilerInsertion &) { |
338 | if (offset) { |
339 | *offset = 0; |
340 | } |
341 | return static_cast<const SourceFile *>(nullptr); |
342 | }, |
343 | }, |
344 | origin.u); |
345 | } |
346 | |
347 | const char *AllSources::GetSource(ProvenanceRange range) const { |
348 | Provenance start{range.start()}; |
349 | const Origin &origin{MapToOrigin(start)}; |
350 | return origin.covers.Contains(range) |
351 | ? &origin[origin.covers.MemberOffset(start)] |
352 | : nullptr; |
353 | } |
354 | |
355 | std::optional<SourcePosition> AllSources::GetSourcePosition( |
356 | Provenance prov) const { |
357 | const Origin &origin{MapToOrigin(prov)}; |
358 | return common::visit( |
359 | common::visitors{ |
360 | [&](const Inclusion &inc) -> std::optional<SourcePosition> { |
361 | std::size_t offset{origin.covers.MemberOffset(prov)}; |
362 | return inc.source.GetSourcePosition(offset); |
363 | }, |
364 | [&](const Macro &) { |
365 | return GetSourcePosition(origin.replaces.start()); |
366 | }, |
367 | [](const CompilerInsertion &) -> std::optional<SourcePosition> { |
368 | return std::nullopt; |
369 | }, |
370 | }, |
371 | origin.u); |
372 | } |
373 | |
374 | std::optional<ProvenanceRange> AllSources::GetFirstFileProvenance() const { |
375 | for (const auto &origin : origin_) { |
376 | if (std::holds_alternative<Inclusion>(origin.u)) { |
377 | return origin.covers; |
378 | } |
379 | } |
380 | return std::nullopt; |
381 | } |
382 | |
383 | std::string AllSources::GetPath(Provenance at) const { |
384 | std::size_t offset{0}; |
385 | const SourceFile *source{GetSourceFile(at, &offset)}; |
386 | return source ? *source->GetSourcePosition(offset).path : ""s ; |
387 | } |
388 | |
389 | int AllSources::GetLineNumber(Provenance at) const { |
390 | std::size_t offset{0}; |
391 | const SourceFile *source{GetSourceFile(at, &offset)}; |
392 | return source ? source->GetSourcePosition(offset).line : 0; |
393 | } |
394 | |
395 | Provenance AllSources::CompilerInsertionProvenance(char ch) { |
396 | auto iter{compilerInsertionProvenance_.find(ch)}; |
397 | if (iter != compilerInsertionProvenance_.end()) { |
398 | return iter->second; |
399 | } |
400 | ProvenanceRange newCharRange{AddCompilerInsertion(std::string{ch})}; |
401 | Provenance newCharProvenance{newCharRange.start()}; |
402 | compilerInsertionProvenance_.insert(std::make_pair(ch, newCharProvenance)); |
403 | return newCharProvenance; |
404 | } |
405 | |
406 | ProvenanceRange AllSources::IntersectionWithSourceFiles( |
407 | ProvenanceRange range) const { |
408 | if (range.empty()) { |
409 | return {}; |
410 | } else { |
411 | const Origin &origin{MapToOrigin(range.start())}; |
412 | if (std::holds_alternative<Inclusion>(origin.u)) { |
413 | return range.Intersection(origin.covers); |
414 | } else { |
415 | auto skip{ |
416 | origin.covers.size() - origin.covers.MemberOffset(range.start())}; |
417 | return IntersectionWithSourceFiles(range.Suffix(skip)); |
418 | } |
419 | } |
420 | } |
421 | |
422 | AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source) |
423 | : u{Inclusion{source}}, covers{r} {} |
424 | AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &included, |
425 | ProvenanceRange from, bool isModule) |
426 | : u{Inclusion{included, isModule}}, covers{r}, replaces{from} {} |
427 | AllSources::Origin::Origin(ProvenanceRange r, ProvenanceRange def, |
428 | ProvenanceRange use, const std::string &expansion) |
429 | : u{Macro{def, expansion}}, covers{r}, replaces{use} {} |
430 | AllSources::Origin::Origin(ProvenanceRange r, const std::string &text) |
431 | : u{CompilerInsertion{text}}, covers{r} {} |
432 | |
433 | const char &AllSources::Origin::operator[](std::size_t n) const { |
434 | return common::visit( |
435 | common::visitors{ |
436 | [n](const Inclusion &inc) -> const char & { |
437 | return inc.source.content()[n]; |
438 | }, |
439 | [n](const Macro &mac) -> const char & { return mac.expansion[n]; }, |
440 | [n](const CompilerInsertion &ins) -> const char & { |
441 | return ins.text[n]; |
442 | }, |
443 | }, |
444 | u); |
445 | } |
446 | |
447 | const AllSources::Origin &AllSources::MapToOrigin(Provenance at) const { |
448 | CHECK(range_.Contains(at)); |
449 | std::size_t low{0}, count{origin_.size()}; |
450 | while (count > 1) { |
451 | std::size_t mid{low + (count >> 1)}; |
452 | if (at < origin_[mid].covers.start()) { |
453 | count = mid - low; |
454 | } else { |
455 | count -= mid - low; |
456 | low = mid; |
457 | } |
458 | } |
459 | CHECK(origin_[low].covers.Contains(at)); |
460 | return origin_[low]; |
461 | } |
462 | |
463 | Provenance AllSources::GetReplacedProvenance(Provenance provenance) const { |
464 | const Origin &origin{MapToOrigin(provenance)}; |
465 | if (std::holds_alternative<Macro>(origin.u)) { |
466 | return origin.replaces.start(); |
467 | } |
468 | return provenance; |
469 | } |
470 | |
471 | std::optional<ProvenanceRange> CookedSource::GetProvenanceRange( |
472 | CharBlock cookedRange) const { |
473 | if (!AsCharBlock().Contains(cookedRange)) { |
474 | return std::nullopt; |
475 | } |
476 | ProvenanceRange first{provenanceMap_.Map(cookedRange.begin() - &data_[0])}; |
477 | if (cookedRange.size() <= first.size()) { // always true when empty |
478 | return first.Prefix(cookedRange.size()); |
479 | } |
480 | ProvenanceRange last{provenanceMap_.Map(cookedRange.end() - 1 - &data_[0])}; |
481 | if (first.start() <= last.start()) { |
482 | return {ProvenanceRange{first.start(), last.start() - first.start() + 1}}; |
483 | } else { |
484 | // cookedRange may start (resp. end) in a macro expansion while it does not |
485 | // end (resp. start) in this macro expansion. Attempt to build a range |
486 | // over the replaced source. |
487 | Provenance firstStart{allSources_.GetReplacedProvenance(first.start())}; |
488 | Provenance lastStart{allSources_.GetReplacedProvenance(last.start())}; |
489 | if (firstStart <= lastStart) { |
490 | return {ProvenanceRange{firstStart, lastStart - firstStart + 1}}; |
491 | } else { |
492 | return std::nullopt; |
493 | } |
494 | } |
495 | } |
496 | |
497 | std::optional<CharBlock> CookedSource::GetCharBlock( |
498 | ProvenanceRange range) const { |
499 | CHECK(!invertedMap_.empty() && |
500 | "CompileProvenanceRangeToOffsetMappings not called" ); |
501 | if (auto to{invertedMap_.Map(range)}) { |
502 | return CharBlock{data_.c_str() + *to, range.size()}; |
503 | } else { |
504 | return std::nullopt; |
505 | } |
506 | } |
507 | |
508 | std::size_t CookedSource::BufferedBytes() const { return buffer_.bytes(); } |
509 | |
510 | void CookedSource::Marshal(AllCookedSources &allCookedSources) { |
511 | CHECK(provenanceMap_.SizeInBytes() == buffer_.bytes()); |
512 | provenanceMap_.Put(allCookedSources.allSources().AddCompilerInsertion( |
513 | "(after end of source)" )); |
514 | data_ = buffer_.Marshal(); |
515 | buffer_.clear(); |
516 | allCookedSources.Register(*this); |
517 | } |
518 | |
519 | void CookedSource::CompileProvenanceRangeToOffsetMappings( |
520 | AllSources &allSources) { |
521 | if (invertedMap_.empty()) { |
522 | invertedMap_ = provenanceMap_.Invert(allSources); |
523 | } |
524 | } |
525 | |
526 | static void DumpRange(llvm::raw_ostream &o, const ProvenanceRange &r) { |
527 | o << "[" << r.start().offset() << ".." << r.Last().offset() << "] (" |
528 | << r.size() << " bytes)" ; |
529 | } |
530 | |
531 | llvm::raw_ostream &ProvenanceRangeToOffsetMappings::Dump( |
532 | llvm::raw_ostream &o) const { |
533 | for (const auto &m : map_) { |
534 | o << "provenances " ; |
535 | DumpRange(o, m.first); |
536 | o << " -> offsets [" << m.second << ".." << (m.second + m.first.size() - 1) |
537 | << "]\n" ; |
538 | } |
539 | return o; |
540 | } |
541 | |
542 | llvm::raw_ostream &OffsetToProvenanceMappings::Dump( |
543 | llvm::raw_ostream &o) const { |
544 | for (const ContiguousProvenanceMapping &m : provenanceMap_) { |
545 | std::size_t n{m.range.size()}; |
546 | o << "offsets [" << m.start << ".." << (m.start + n - 1) |
547 | << "] -> provenances " ; |
548 | DumpRange(o, m.range); |
549 | o << '\n'; |
550 | } |
551 | return o; |
552 | } |
553 | |
554 | llvm::raw_ostream &AllSources::Dump(llvm::raw_ostream &o) const { |
555 | o << "AllSources range_ " ; |
556 | DumpRange(o, range_); |
557 | o << '\n'; |
558 | std::set<const SourceFile *> sources; |
559 | for (const Origin &m : origin_) { |
560 | o << " " ; |
561 | DumpRange(o, m.covers); |
562 | o << " -> " ; |
563 | common::visit(common::visitors{ |
564 | [&](const Inclusion &inc) { |
565 | if (inc.isModule) { |
566 | o << "module " ; |
567 | } |
568 | o << "file " << inc.source.path(); |
569 | sources.emplace(&inc.source); |
570 | }, |
571 | [&](const Macro &mac) { o << "macro " << mac.expansion; }, |
572 | [&](const CompilerInsertion &ins) { |
573 | o << "compiler '" << ins.text << '\''; |
574 | if (ins.text.length() == 1) { |
575 | int ch = ins.text[0]; |
576 | o << "(0x" ; |
577 | o.write_hex(ch & 0xff) << ")" ; |
578 | } |
579 | }, |
580 | }, |
581 | m.u); |
582 | if (IsValid(m.replaces)) { |
583 | o << " replaces " ; |
584 | DumpRange(o, m.replaces); |
585 | } |
586 | o << '\n'; |
587 | } |
588 | for (const SourceFile *sf : sources) { |
589 | sf->Dump(o); |
590 | } |
591 | return o; |
592 | } |
593 | |
594 | llvm::raw_ostream &CookedSource::Dump(llvm::raw_ostream &o) const { |
595 | o << "CookedSource::provenanceMap_:\n" ; |
596 | provenanceMap_.Dump(o); |
597 | o << "CookedSource::invertedMap_:\n" ; |
598 | invertedMap_.Dump(o); |
599 | return o; |
600 | } |
601 | |
602 | AllCookedSources::AllCookedSources(AllSources &s) : allSources_{s} {} |
603 | AllCookedSources::~AllCookedSources() {} |
604 | |
605 | CookedSource &AllCookedSources::NewCookedSource() { |
606 | return cooked_.emplace_back(allSources_); |
607 | } |
608 | |
609 | const CookedSource *AllCookedSources::Find(CharBlock x) const { |
610 | auto pair{index_.equal_range(x)}; |
611 | for (auto iter{pair.first}; iter != pair.second; ++iter) { |
612 | if (iter->second.AsCharBlock().Contains(x)) { |
613 | return &iter->second; |
614 | } |
615 | } |
616 | return nullptr; |
617 | } |
618 | |
619 | std::optional<ProvenanceRange> AllCookedSources::GetProvenanceRange( |
620 | CharBlock cb) const { |
621 | if (const CookedSource * c{Find(cb)}) { |
622 | return c->GetProvenanceRange(cb); |
623 | } else { |
624 | return std::nullopt; |
625 | } |
626 | } |
627 | |
628 | std::optional<CharBlock> AllCookedSources::GetCharBlockFromLineAndColumns( |
629 | int line, int startColumn, int endColumn) const { |
630 | // 2nd column is exclusive, meaning it is target column + 1. |
631 | CHECK(line > 0 && startColumn > 0 && endColumn > 0); |
632 | CHECK(startColumn < endColumn); |
633 | auto provenanceStart{allSources_.GetFirstFileProvenance().value().start()}; |
634 | if (auto sourceFile{allSources_.GetSourceFile(provenanceStart)}) { |
635 | CHECK(line <= static_cast<int>(sourceFile->lines())); |
636 | return GetCharBlock(ProvenanceRange(sourceFile->GetLineStartOffset(line) + |
637 | provenanceStart.offset() + startColumn - 1, |
638 | endColumn - startColumn)); |
639 | } |
640 | return std::nullopt; |
641 | } |
642 | |
643 | std::optional<std::pair<SourcePosition, SourcePosition>> |
644 | AllCookedSources::GetSourcePositionRange(CharBlock cookedRange) const { |
645 | if (auto range{GetProvenanceRange(cookedRange)}) { |
646 | if (auto firstOffset{allSources_.GetSourcePosition(range->start())}) { |
647 | if (auto secondOffset{ |
648 | allSources_.GetSourcePosition(range->start() + range->size())}) { |
649 | return std::pair{*firstOffset, *secondOffset}; |
650 | } |
651 | } |
652 | } |
653 | return std::nullopt; |
654 | } |
655 | |
656 | std::optional<CharBlock> AllCookedSources::GetCharBlock( |
657 | ProvenanceRange range) const { |
658 | for (const auto &c : cooked_) { |
659 | if (auto result{c.GetCharBlock(range)}) { |
660 | return result; |
661 | } |
662 | } |
663 | return std::nullopt; |
664 | } |
665 | |
666 | void AllCookedSources::Dump(llvm::raw_ostream &o) const { |
667 | o << "AllSources:\n" ; |
668 | allSources_.Dump(o); |
669 | for (const auto &c : cooked_) { |
670 | c.Dump(o); |
671 | } |
672 | } |
673 | |
674 | bool AllCookedSources::Precedes(CharBlock x, CharBlock y) const { |
675 | if (const CookedSource * xSource{Find(x)}) { |
676 | if (xSource->AsCharBlock().Contains(y)) { |
677 | return x.begin() < y.begin(); |
678 | } else if (const CookedSource * ySource{Find(y)}) { |
679 | return xSource->number() < ySource->number(); |
680 | } else { |
681 | return true; // by fiat, all cooked source < anything outside |
682 | } |
683 | } else if (Find(y)) { |
684 | return false; |
685 | } else { |
686 | // Both names are compiler-created (SaveTempName). |
687 | return x < y; |
688 | } |
689 | } |
690 | |
691 | void AllCookedSources::Register(CookedSource &cooked) { |
692 | index_.emplace(cooked.AsCharBlock(), cooked); |
693 | cooked.set_number(static_cast<int>(index_.size())); |
694 | } |
695 | |
696 | } // namespace Fortran::parser |
697 | |