FLANG
provenance.h
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
29namespace 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
49class AllSources;
51
52class Provenance {
53public:
54 Provenance() {}
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
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
77private:
78 std::size_t offset_{0};
79};
80
81using 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.
88class ProvenanceRangeToOffsetMappings {
89public:
90 ProvenanceRangeToOffsetMappings();
91 ~ProvenanceRangeToOffsetMappings();
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
97private:
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.
112class OffsetToProvenanceMappings {
113public:
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
126private:
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.
139class AllSources {
140public:
141 AllSources();
142 ~AllSources();
143
144 std::size_t size() const { return range_.size(); }
145 const char &operator[](Provenance) const;
146 Encoding encoding() const { return encoding_; }
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 *OpenPath(std::string path, llvm::raw_ostream &error);
155 const SourceFile *Open(std::string path, llvm::raw_ostream &error,
156 std::optional<std::string> &&prependPath = std::nullopt);
157 const SourceFile *ReadStandardInput(llvm::raw_ostream &error);
158
159 // Returns the paths of every source file opened so far: the main file and any
160 // files pulled in by INCLUDE lines or #include directives. Paths are returned
161 // without duplicates, in the order they were first opened.
162 std::vector<std::string> GetIncludedFilePaths() const;
163
164 ProvenanceRange AddIncludedFile(
165 const SourceFile &, ProvenanceRange, bool isModule = false);
166 ProvenanceRange AddMacroCall(
167 ProvenanceRange def, ProvenanceRange use, const std::string &expansion);
168 ProvenanceRange AddCompilerInsertion(std::string);
169
170 // If provenance is in an expanded macro, return the starting provenance of
171 // the replaced macro. Otherwise, return the input provenance.
172 Provenance GetReplacedProvenance(Provenance) const;
173
174 bool IsValid(Provenance at) const { return range_.Contains(at); }
175 bool IsValid(ProvenanceRange range) const {
176 return range.size() > 0 && range_.Contains(range);
177 }
178 void setShowColors(bool showColors) { showColors_ = showColors; }
179 bool getShowColors() const { return showColors_; }
180 std::optional<ProvenanceRange> GetInclusionInfo(
181 const std::optional<ProvenanceRange> &) const;
182 void EmitMessage(llvm::raw_ostream &, const std::optional<ProvenanceRange> &,
183 const std::string &message, const std::string &prefix,
184 llvm::raw_ostream::Colors color, bool echoSourceLine = false) const;
185 const SourceFile *GetSourceFile(
186 Provenance, std::size_t *offset = nullptr, bool topLevel = false) const;
187 const char *GetSource(ProvenanceRange) const;
188 std::optional<SourcePosition> GetSourcePosition(Provenance) const;
189 std::optional<ProvenanceRange> GetFirstFileProvenance() const;
190 std::string GetPath(Provenance, bool topLevel = false) const; // __FILE__
191 int GetLineNumber(Provenance) const; // __LINE__
192 Provenance CompilerInsertionProvenance(char ch);
193 ProvenanceRange IntersectionWithSourceFiles(ProvenanceRange) const;
194 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
195
196private:
197 struct Inclusion {
198 const SourceFile &source;
199 bool isModule{false};
200 };
201 struct Macro {
202 ProvenanceRange definition;
203 std::string expansion;
204 };
205 struct CompilerInsertion {
206 std::string text;
207 };
208
209 struct Origin {
210 Origin(ProvenanceRange, const SourceFile &);
211 Origin(ProvenanceRange, const SourceFile &, ProvenanceRange,
212 bool isModule = false);
213 Origin(ProvenanceRange, ProvenanceRange def, ProvenanceRange use,
214 const std::string &expansion);
215 Origin(ProvenanceRange, const std::string &);
216
217 const char &operator[](std::size_t) const;
218
219 std::variant<Inclusion, Macro, CompilerInsertion> u;
220 ProvenanceRange covers, replaces;
221 };
222
223 const Origin &MapToOrigin(Provenance) const;
224
225 // Elements are in ascending & contiguous order of .covers.
226 std::vector<Origin> origin_;
227 ProvenanceRange range_;
228 std::map<char, Provenance> compilerInsertionProvenance_;
229 std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
230 std::list<std::string> searchPath_;
231 Encoding encoding_{Encoding::UTF_8};
232 bool showColors_{false};
233};
234
235// Represents the result of preprocessing and prescanning a single source
236// file (and all its inclusions) or module file. Parsers operate within
237// single instances of CookedSource.
238class CookedSource {
239public:
240 explicit CookedSource(AllSources &allSources) : allSources_{allSources} {};
241
242 int number() const { return number_; }
243 void set_number(int n) { number_ = n; }
244
245 CharBlock AsCharBlock() const { return CharBlock{data_}; }
246 std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
247 std::optional<CharBlock> GetCharBlock(ProvenanceRange) const;
248
249 // The result of a Put() is the offset that the new data
250 // will have in the eventually marshaled contiguous buffer.
251 std::size_t Put(const char *data, std::size_t bytes) {
252 return buffer_.Put(data, bytes);
253 }
254 std::size_t Put(const std::string &s) { return buffer_.Put(s); }
255 std::size_t Put(char ch) { return buffer_.Put(&ch, 1); }
256 std::size_t Put(char ch, Provenance p) {
257 provenanceMap_.Put(ProvenanceRange{p, 1});
258 return buffer_.Put(&ch, 1);
259 }
260
261 void PutProvenance(Provenance p) { provenanceMap_.Put(ProvenanceRange{p}); }
262 void PutProvenance(ProvenanceRange pr) { provenanceMap_.Put(pr); }
263 void PutProvenanceMappings(const OffsetToProvenanceMappings &pm) {
264 provenanceMap_.Put(pm);
265 }
266
267 void MarkPossibleFixedFormContinuation() {
268 possibleFixedFormContinuations_.push_back(BufferedBytes());
269 }
270
271 std::size_t BufferedBytes() const;
272 void Marshal(AllCookedSources &); // marshals text into one contiguous block
273 void CompileProvenanceRangeToOffsetMappings(AllSources &);
274 llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
275
276private:
277 AllSources &allSources_;
278 int number_{0}; // for sorting purposes
279 CharBuffer buffer_; // before Marshal()
280 std::string data_; // all of it, prescanned and preprocessed
281 OffsetToProvenanceMappings provenanceMap_;
283 std::list<std::size_t> possibleFixedFormContinuations_;
284};
285
286class AllCookedSources {
287public:
288 explicit AllCookedSources(AllSources &);
289 ~AllCookedSources();
290
291 AllSources &allSources() { return allSources_; }
292 const AllSources &allSources() const { return allSources_; }
293
294 CookedSource &NewCookedSource();
295
296 const CookedSource *Find(CharBlock) const;
297 const CookedSource *Find(const char *p) const { return Find(CharBlock{p}); }
298
299 bool IsValid(ProvenanceRange r) const { return allSources_.IsValid(r); }
300
301 std::optional<ProvenanceRange> GetProvenanceRange(CharBlock) const;
302 std::optional<CharBlock> GetCharBlockFromLineAndColumns(
303 int line, int startColumn, int endColumn) const;
304 std::optional<std::pair<SourcePosition, SourcePosition>>
305 GetSourcePositionRange(CharBlock) const;
306 std::optional<CharBlock> GetCharBlock(ProvenanceRange) const;
307 void Dump(llvm::raw_ostream &) const;
308
309 // For sorting symbol names without being dependent on pointer values
310 bool Precedes(CharBlock, CharBlock) const;
311
312 // Once a CookedSource is complete, add it to index_ and assign its number_
313 void Register(CookedSource &);
314
315private:
316 AllSources &allSources_;
317 std::list<CookedSource> cooked_; // owns all CookedSource instances
318 std::map<CharBlock, const CookedSource &, CharBlockPointerComparator> index_;
319};
320
321} // namespace Fortran::parser
322#endif // FORTRAN_PARSER_PROVENANCE_H_
Definition interval.h:22
Definition provenance.h:286
Definition provenance.h:139
Definition char-block.h:26
Definition char-buffer.h:22
Definition provenance.h:238
Definition provenance.h:52
Definition source.h:52
Definition check-expression.h:19