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