1 //===-- include/flang/Parser/provenance.h -----------------------*- C++ -*-===//
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 #ifndef FORTRAN_PARSER_PROVENANCE_H_
10 #define FORTRAN_PARSER_PROVENANCE_H_
11 
12 #include "char-block.h"
13 #include "char-buffer.h"
14 #include "characters.h"
15 #include "source.h"
16 #include "flang/Common/idioms.h"
17 #include "flang/Common/interval.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include <cstddef>
20 #include <list>
21 #include <map>
22 #include <memory>
23 #include <optional>
24 #include <string>
25 #include <utility>
26 #include <variant>
27 #include <vector>
28 
29 namespace Fortran::parser {
30 
31 // Each character in the contiguous source stream built by the
32 // prescanner corresponds to a particular character in a source file,
33 // include file, macro expansion, or compiler-inserted text.
34 // The location of this original character to which a parsable character
35 // corresponds is its provenance.
36 //
37 // Provenances are offsets into an (unmaterialized) marshaling of the
38 // entire contents of all the original source files, include files, macro
39 // expansions, &c. for each visit to each source.  These origins of the
40 // original source characters constitute a forest whose roots are
41 // the original source files named on the compiler's command line.
42 // Given a Provenance, we can find the tree node that contains it in time
43 // O(log(# of origins)), and describe the position precisely by walking
44 // up the tree.  (It would be possible, via a time/space trade-off, to
45 // cap the time by the use of an intermediate table that would be indexed
46 // by the upper bits of an offset, but that does not appear to be
47 // necessary.)
48 
49 class AllSources;
50 class AllCookedSources;
51 
52 class Provenance {
53 public:
Provenance()54   Provenance() {}
Provenance(std::size_t offset)55   Provenance(std::size_t offset) : offset_{offset} { CHECK(offset > 0); }
56   Provenance(const Provenance &that) = default;
57   Provenance(Provenance &&that) = default;
58   Provenance &operator=(const Provenance &that) = default;
59   Provenance &operator=(Provenance &&that) = default;
60 
offset()61   std::size_t offset() const { return offset_; }
62 
63   Provenance operator+(ptrdiff_t n) const {
64     CHECK(n > -static_cast<ptrdiff_t>(offset_));
65     return {offset_ + static_cast<std::size_t>(n)};
66   }
67   Provenance operator+(std::size_t n) const { return {offset_ + n}; }
68   std::size_t operator-(Provenance that) const {
69     CHECK(that <= *this);
70     return offset_ - that.offset_;
71   }
72   bool operator<(Provenance that) const { return offset_ < that.offset_; }
73   bool operator<=(Provenance that) const { return !(that < *this); }
74   bool operator==(Provenance that) const { return offset_ == that.offset_; }
75   bool operator!=(Provenance that) const { return !(*this == that); }
76 
77 private:
78   std::size_t offset_{0};
79 };
80 
81 using ProvenanceRange = common::Interval<Provenance>;
82 
83 // Maps contiguous ranges of byte offsets in original source files to
84 // contiguous ranges in the cooked character stream; essentially a
85 // partial inversion of OffsetToProvenanceMappings (below).
86 // Used for implementing the first step of mapping an identifier
87 // selected in a code editor to one of its declarative statements.
88 class ProvenanceRangeToOffsetMappings {
89 public:
90   ProvenanceRangeToOffsetMappings();
91   ~ProvenanceRangeToOffsetMappings();
empty()92   bool empty() const { return map_.empty(); }
93   void Put(ProvenanceRange, std::size_t offset);
94   std::optional<std::size_t> Map(ProvenanceRange) const;
95   llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
96 
97 private:
98   // A comparison function object for use in std::multimap<Compare=>.
99   // Intersecting intervals will effectively compare equal, not being
100   // either < nor >= each other.
101   struct WhollyPrecedes {
102     bool operator()(ProvenanceRange, ProvenanceRange) const;
103   };
104 
105   std::multimap<ProvenanceRange, std::size_t, WhollyPrecedes> map_;
106 };
107 
108 // Maps 0-based local offsets in some contiguous range (e.g., a token
109 // sequence) to their provenances.  Lookup time is on the order of
110 // O(log(#of intervals with contiguous provenances)).  As mentioned
111 // above, this time could be capped via a time/space trade-off.
112 class OffsetToProvenanceMappings {
113 public:
OffsetToProvenanceMappings()114   OffsetToProvenanceMappings() {}
115   void clear();
116   void swap(OffsetToProvenanceMappings &);
117   void shrink_to_fit();
118   std::size_t SizeInBytes() const;
119   void Put(ProvenanceRange);
120   void Put(const OffsetToProvenanceMappings &);
121   ProvenanceRange Map(std::size_t at) const;
122   void RemoveLastBytes(std::size_t);
123   ProvenanceRangeToOffsetMappings Invert(const AllSources &) const;
124   llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
125 
126 private:
127   struct ContiguousProvenanceMapping {
128     std::size_t start;
129     ProvenanceRange range;
130   };
131 
132   // Elements appear in ascending order of distinct .start values;
133   // their .range values are disjoint and not necessarily adjacent.
134   std::vector<ContiguousProvenanceMapping> provenanceMap_;
135 };
136 
137 // A singleton AllSources instance for the whole compilation
138 // is shared by reference.
139 class AllSources {
140 public:
141   AllSources();
142   ~AllSources();
143 
size()144   std::size_t size() const { return range_.size(); }
145   const char &operator[](Provenance) const;
encoding()146   Encoding encoding() const { return encoding_; }
set_encoding(Encoding e)147   AllSources &set_encoding(Encoding e) {
148     encoding_ = e;
149     return *this;
150   }
151 
152   void ClearSearchPath();
153   void AppendSearchPathDirectory(std::string); // new last directory
154   const SourceFile *Open(std::string path, llvm::raw_ostream &error,
155       std::optional<std::string> &&prependPath = std::nullopt);
156   const SourceFile *ReadStandardInput(llvm::raw_ostream &error);
157 
158   ProvenanceRange AddIncludedFile(
159       const SourceFile &, ProvenanceRange, bool isModule = false);
160   ProvenanceRange AddMacroCall(
161       ProvenanceRange def, ProvenanceRange use, const std::string &expansion);
162   ProvenanceRange AddCompilerInsertion(std::string);
163 
IsValid(Provenance at)164   bool IsValid(Provenance at) const { return range_.Contains(at); }
IsValid(ProvenanceRange range)165   bool IsValid(ProvenanceRange range) const {
166     return range.size() > 0 && range_.Contains(range);
167   }
168   void EmitMessage(llvm::raw_ostream &, const std::optional<ProvenanceRange> &,
169       const std::string &message, bool echoSourceLine = false) const;
170   const SourceFile *GetSourceFile(
171       Provenance, std::size_t *offset = nullptr) const;
172   const char *GetSource(ProvenanceRange) const;
173   std::optional<SourcePosition> GetSourcePosition(Provenance) const;
174   std::optional<ProvenanceRange> GetFirstFileProvenance() const;
175   std::string GetPath(Provenance) const; // __FILE__
176   int GetLineNumber(Provenance) const; // __LINE__
177   Provenance CompilerInsertionProvenance(char ch);
178   ProvenanceRange IntersectionWithSourceFiles(ProvenanceRange) const;
179   llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
180 
181 private:
182   struct Inclusion {
183     const SourceFile &source;
184     bool isModule{false};
185   };
186   struct Macro {
187     ProvenanceRange definition;
188     std::string expansion;
189   };
190   struct CompilerInsertion {
191     std::string text;
192   };
193 
194   struct Origin {
195     Origin(ProvenanceRange, const SourceFile &);
196     Origin(ProvenanceRange, const SourceFile &, ProvenanceRange,
197         bool isModule = false);
198     Origin(ProvenanceRange, ProvenanceRange def, ProvenanceRange use,
199         const std::string &expansion);
200     Origin(ProvenanceRange, const std::string &);
201 
202     const char &operator[](std::size_t) const;
203 
204     std::variant<Inclusion, Macro, CompilerInsertion> u;
205     ProvenanceRange covers, replaces;
206   };
207 
208   const Origin &MapToOrigin(Provenance) const;
209 
210   // Elements are in ascending & contiguous order of .covers.
211   std::vector<Origin> origin_;
212   ProvenanceRange range_;
213   std::map<char, Provenance> compilerInsertionProvenance_;
214   std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
215   std::list<std::string> searchPath_;
216   Encoding encoding_{Encoding::UTF_8};
217 };
218 
219 // Represents the result of preprocessing and prescanning a single source
220 // file (and all its inclusions) or module file.  Parsers operate within
221 // single instances of CookedSource.
222 class CookedSource {
223 public:
number()224   int number() const { return number_; }
set_number(int n)225   void set_number(int n) { number_ = n; }
226 
AsCharBlock()227   CharBlock AsCharBlock() const { return CharBlock{data_}; }
228   std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
229   std::optional<CharBlock> GetCharBlock(ProvenanceRange) const;
230 
231   // The result of a Put() is the offset that the new data
232   // will have in the eventually marshaled contiguous buffer.
Put(const char * data,std::size_t bytes)233   std::size_t Put(const char *data, std::size_t bytes) {
234     return buffer_.Put(data, bytes);
235   }
Put(const std::string & s)236   std::size_t Put(const std::string &s) { return buffer_.Put(s); }
Put(char ch)237   std::size_t Put(char ch) { return buffer_.Put(&ch, 1); }
Put(char ch,Provenance p)238   std::size_t Put(char ch, Provenance p) {
239     provenanceMap_.Put(ProvenanceRange{p, 1});
240     return buffer_.Put(&ch, 1);
241   }
242 
PutProvenance(Provenance p)243   void PutProvenance(Provenance p) { provenanceMap_.Put(ProvenanceRange{p}); }
PutProvenance(ProvenanceRange pr)244   void PutProvenance(ProvenanceRange pr) { provenanceMap_.Put(pr); }
PutProvenanceMappings(const OffsetToProvenanceMappings & pm)245   void PutProvenanceMappings(const OffsetToProvenanceMappings &pm) {
246     provenanceMap_.Put(pm);
247   }
248 
249   std::size_t BufferedBytes() const;
250   void Marshal(AllCookedSources &); // marshals text into one contiguous block
251   void CompileProvenanceRangeToOffsetMappings(AllSources &);
252   llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
253 
254 private:
255   int number_{0}; // for sorting purposes
256   CharBuffer buffer_; // before Marshal()
257   std::string data_; // all of it, prescanned and preprocessed
258   OffsetToProvenanceMappings provenanceMap_;
259   ProvenanceRangeToOffsetMappings invertedMap_;
260 };
261 
262 class AllCookedSources {
263 public:
264   explicit AllCookedSources(AllSources &);
265   ~AllCookedSources();
266 
allSources()267   AllSources &allSources() { return allSources_; }
allSources()268   const AllSources &allSources() const { return allSources_; }
269 
270   CookedSource &NewCookedSource();
271 
272   const CookedSource *Find(CharBlock) const;
Find(const char * p)273   const CookedSource *Find(const char *p) const { return Find(CharBlock{p}); }
274 
IsValid(ProvenanceRange r)275   bool IsValid(ProvenanceRange r) const { return allSources_.IsValid(r); }
276 
277   std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
278   std::optional<CharBlock> GetCharBlockFromLineAndColumns(
279       int line, int startColumn, int endColumn) const;
280   std::optional<std::pair<SourcePosition, SourcePosition>>
281       GetSourcePositionRange(CharBlock) const;
282   std::optional<CharBlock> GetCharBlock(ProvenanceRange) const;
283   void Dump(llvm::raw_ostream &) const;
284 
285   // For sorting symbol names without being dependent on pointer values
286   bool Precedes(CharBlock, CharBlock) const;
287 
288   // Once a CookedSource is complete, add it to index_ and assign its number_
289   void Register(CookedSource &);
290 
291 private:
292   AllSources &allSources_;
293   std::list<CookedSource> cooked_; // owns all CookedSource instances
294   std::map<CharBlock, const CookedSource &, CharBlockPointerComparator> index_;
295 };
296 
297 // For use as a Comparator for maps, sets, sorting, &c.
298 class CharBlockComparator {
299 public:
CharBlockComparator(const AllCookedSources & all)300   explicit CharBlockComparator(const AllCookedSources &all) : all_{all} {}
operator()301   bool operator()(CharBlock x, CharBlock y) const {
302     return all_.Precedes(x, y);
303   }
304 
305 private:
306   const AllCookedSources &all_;
307 };
308 
309 } // namespace Fortran::parser
310 #endif // FORTRAN_PARSER_PROVENANCE_H_
311