//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/Frontend/TextDiagnostic.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Locale.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <optional> usingnamespaceclang; static const enum raw_ostream::Colors noteColor = …; static const enum raw_ostream::Colors remarkColor = …; static const enum raw_ostream::Colors fixitColor = …; static const enum raw_ostream::Colors caretColor = …; static const enum raw_ostream::Colors warningColor = …; static const enum raw_ostream::Colors templateColor = …; static const enum raw_ostream::Colors errorColor = …; static const enum raw_ostream::Colors fatalColor = …; // Used for changing only the bold attribute. static const enum raw_ostream::Colors savedColor = …; // Magenta is taken for 'warning'. Red is already 'error' and 'cyan' // is already taken for 'note'. Green is already used to underline // source ranges. White and black are bad because of the usual // terminal backgrounds. Which leaves us only with TWO options. static constexpr raw_ostream::Colors CommentColor = …; static constexpr raw_ostream::Colors LiteralColor = …; static constexpr raw_ostream::Colors KeywordColor = …; /// Add highlights to differences in template strings. static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, bool &Normal, bool Bold) { … } /// Number of spaces to indent when word-wrapping. const unsigned WordWrapIndentation = …; static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { … } /// returns a printable representation of first item from input range /// /// This function returns a printable representation of the next item in a line /// of source. If the next byte begins a valid and printable character, that /// character is returned along with 'true'. /// /// Otherwise, if the next byte begins a valid, but unprintable character, a /// printable, escaped representation of the character is returned, along with /// 'false'. Otherwise a printable, escaped representation of the next byte /// is returned along with 'false'. /// /// \note The index is updated to be used with a subsequent call to /// printableTextForNextCharacter. /// /// \param SourceLine The line of source /// \param I Pointer to byte index, /// \param TabStop used to expand tabs /// \return pair(printable text, 'true' iff original text was printable) /// static std::pair<SmallString<16>, bool> printableTextForNextCharacter(StringRef SourceLine, size_t *I, unsigned TabStop) { … } static void expandTabs(std::string &SourceLine, unsigned TabStop) { … } /// \p BytesOut: /// A mapping from columns to the byte of the source line that produced the /// character displaying at that column. This is the inverse of \p ColumnsOut. /// /// The last element in the array is the number of bytes in the source string. /// /// example: (given a tabstop of 8) /// /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} /// /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to /// display) /// /// \p ColumnsOut: /// A mapping from the bytes /// of the printable representation of the line to the columns those printable /// characters will appear at (numbering the first column as 0). /// /// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab /// character) then the array will map that byte to the first column the /// tab appears at and the next value in the map will have been incremented /// more than once. /// /// If a byte is the first in a sequence of bytes that together map to a single /// entity in the output, then the array will map that byte to the appropriate /// column while the subsequent bytes will be -1. /// /// The last element in the array does not correspond to any byte in the input /// and instead is the number of columns needed to display the source /// /// example: (given a tabstop of 8) /// /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} /// /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to /// display) static void genColumnByteMapping(StringRef SourceLine, unsigned TabStop, SmallVectorImpl<int> &BytesOut, SmallVectorImpl<int> &ColumnsOut) { … } namespace { struct SourceColumnMap { … }; } // end anonymous namespace /// When the source code line we want to print is too long for /// the terminal, select the "interesting" region. static void selectInterestingSourceRegion(std::string &SourceLine, std::string &CaretLine, std::string &FixItInsertionLine, unsigned Columns, const SourceColumnMap &map) { … } /// Skip over whitespace in the string, starting at the given /// index. /// /// \returns The index of the first non-whitespace character that is /// greater than or equal to Idx or, if no such character exists, /// returns the end of the string. static unsigned skipWhitespace(unsigned Idx, StringRef Str, unsigned Length) { … } /// If the given character is the start of some kind of /// balanced punctuation (e.g., quotes or parentheses), return the /// character that will terminate the punctuation. /// /// \returns The ending punctuation character, if any, or the NULL /// character if the input character does not start any punctuation. static inline char findMatchingPunctuation(char c) { … } /// Find the end of the word starting at the given offset /// within a string. /// /// \returns the index pointing one character past the end of the /// word. static unsigned findEndOfWord(unsigned Start, StringRef Str, unsigned Length, unsigned Column, unsigned Columns) { … } /// Print the given string to a stream, word-wrapping it to /// some number of columns in the process. /// /// \param OS the stream to which the word-wrapping string will be /// emitted. /// \param Str the string to word-wrap and output. /// \param Columns the number of columns to word-wrap to. /// \param Column the column number at which the first character of \p /// Str will be printed. This will be non-zero when part of the first /// line has already been printed. /// \param Bold if the current text should be bold /// \returns true if word-wrapping was required, or false if the /// string fit on the first line. static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns, unsigned Column, bool Bold) { … } TextDiagnostic::TextDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, DiagnosticOptions *DiagOpts, const Preprocessor *PP) : … { … } TextDiagnostic::~TextDiagnostic() { … } void TextDiagnostic::emitDiagnosticMessage( FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, DiagOrStoredDiag D) { … } /*static*/ void TextDiagnostic::printDiagnosticLevel(raw_ostream &OS, DiagnosticsEngine::Level Level, bool ShowColors) { … } /*static*/ void TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, bool IsSupplemental, StringRef Message, unsigned CurrentColumn, unsigned Columns, bool ShowColors) { … } void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) { … } /// Print out the file/line/column information and include trace. /// /// This method handles the emission of the diagnostic location information. /// This includes extracting as much location information as is present for /// the diagnostic and printing it, as well as any include stack or source /// ranges necessary. void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, ArrayRef<CharSourceRange> Ranges) { … } void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { … } void TextDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) { … } void TextDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc, StringRef ModuleName) { … } /// Find the suitable set of lines to show to include a set of ranges. static std::optional<std::pair<unsigned, unsigned>> findLinesForRange(const CharSourceRange &R, FileID FID, const SourceManager &SM) { … } /// Add as much of range B into range A as possible without exceeding a maximum /// size of MaxRange. Ranges are inclusive. static std::pair<unsigned, unsigned> maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B, unsigned MaxRange) { … } struct LineRange { … }; /// Highlight \p R (with ~'s) on the current source line. static void highlightRange(const LineRange &R, const SourceColumnMap &Map, std::string &CaretLine) { … } static std::string buildFixItInsertionLine(FileID FID, unsigned LineNo, const SourceColumnMap &map, ArrayRef<FixItHint> Hints, const SourceManager &SM, const DiagnosticOptions *DiagOpts) { … } static unsigned getNumDisplayWidth(unsigned N) { … } /// Filter out invalid ranges, ranges that don't fit into the window of /// source lines we will print, and ranges from other files. /// /// For the remaining ranges, convert them to simple LineRange structs, /// which only cover one line at a time. static SmallVector<LineRange> prepareAndFilterRanges(const SmallVectorImpl<CharSourceRange> &Ranges, const SourceManager &SM, const std::pair<unsigned, unsigned> &Lines, FileID FID, const LangOptions &LangOpts) { … } /// Creates syntax highlighting information in form of StyleRanges. /// /// The returned unique ptr has always exactly size /// (\p EndLineNumber - \p StartLineNumber + 1). Each SmallVector in there /// corresponds to syntax highlighting information in one line. In each line, /// the StyleRanges are non-overlapping and sorted from start to end of the /// line. static std::unique_ptr<llvm::SmallVector<TextDiagnostic::StyleRange>[]> highlightLines(StringRef FileData, unsigned StartLineNumber, unsigned EndLineNumber, const Preprocessor *PP, const LangOptions &LangOpts, bool ShowColors, FileID FID, const SourceManager &SM) { … } /// Emit a code snippet and caret line. /// /// This routine emits a single line's code snippet and caret line.. /// /// \param Loc The location for the caret. /// \param Ranges The underlined ranges for this code snippet. /// \param Hints The FixIt hints active for this diagnostic. void TextDiagnostic::emitSnippetAndCaret( FullSourceLoc Loc, DiagnosticsEngine::Level Level, SmallVectorImpl<CharSourceRange> &Ranges, ArrayRef<FixItHint> Hints) { … } void TextDiagnostic::emitSnippet(StringRef SourceLine, unsigned MaxLineNoDisplayWidth, unsigned LineNo, unsigned DisplayLineNo, ArrayRef<StyleRange> Styles) { … } void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, const SourceManager &SM) { … }