10b57cec5SDimitry Andric //===- SourceMgr.h - Manager for Source Buffers & Diagnostics ---*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file declares the SMDiagnostic and SourceMgr classes. This 100b57cec5SDimitry Andric // provides a simple substrate for diagnostics, #include handling, and other low 110b57cec5SDimitry Andric // level things for simple parsers. 120b57cec5SDimitry Andric // 130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #ifndef LLVM_SUPPORT_SOURCEMGR_H 160b57cec5SDimitry Andric #define LLVM_SUPPORT_SOURCEMGR_H 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 190b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 200b57cec5SDimitry Andric #include "llvm/Support/SMLoc.h" 210b57cec5SDimitry Andric #include <vector> 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric namespace llvm { 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric class raw_ostream; 260b57cec5SDimitry Andric class SMDiagnostic; 270b57cec5SDimitry Andric class SMFixIt; 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric /// This owns the files read by a parser, handles include stacks, 300b57cec5SDimitry Andric /// and handles diagnostic wrangling. 310b57cec5SDimitry Andric class SourceMgr { 320b57cec5SDimitry Andric public: 330b57cec5SDimitry Andric enum DiagKind { 340b57cec5SDimitry Andric DK_Error, 350b57cec5SDimitry Andric DK_Warning, 360b57cec5SDimitry Andric DK_Remark, 370b57cec5SDimitry Andric DK_Note, 380b57cec5SDimitry Andric }; 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric /// Clients that want to handle their own diagnostics in a custom way can 410b57cec5SDimitry Andric /// register a function pointer+context as a diagnostic handler. 420b57cec5SDimitry Andric /// It gets called each time PrintMessage is invoked. 430b57cec5SDimitry Andric using DiagHandlerTy = void (*)(const SMDiagnostic &, void *Context); 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric private: 460b57cec5SDimitry Andric struct SrcBuffer { 470b57cec5SDimitry Andric /// The memory buffer for the file. 480b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> Buffer; 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric /// Vector of offsets into Buffer at which there are line-endings 510b57cec5SDimitry Andric /// (lazily populated). Once populated, the '\n' that marks the end of 520b57cec5SDimitry Andric /// line number N from [1..] is at Buffer[OffsetCache[N-1]]. Since 530b57cec5SDimitry Andric /// these offsets are in sorted (ascending) order, they can be 540b57cec5SDimitry Andric /// binary-searched for the first one after any given offset (eg. an 550b57cec5SDimitry Andric /// offset corresponding to a particular SMLoc). 565ffd83dbSDimitry Andric /// 575ffd83dbSDimitry Andric /// Since we're storing offsets into relatively small files (often smaller 585ffd83dbSDimitry Andric /// than 2^8 or 2^16 bytes), we select the offset vector element type 595ffd83dbSDimitry Andric /// dynamically based on the size of Buffer. 605ffd83dbSDimitry Andric mutable void *OffsetCache = nullptr; 610b57cec5SDimitry Andric 625f757f3fSDimitry Andric /// Look up a given \p Ptr in the buffer, determining which line it came 635ffd83dbSDimitry Andric /// from. 640b57cec5SDimitry Andric unsigned getLineNumber(const char *Ptr) const; 655ffd83dbSDimitry Andric template <typename T> 665ffd83dbSDimitry Andric unsigned getLineNumberSpecialized(const char *Ptr) const; 675ffd83dbSDimitry Andric 685ffd83dbSDimitry Andric /// Return a pointer to the first character of the specified line number or 695ffd83dbSDimitry Andric /// null if the line number is invalid. 705ffd83dbSDimitry Andric const char *getPointerForLineNumber(unsigned LineNo) const; 715ffd83dbSDimitry Andric template <typename T> 725ffd83dbSDimitry Andric const char *getPointerForLineNumberSpecialized(unsigned LineNo) const; 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric /// This is the location of the parent include, or null if at the top level. 750b57cec5SDimitry Andric SMLoc IncludeLoc; 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric SrcBuffer() = default; 780b57cec5SDimitry Andric SrcBuffer(SrcBuffer &&); 790b57cec5SDimitry Andric SrcBuffer(const SrcBuffer &) = delete; 800b57cec5SDimitry Andric SrcBuffer &operator=(const SrcBuffer &) = delete; 810b57cec5SDimitry Andric ~SrcBuffer(); 820b57cec5SDimitry Andric }; 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric /// This is all of the buffers that we are reading from. 850b57cec5SDimitry Andric std::vector<SrcBuffer> Buffers; 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric // This is the list of directories we should search for include files in. 880b57cec5SDimitry Andric std::vector<std::string> IncludeDirectories; 890b57cec5SDimitry Andric 900b57cec5SDimitry Andric DiagHandlerTy DiagHandler = nullptr; 910b57cec5SDimitry Andric void *DiagContext = nullptr; 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric bool isValidBufferID(unsigned i) const { return i && i <= Buffers.size(); } 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric public: 960b57cec5SDimitry Andric SourceMgr() = default; 970b57cec5SDimitry Andric SourceMgr(const SourceMgr &) = delete; 980b57cec5SDimitry Andric SourceMgr &operator=(const SourceMgr &) = delete; 990b57cec5SDimitry Andric SourceMgr(SourceMgr &&) = default; 1000b57cec5SDimitry Andric SourceMgr &operator=(SourceMgr &&) = default; 1010b57cec5SDimitry Andric ~SourceMgr() = default; 1020b57cec5SDimitry Andric 10381ad6265SDimitry Andric /// Return the include directories of this source manager. 10481ad6265SDimitry Andric ArrayRef<std::string> getIncludeDirs() const { return IncludeDirectories; } 10581ad6265SDimitry Andric 1060b57cec5SDimitry Andric void setIncludeDirs(const std::vector<std::string> &Dirs) { 1070b57cec5SDimitry Andric IncludeDirectories = Dirs; 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric /// Specify a diagnostic handler to be invoked every time PrintMessage is 1110b57cec5SDimitry Andric /// called. \p Ctx is passed into the handler when it is invoked. 1120b57cec5SDimitry Andric void setDiagHandler(DiagHandlerTy DH, void *Ctx = nullptr) { 1130b57cec5SDimitry Andric DiagHandler = DH; 1140b57cec5SDimitry Andric DiagContext = Ctx; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric DiagHandlerTy getDiagHandler() const { return DiagHandler; } 1180b57cec5SDimitry Andric void *getDiagContext() const { return DiagContext; } 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric const SrcBuffer &getBufferInfo(unsigned i) const { 1210b57cec5SDimitry Andric assert(isValidBufferID(i)); 1220b57cec5SDimitry Andric return Buffers[i - 1]; 1230b57cec5SDimitry Andric } 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric const MemoryBuffer *getMemoryBuffer(unsigned i) const { 1260b57cec5SDimitry Andric assert(isValidBufferID(i)); 1270b57cec5SDimitry Andric return Buffers[i - 1].Buffer.get(); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1305ffd83dbSDimitry Andric unsigned getNumBuffers() const { return Buffers.size(); } 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric unsigned getMainFileID() const { 1330b57cec5SDimitry Andric assert(getNumBuffers()); 1340b57cec5SDimitry Andric return 1; 1350b57cec5SDimitry Andric } 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric SMLoc getParentIncludeLoc(unsigned i) const { 1380b57cec5SDimitry Andric assert(isValidBufferID(i)); 1390b57cec5SDimitry Andric return Buffers[i - 1].IncludeLoc; 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric /// Add a new source buffer to this source manager. This takes ownership of 1430b57cec5SDimitry Andric /// the memory buffer. 1440b57cec5SDimitry Andric unsigned AddNewSourceBuffer(std::unique_ptr<MemoryBuffer> F, 1450b57cec5SDimitry Andric SMLoc IncludeLoc) { 1460b57cec5SDimitry Andric SrcBuffer NB; 1470b57cec5SDimitry Andric NB.Buffer = std::move(F); 1480b57cec5SDimitry Andric NB.IncludeLoc = IncludeLoc; 1490b57cec5SDimitry Andric Buffers.push_back(std::move(NB)); 1500b57cec5SDimitry Andric return Buffers.size(); 1510b57cec5SDimitry Andric } 1520b57cec5SDimitry Andric 15381ad6265SDimitry Andric /// Takes the source buffers from the given source manager and append them to 15481ad6265SDimitry Andric /// the current manager. `MainBufferIncludeLoc` is an optional include 15581ad6265SDimitry Andric /// location to attach to the main buffer of `SrcMgr` after it gets moved to 15681ad6265SDimitry Andric /// the current manager. 15781ad6265SDimitry Andric void takeSourceBuffersFrom(SourceMgr &SrcMgr, 15881ad6265SDimitry Andric SMLoc MainBufferIncludeLoc = SMLoc()) { 15981ad6265SDimitry Andric if (SrcMgr.Buffers.empty()) 16081ad6265SDimitry Andric return; 16181ad6265SDimitry Andric 16281ad6265SDimitry Andric size_t OldNumBuffers = getNumBuffers(); 16381ad6265SDimitry Andric std::move(SrcMgr.Buffers.begin(), SrcMgr.Buffers.end(), 16481ad6265SDimitry Andric std::back_inserter(Buffers)); 16581ad6265SDimitry Andric SrcMgr.Buffers.clear(); 16681ad6265SDimitry Andric Buffers[OldNumBuffers].IncludeLoc = MainBufferIncludeLoc; 16781ad6265SDimitry Andric } 16881ad6265SDimitry Andric 1690b57cec5SDimitry Andric /// Search for a file with the specified name in the current directory or in 1700b57cec5SDimitry Andric /// one of the IncludeDirs. 1710b57cec5SDimitry Andric /// 1720b57cec5SDimitry Andric /// If no file is found, this returns 0, otherwise it returns the buffer ID 1730b57cec5SDimitry Andric /// of the stacked file. The full path to the included file can be found in 1740b57cec5SDimitry Andric /// \p IncludedFile. 1750b57cec5SDimitry Andric unsigned AddIncludeFile(const std::string &Filename, SMLoc IncludeLoc, 1760b57cec5SDimitry Andric std::string &IncludedFile); 1770b57cec5SDimitry Andric 17881ad6265SDimitry Andric /// Search for a file with the specified name in the current directory or in 17981ad6265SDimitry Andric /// one of the IncludeDirs, and try to open it **without** adding to the 18081ad6265SDimitry Andric /// SourceMgr. If the opened file is intended to be added to the source 18181ad6265SDimitry Andric /// manager, prefer `AddIncludeFile` instead. 18281ad6265SDimitry Andric /// 18381ad6265SDimitry Andric /// If no file is found, this returns an Error, otherwise it returns the 18481ad6265SDimitry Andric /// buffer of the stacked file. The full path to the included file can be 18581ad6265SDimitry Andric /// found in \p IncludedFile. 18681ad6265SDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> 18781ad6265SDimitry Andric OpenIncludeFile(const std::string &Filename, std::string &IncludedFile); 18881ad6265SDimitry Andric 1890b57cec5SDimitry Andric /// Return the ID of the buffer containing the specified location. 1900b57cec5SDimitry Andric /// 1910b57cec5SDimitry Andric /// 0 is returned if the buffer is not found. 1920b57cec5SDimitry Andric unsigned FindBufferContainingLoc(SMLoc Loc) const; 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric /// Find the line number for the specified location in the specified file. 1950b57cec5SDimitry Andric /// This is not a fast method. 1960b57cec5SDimitry Andric unsigned FindLineNumber(SMLoc Loc, unsigned BufferID = 0) const { 1970b57cec5SDimitry Andric return getLineAndColumn(Loc, BufferID).first; 1980b57cec5SDimitry Andric } 1990b57cec5SDimitry Andric 2000b57cec5SDimitry Andric /// Find the line and column number for the specified location in the 2010b57cec5SDimitry Andric /// specified file. This is not a fast method. 2020b57cec5SDimitry Andric std::pair<unsigned, unsigned> getLineAndColumn(SMLoc Loc, 2030b57cec5SDimitry Andric unsigned BufferID = 0) const; 2040b57cec5SDimitry Andric 205e8d8bef9SDimitry Andric /// Get a string with the \p SMLoc filename and line number 206e8d8bef9SDimitry Andric /// formatted in the standard style. 207e8d8bef9SDimitry Andric std::string getFormattedLocationNoOffset(SMLoc Loc, 208e8d8bef9SDimitry Andric bool IncludePath = false) const; 209e8d8bef9SDimitry Andric 2105ffd83dbSDimitry Andric /// Given a line and column number in a mapped buffer, turn it into an SMLoc. 2115ffd83dbSDimitry Andric /// This will return a null SMLoc if the line/column location is invalid. 2125ffd83dbSDimitry Andric SMLoc FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo, 2135ffd83dbSDimitry Andric unsigned ColNo); 2145ffd83dbSDimitry Andric 2150b57cec5SDimitry Andric /// Emit a message about the specified location with the specified string. 2160b57cec5SDimitry Andric /// 2170b57cec5SDimitry Andric /// \param ShowColors Display colored messages if output is a terminal and 2180b57cec5SDimitry Andric /// the default error handler is used. 2195ffd83dbSDimitry Andric void PrintMessage(raw_ostream &OS, SMLoc Loc, DiagKind Kind, const Twine &Msg, 2205ffd83dbSDimitry Andric ArrayRef<SMRange> Ranges = {}, 2215ffd83dbSDimitry Andric ArrayRef<SMFixIt> FixIts = {}, 2220b57cec5SDimitry Andric bool ShowColors = true) const; 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric /// Emits a diagnostic to llvm::errs(). 2250b57cec5SDimitry Andric void PrintMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, 2265ffd83dbSDimitry Andric ArrayRef<SMRange> Ranges = {}, 2275ffd83dbSDimitry Andric ArrayRef<SMFixIt> FixIts = {}, 2280b57cec5SDimitry Andric bool ShowColors = true) const; 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric /// Emits a manually-constructed diagnostic to the given output stream. 2310b57cec5SDimitry Andric /// 2320b57cec5SDimitry Andric /// \param ShowColors Display colored messages if output is a terminal and 2330b57cec5SDimitry Andric /// the default error handler is used. 2340b57cec5SDimitry Andric void PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, 2350b57cec5SDimitry Andric bool ShowColors = true) const; 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric /// Return an SMDiagnostic at the specified location with the specified 2380b57cec5SDimitry Andric /// string. 2390b57cec5SDimitry Andric /// 2400b57cec5SDimitry Andric /// \param Msg If non-null, the kind of message (e.g., "error") which is 2410b57cec5SDimitry Andric /// prefixed to the message. 2420b57cec5SDimitry Andric SMDiagnostic GetMessage(SMLoc Loc, DiagKind Kind, const Twine &Msg, 2435ffd83dbSDimitry Andric ArrayRef<SMRange> Ranges = {}, 2445ffd83dbSDimitry Andric ArrayRef<SMFixIt> FixIts = {}) const; 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric /// Prints the names of included files and the line of the file they were 2470b57cec5SDimitry Andric /// included from. A diagnostic handler can use this before printing its 2480b57cec5SDimitry Andric /// custom formatted message. 2490b57cec5SDimitry Andric /// 2500b57cec5SDimitry Andric /// \param IncludeLoc The location of the include. 2510b57cec5SDimitry Andric /// \param OS the raw_ostream to print on. 2520b57cec5SDimitry Andric void PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const; 2530b57cec5SDimitry Andric }; 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric /// Represents a single fixit, a replacement of one range of text with another. 2560b57cec5SDimitry Andric class SMFixIt { 2570b57cec5SDimitry Andric SMRange Range; 2580b57cec5SDimitry Andric 2590b57cec5SDimitry Andric std::string Text; 2600b57cec5SDimitry Andric 2610b57cec5SDimitry Andric public: 2625ffd83dbSDimitry Andric SMFixIt(SMRange R, const Twine &Replacement); 2630b57cec5SDimitry Andric 2645ffd83dbSDimitry Andric SMFixIt(SMLoc Loc, const Twine &Replacement) 2655ffd83dbSDimitry Andric : SMFixIt(SMRange(Loc, Loc), Replacement) {} 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric StringRef getText() const { return Text; } 2680b57cec5SDimitry Andric SMRange getRange() const { return Range; } 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric bool operator<(const SMFixIt &Other) const { 2710b57cec5SDimitry Andric if (Range.Start.getPointer() != Other.Range.Start.getPointer()) 2720b57cec5SDimitry Andric return Range.Start.getPointer() < Other.Range.Start.getPointer(); 2730b57cec5SDimitry Andric if (Range.End.getPointer() != Other.Range.End.getPointer()) 2740b57cec5SDimitry Andric return Range.End.getPointer() < Other.Range.End.getPointer(); 2750b57cec5SDimitry Andric return Text < Other.Text; 2760b57cec5SDimitry Andric } 2770b57cec5SDimitry Andric }; 2780b57cec5SDimitry Andric 2790b57cec5SDimitry Andric /// Instances of this class encapsulate one diagnostic report, allowing 2800b57cec5SDimitry Andric /// printing to a raw_ostream as a caret diagnostic. 2810b57cec5SDimitry Andric class SMDiagnostic { 2820b57cec5SDimitry Andric const SourceMgr *SM = nullptr; 2830b57cec5SDimitry Andric SMLoc Loc; 2840b57cec5SDimitry Andric std::string Filename; 2850b57cec5SDimitry Andric int LineNo = 0; 2860b57cec5SDimitry Andric int ColumnNo = 0; 2870b57cec5SDimitry Andric SourceMgr::DiagKind Kind = SourceMgr::DK_Error; 2880b57cec5SDimitry Andric std::string Message, LineContents; 2890b57cec5SDimitry Andric std::vector<std::pair<unsigned, unsigned>> Ranges; 2900b57cec5SDimitry Andric SmallVector<SMFixIt, 4> FixIts; 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric public: 2930b57cec5SDimitry Andric // Null diagnostic. 2940b57cec5SDimitry Andric SMDiagnostic() = default; 2950b57cec5SDimitry Andric // Diagnostic with no location (e.g. file not found, command line arg error). 2960b57cec5SDimitry Andric SMDiagnostic(StringRef filename, SourceMgr::DiagKind Knd, StringRef Msg) 2970b57cec5SDimitry Andric : Filename(filename), LineNo(-1), ColumnNo(-1), Kind(Knd), Message(Msg) {} 2980b57cec5SDimitry Andric 2990b57cec5SDimitry Andric // Diagnostic with a location. 3005ffd83dbSDimitry Andric SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, int Line, int Col, 3015ffd83dbSDimitry Andric SourceMgr::DiagKind Kind, StringRef Msg, StringRef LineStr, 3020b57cec5SDimitry Andric ArrayRef<std::pair<unsigned, unsigned>> Ranges, 3035ffd83dbSDimitry Andric ArrayRef<SMFixIt> FixIts = {}); 3040b57cec5SDimitry Andric 3050b57cec5SDimitry Andric const SourceMgr *getSourceMgr() const { return SM; } 3060b57cec5SDimitry Andric SMLoc getLoc() const { return Loc; } 3070b57cec5SDimitry Andric StringRef getFilename() const { return Filename; } 3080b57cec5SDimitry Andric int getLineNo() const { return LineNo; } 3090b57cec5SDimitry Andric int getColumnNo() const { return ColumnNo; } 3100b57cec5SDimitry Andric SourceMgr::DiagKind getKind() const { return Kind; } 3110b57cec5SDimitry Andric StringRef getMessage() const { return Message; } 3120b57cec5SDimitry Andric StringRef getLineContents() const { return LineContents; } 3130b57cec5SDimitry Andric ArrayRef<std::pair<unsigned, unsigned>> getRanges() const { return Ranges; } 3140b57cec5SDimitry Andric 3155ffd83dbSDimitry Andric void addFixIt(const SMFixIt &Hint) { FixIts.push_back(Hint); } 3160b57cec5SDimitry Andric 3175ffd83dbSDimitry Andric ArrayRef<SMFixIt> getFixIts() const { return FixIts; } 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric void print(const char *ProgName, raw_ostream &S, bool ShowColors = true, 320*0fca6ea1SDimitry Andric bool ShowKindLabel = true, bool ShowLocation = true) const; 3210b57cec5SDimitry Andric }; 3220b57cec5SDimitry Andric 3230b57cec5SDimitry Andric } // end namespace llvm 3240b57cec5SDimitry Andric 3250b57cec5SDimitry Andric #endif // LLVM_SUPPORT_SOURCEMGR_H 326