Mercurial > hg > CbC > CbC_llvm
comparison clang/lib/Frontend/TextDiagnostic.cpp @ 252:1f2b6ac9f198 llvm-original
LLVM16-1
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 18 Aug 2023 09:04:13 +0900 |
parents | c4bab56944e8 |
children |
comparison
equal
deleted
inserted
replaced
237:c80f45b162ad | 252:1f2b6ac9f198 |
---|---|
18 #include "llvm/Support/ErrorHandling.h" | 18 #include "llvm/Support/ErrorHandling.h" |
19 #include "llvm/Support/Locale.h" | 19 #include "llvm/Support/Locale.h" |
20 #include "llvm/Support/Path.h" | 20 #include "llvm/Support/Path.h" |
21 #include "llvm/Support/raw_ostream.h" | 21 #include "llvm/Support/raw_ostream.h" |
22 #include <algorithm> | 22 #include <algorithm> |
23 #include <optional> | |
23 | 24 |
24 using namespace clang; | 25 using namespace clang; |
25 | 26 |
26 static const enum raw_ostream::Colors noteColor = | 27 static const enum raw_ostream::Colors noteColor = |
27 raw_ostream::BLACK; | 28 raw_ostream::BLACK; |
88 /// | 89 /// |
89 /// \note The index is updated to be used with a subsequent call to | 90 /// \note The index is updated to be used with a subsequent call to |
90 /// printableTextForNextCharacter. | 91 /// printableTextForNextCharacter. |
91 /// | 92 /// |
92 /// \param SourceLine The line of source | 93 /// \param SourceLine The line of source |
93 /// \param i Pointer to byte index, | 94 /// \param I Pointer to byte index, |
94 /// \param TabStop used to expand tabs | 95 /// \param TabStop used to expand tabs |
95 /// \return pair(printable text, 'true' iff original text was printable) | 96 /// \return pair(printable text, 'true' iff original text was printable) |
96 /// | 97 /// |
97 static std::pair<SmallString<16>, bool> | 98 static std::pair<SmallString<16>, bool> |
98 printableTextForNextCharacter(StringRef SourceLine, size_t *i, | 99 printableTextForNextCharacter(StringRef SourceLine, size_t *I, |
99 unsigned TabStop) { | 100 unsigned TabStop) { |
100 assert(i && "i must not be null"); | 101 assert(I && "I must not be null"); |
101 assert(*i<SourceLine.size() && "must point to a valid index"); | 102 assert(*I < SourceLine.size() && "must point to a valid index"); |
102 | 103 |
103 if (SourceLine[*i]=='\t') { | 104 if (SourceLine[*I] == '\t') { |
104 assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && | 105 assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && |
105 "Invalid -ftabstop value"); | 106 "Invalid -ftabstop value"); |
106 unsigned col = bytesSincePreviousTabOrLineBegin(SourceLine, *i); | 107 unsigned Col = bytesSincePreviousTabOrLineBegin(SourceLine, *I); |
107 unsigned NumSpaces = TabStop - col%TabStop; | 108 unsigned NumSpaces = TabStop - (Col % TabStop); |
108 assert(0 < NumSpaces && NumSpaces <= TabStop | 109 assert(0 < NumSpaces && NumSpaces <= TabStop |
109 && "Invalid computation of space amt"); | 110 && "Invalid computation of space amt"); |
110 ++(*i); | 111 ++(*I); |
111 | 112 |
112 SmallString<16> expandedTab; | 113 SmallString<16> ExpandedTab; |
113 expandedTab.assign(NumSpaces, ' '); | 114 ExpandedTab.assign(NumSpaces, ' '); |
114 return std::make_pair(expandedTab, true); | 115 return std::make_pair(ExpandedTab, true); |
115 } | 116 } |
116 | 117 |
117 unsigned char const *begin, *end; | 118 const unsigned char *Begin = SourceLine.bytes_begin() + *I; |
118 begin = reinterpret_cast<unsigned char const *>(&*(SourceLine.begin() + *i)); | 119 |
119 end = begin + (SourceLine.size() - *i); | 120 // Fast path for the common ASCII case. |
120 | 121 if (*Begin < 0x80 && llvm::sys::locale::isPrint(*Begin)) { |
121 if (llvm::isLegalUTF8Sequence(begin, end)) { | 122 ++(*I); |
122 llvm::UTF32 c; | 123 return std::make_pair(SmallString<16>(Begin, Begin + 1), true); |
123 llvm::UTF32 *cptr = &c; | 124 } |
124 unsigned char const *original_begin = begin; | 125 unsigned CharSize = llvm::getNumBytesForUTF8(*Begin); |
125 unsigned char const *cp_end = | 126 const unsigned char *End = Begin + CharSize; |
126 begin + llvm::getNumBytesForUTF8(SourceLine[*i]); | 127 |
127 | 128 // Convert it to UTF32 and check if it's printable. |
128 llvm::ConversionResult res = llvm::ConvertUTF8toUTF32( | 129 if (End <= SourceLine.bytes_end() && llvm::isLegalUTF8Sequence(Begin, End)) { |
129 &begin, cp_end, &cptr, cptr + 1, llvm::strictConversion); | 130 llvm::UTF32 C; |
130 (void)res; | 131 llvm::UTF32 *CPtr = &C; |
131 assert(llvm::conversionOK == res); | 132 |
132 assert(0 < begin-original_begin | 133 // Begin and end before conversion. |
133 && "we must be further along in the string now"); | 134 unsigned char const *OriginalBegin = Begin; |
134 *i += begin-original_begin; | 135 llvm::ConversionResult Res = llvm::ConvertUTF8toUTF32( |
135 | 136 &Begin, End, &CPtr, CPtr + 1, llvm::strictConversion); |
136 if (!llvm::sys::locale::isPrint(c)) { | 137 (void)Res; |
137 // If next character is valid UTF-8, but not printable | 138 assert(Res == llvm::conversionOK); |
138 SmallString<16> expandedCP("<U+>"); | 139 assert(OriginalBegin < Begin); |
139 while (c) { | 140 assert((Begin - OriginalBegin) == CharSize); |
140 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(c%16)); | 141 |
141 c/=16; | 142 (*I) += (Begin - OriginalBegin); |
142 } | 143 |
143 while (expandedCP.size() < 8) | 144 // Valid, multi-byte, printable UTF8 character. |
144 expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0)); | 145 if (llvm::sys::locale::isPrint(C)) |
145 return std::make_pair(expandedCP, false); | 146 return std::make_pair(SmallString<16>(OriginalBegin, End), true); |
146 } | 147 |
147 | 148 // Valid but not printable. |
148 // If next character is valid UTF-8, and printable | 149 SmallString<16> Str("<U+>"); |
149 return std::make_pair(SmallString<16>(original_begin, cp_end), true); | 150 while (C) { |
150 | 151 Str.insert(Str.begin() + 3, llvm::hexdigit(C % 16)); |
151 } | 152 C /= 16; |
152 | 153 } |
153 // If next byte is not valid UTF-8 (and therefore not printable) | 154 while (Str.size() < 8) |
154 SmallString<16> expandedByte("<XX>"); | 155 Str.insert(Str.begin() + 3, llvm::hexdigit(0)); |
155 unsigned char byte = SourceLine[*i]; | 156 return std::make_pair(Str, false); |
156 expandedByte[1] = llvm::hexdigit(byte / 16); | 157 } |
157 expandedByte[2] = llvm::hexdigit(byte % 16); | 158 |
158 ++(*i); | 159 // Otherwise, not printable since it's not valid UTF8. |
159 return std::make_pair(expandedByte, false); | 160 SmallString<16> ExpandedByte("<XX>"); |
161 unsigned char Byte = SourceLine[*I]; | |
162 ExpandedByte[1] = llvm::hexdigit(Byte / 16); | |
163 ExpandedByte[2] = llvm::hexdigit(Byte % 16); | |
164 ++(*I); | |
165 return std::make_pair(ExpandedByte, false); | |
160 } | 166 } |
161 | 167 |
162 static void expandTabs(std::string &SourceLine, unsigned TabStop) { | 168 static void expandTabs(std::string &SourceLine, unsigned TabStop) { |
163 size_t i = SourceLine.size(); | 169 size_t I = SourceLine.size(); |
164 while (i>0) { | 170 while (I > 0) { |
165 i--; | 171 I--; |
166 if (SourceLine[i]!='\t') | 172 if (SourceLine[I] != '\t') |
167 continue; | 173 continue; |
168 size_t tmp_i = i; | 174 size_t TmpI = I; |
169 std::pair<SmallString<16>,bool> res | 175 auto [Str, Printable] = |
170 = printableTextForNextCharacter(SourceLine, &tmp_i, TabStop); | 176 printableTextForNextCharacter(SourceLine, &TmpI, TabStop); |
171 SourceLine.replace(i, 1, res.first.c_str()); | 177 SourceLine.replace(I, 1, Str.c_str()); |
172 } | 178 } |
173 } | 179 } |
174 | 180 |
175 /// This function takes a raw source line and produces a mapping from the bytes | 181 /// \p BytesOut: |
182 /// A mapping from columns to the byte of the source line that produced the | |
183 /// character displaying at that column. This is the inverse of \p ColumnsOut. | |
184 /// | |
185 /// The last element in the array is the number of bytes in the source string. | |
186 /// | |
187 /// example: (given a tabstop of 8) | |
188 /// | |
189 /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} | |
190 /// | |
191 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to | |
192 /// display) | |
193 /// | |
194 /// \p ColumnsOut: | |
195 /// A mapping from the bytes | |
176 /// of the printable representation of the line to the columns those printable | 196 /// of the printable representation of the line to the columns those printable |
177 /// characters will appear at (numbering the first column as 0). | 197 /// characters will appear at (numbering the first column as 0). |
178 /// | 198 /// |
179 /// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab | 199 /// If a byte 'i' corresponds to multiple columns (e.g. the byte contains a tab |
180 /// character) then the array will map that byte to the first column the | 200 /// character) then the array will map that byte to the first column the |
192 /// | 212 /// |
193 /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} | 213 /// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} |
194 /// | 214 /// |
195 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to | 215 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to |
196 /// display) | 216 /// display) |
197 static void byteToColumn(StringRef SourceLine, unsigned TabStop, | 217 static void genColumnByteMapping(StringRef SourceLine, unsigned TabStop, |
198 SmallVectorImpl<int> &out) { | 218 SmallVectorImpl<int> &BytesOut, |
199 out.clear(); | 219 SmallVectorImpl<int> &ColumnsOut) { |
220 assert(BytesOut.empty()); | |
221 assert(ColumnsOut.empty()); | |
200 | 222 |
201 if (SourceLine.empty()) { | 223 if (SourceLine.empty()) { |
202 out.resize(1u,0); | 224 BytesOut.resize(1u, 0); |
225 ColumnsOut.resize(1u, 0); | |
203 return; | 226 return; |
204 } | 227 } |
205 | 228 |
206 out.resize(SourceLine.size()+1, -1); | 229 ColumnsOut.resize(SourceLine.size() + 1, -1); |
207 | 230 |
208 int columns = 0; | 231 int Columns = 0; |
209 size_t i = 0; | 232 size_t I = 0; |
210 while (i<SourceLine.size()) { | 233 while (I < SourceLine.size()) { |
211 out[i] = columns; | 234 ColumnsOut[I] = Columns; |
212 std::pair<SmallString<16>,bool> res | 235 BytesOut.resize(Columns + 1, -1); |
213 = printableTextForNextCharacter(SourceLine, &i, TabStop); | 236 BytesOut.back() = I; |
214 columns += llvm::sys::locale::columnWidth(res.first); | 237 auto [Str, Printable] = |
215 } | 238 printableTextForNextCharacter(SourceLine, &I, TabStop); |
216 out.back() = columns; | 239 Columns += llvm::sys::locale::columnWidth(Str); |
217 } | 240 } |
218 | 241 |
219 /// This function takes a raw source line and produces a mapping from columns | 242 ColumnsOut.back() = Columns; |
220 /// to the byte of the source line that produced the character displaying at | 243 BytesOut.resize(Columns + 1, -1); |
221 /// that column. This is the inverse of the mapping produced by byteToColumn() | 244 BytesOut.back() = I; |
222 /// | |
223 /// The last element in the array is the number of bytes in the source string | |
224 /// | |
225 /// example: (given a tabstop of 8) | |
226 /// | |
227 /// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} | |
228 /// | |
229 /// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to | |
230 /// display) | |
231 static void columnToByte(StringRef SourceLine, unsigned TabStop, | |
232 SmallVectorImpl<int> &out) { | |
233 out.clear(); | |
234 | |
235 if (SourceLine.empty()) { | |
236 out.resize(1u, 0); | |
237 return; | |
238 } | |
239 | |
240 int columns = 0; | |
241 size_t i = 0; | |
242 while (i<SourceLine.size()) { | |
243 out.resize(columns+1, -1); | |
244 out.back() = i; | |
245 std::pair<SmallString<16>,bool> res | |
246 = printableTextForNextCharacter(SourceLine, &i, TabStop); | |
247 columns += llvm::sys::locale::columnWidth(res.first); | |
248 } | |
249 out.resize(columns+1, -1); | |
250 out.back() = i; | |
251 } | 245 } |
252 | 246 |
253 namespace { | 247 namespace { |
254 struct SourceColumnMap { | 248 struct SourceColumnMap { |
255 SourceColumnMap(StringRef SourceLine, unsigned TabStop) | 249 SourceColumnMap(StringRef SourceLine, unsigned TabStop) |
256 : m_SourceLine(SourceLine) { | 250 : m_SourceLine(SourceLine) { |
257 | 251 |
258 ::byteToColumn(SourceLine, TabStop, m_byteToColumn); | 252 genColumnByteMapping(SourceLine, TabStop, m_columnToByte, m_byteToColumn); |
259 ::columnToByte(SourceLine, TabStop, m_columnToByte); | |
260 | 253 |
261 assert(m_byteToColumn.size()==SourceLine.size()+1); | 254 assert(m_byteToColumn.size()==SourceLine.size()+1); |
262 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size()); | 255 assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size()); |
263 assert(m_byteToColumn.size() | 256 assert(m_byteToColumn.size() |
264 == static_cast<unsigned>(m_columnToByte.back()+1)); | 257 == static_cast<unsigned>(m_columnToByte.back()+1)); |
468 | 461 |
469 CaretStart = map.byteToColumn(SourceStart); | 462 CaretStart = map.byteToColumn(SourceStart); |
470 CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource; | 463 CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource; |
471 | 464 |
472 // [CaretStart, CaretEnd) is the slice we want. Update the various | 465 // [CaretStart, CaretEnd) is the slice we want. Update the various |
473 // output lines to show only this slice, with two-space padding | 466 // output lines to show only this slice. |
474 // before the lines so that it looks nicer. | |
475 | |
476 assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 && | 467 assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 && |
477 SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1); | 468 SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1); |
478 assert(SourceStart <= SourceEnd); | 469 assert(SourceStart <= SourceEnd); |
479 assert(CaretStart <= CaretEnd); | 470 assert(CaretStart <= CaretEnd); |
480 | 471 |
602 /// \param Columns the number of columns to word-wrap to. | 593 /// \param Columns the number of columns to word-wrap to. |
603 /// \param Column the column number at which the first character of \p | 594 /// \param Column the column number at which the first character of \p |
604 /// Str will be printed. This will be non-zero when part of the first | 595 /// Str will be printed. This will be non-zero when part of the first |
605 /// line has already been printed. | 596 /// line has already been printed. |
606 /// \param Bold if the current text should be bold | 597 /// \param Bold if the current text should be bold |
607 /// \param Indentation the number of spaces to indent any lines beyond | |
608 /// the first line. | |
609 /// \returns true if word-wrapping was required, or false if the | 598 /// \returns true if word-wrapping was required, or false if the |
610 /// string fit on the first line. | 599 /// string fit on the first line. |
611 static bool printWordWrapped(raw_ostream &OS, StringRef Str, | 600 static bool printWordWrapped(raw_ostream &OS, StringRef Str, unsigned Columns, |
612 unsigned Columns, | 601 unsigned Column, bool Bold) { |
613 unsigned Column = 0, | |
614 bool Bold = false, | |
615 unsigned Indentation = WordWrapIndentation) { | |
616 const unsigned Length = std::min(Str.find('\n'), Str.size()); | 602 const unsigned Length = std::min(Str.find('\n'), Str.size()); |
617 bool TextNormal = true; | 603 bool TextNormal = true; |
618 | 604 |
619 // The string used to indent each line. | |
620 SmallString<16> IndentStr; | |
621 IndentStr.assign(Indentation, ' '); | |
622 bool Wrapped = false; | 605 bool Wrapped = false; |
623 for (unsigned WordStart = 0, WordEnd; WordStart < Length; | 606 for (unsigned WordStart = 0, WordEnd; WordStart < Length; |
624 WordStart = WordEnd) { | 607 WordStart = WordEnd) { |
625 // Find the beginning of the next word. | 608 // Find the beginning of the next word. |
626 WordStart = skipWhitespace(WordStart, Str, Length); | 609 WordStart = skipWhitespace(WordStart, Str, Length); |
645 } | 628 } |
646 | 629 |
647 // This word does not fit on the current line, so wrap to the next | 630 // This word does not fit on the current line, so wrap to the next |
648 // line. | 631 // line. |
649 OS << '\n'; | 632 OS << '\n'; |
650 OS.write(&IndentStr[0], Indentation); | 633 OS.indent(WordWrapIndentation); |
651 applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), | 634 applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), |
652 TextNormal, Bold); | 635 TextNormal, Bold); |
653 Column = Indentation + WordLength; | 636 Column = WordWrapIndentation + WordLength; |
654 Wrapped = true; | 637 Wrapped = true; |
655 } | 638 } |
656 | 639 |
657 // Append any remaning text from the message with its existing formatting. | 640 // Append any remaning text from the message with its existing formatting. |
658 applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold); | 641 applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold); |
784 OS << Filename; | 767 OS << Filename; |
785 } | 768 } |
786 | 769 |
787 /// Print out the file/line/column information and include trace. | 770 /// Print out the file/line/column information and include trace. |
788 /// | 771 /// |
789 /// This method handlen the emission of the diagnostic location information. | 772 /// This method handles the emission of the diagnostic location information. |
790 /// This includes extracting as much location information as is present for | 773 /// This includes extracting as much location information as is present for |
791 /// the diagnostic and printing it, as well as any include stack or source | 774 /// the diagnostic and printing it, as well as any include stack or source |
792 /// ranges necessary. | 775 /// ranges necessary. |
793 void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, | 776 void TextDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, |
794 DiagnosticsEngine::Level Level, | 777 DiagnosticsEngine::Level Level, |
795 ArrayRef<CharSourceRange> Ranges) { | 778 ArrayRef<CharSourceRange> Ranges) { |
796 if (PLoc.isInvalid()) { | 779 if (PLoc.isInvalid()) { |
797 // At least print the file name if available: | 780 // At least print the file name if available: |
798 FileID FID = Loc.getFileID(); | 781 if (FileID FID = Loc.getFileID(); FID.isValid()) { |
799 if (FID.isValid()) { | |
800 if (const FileEntry *FE = Loc.getFileEntry()) { | 782 if (const FileEntry *FE = Loc.getFileEntry()) { |
801 emitFilename(FE->getName(), Loc.getManager()); | 783 emitFilename(FE->getName(), Loc.getManager()); |
802 OS << ": "; | 784 OS << ": "; |
803 } | 785 } |
804 } | 786 } |
852 } | 834 } |
853 | 835 |
854 if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { | 836 if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { |
855 FileID CaretFileID = Loc.getExpansionLoc().getFileID(); | 837 FileID CaretFileID = Loc.getExpansionLoc().getFileID(); |
856 bool PrintedRange = false; | 838 bool PrintedRange = false; |
857 | 839 const SourceManager &SM = Loc.getManager(); |
858 for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), | 840 |
859 RE = Ranges.end(); | 841 for (const auto &R : Ranges) { |
860 RI != RE; ++RI) { | |
861 // Ignore invalid ranges. | 842 // Ignore invalid ranges. |
862 if (!RI->isValid()) continue; | 843 if (!R.isValid()) |
863 | 844 continue; |
864 auto &SM = Loc.getManager(); | 845 |
865 SourceLocation B = SM.getExpansionLoc(RI->getBegin()); | 846 SourceLocation B = SM.getExpansionLoc(R.getBegin()); |
866 CharSourceRange ERange = SM.getExpansionRange(RI->getEnd()); | 847 CharSourceRange ERange = SM.getExpansionRange(R.getEnd()); |
867 SourceLocation E = ERange.getEnd(); | 848 SourceLocation E = ERange.getEnd(); |
868 bool IsTokenRange = ERange.isTokenRange(); | 849 |
869 | 850 // If the start or end of the range is in another file, just |
870 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); | 851 // discard it. |
871 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); | 852 if (SM.getFileID(B) != CaretFileID || SM.getFileID(E) != CaretFileID) |
872 | |
873 // If the start or end of the range is in another file, just discard | |
874 // it. | |
875 if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) | |
876 continue; | 853 continue; |
877 | 854 |
878 // Add in the length of the token, so that we cover multi-char | 855 // Add in the length of the token, so that we cover multi-char |
879 // tokens. | 856 // tokens. |
880 unsigned TokSize = 0; | 857 unsigned TokSize = 0; |
881 if (IsTokenRange) | 858 if (ERange.isTokenRange()) |
882 TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); | 859 TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); |
883 | 860 |
884 FullSourceLoc BF(B, SM), EF(E, SM); | 861 FullSourceLoc BF(B, SM), EF(E, SM); |
885 OS << '{' | 862 OS << '{' |
886 << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-' | 863 << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-' |
894 } | 871 } |
895 OS << ' '; | 872 OS << ' '; |
896 } | 873 } |
897 | 874 |
898 void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { | 875 void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { |
899 if (DiagOpts->ShowLocation && PLoc.isValid()) | 876 if (DiagOpts->ShowLocation && PLoc.isValid()) { |
900 OS << "In file included from " << PLoc.getFilename() << ':' | 877 OS << "In file included from "; |
901 << PLoc.getLine() << ":\n"; | 878 emitFilename(PLoc.getFilename(), Loc.getManager()); |
902 else | 879 OS << ':' << PLoc.getLine() << ":\n"; |
880 } else | |
903 OS << "In included file:\n"; | 881 OS << "In included file:\n"; |
904 } | 882 } |
905 | 883 |
906 void TextDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, | 884 void TextDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, |
907 StringRef ModuleName) { | 885 StringRef ModuleName) { |
921 else | 899 else |
922 OS << "While building module '" << ModuleName << "':\n"; | 900 OS << "While building module '" << ModuleName << "':\n"; |
923 } | 901 } |
924 | 902 |
925 /// Find the suitable set of lines to show to include a set of ranges. | 903 /// Find the suitable set of lines to show to include a set of ranges. |
926 static llvm::Optional<std::pair<unsigned, unsigned>> | 904 static std::optional<std::pair<unsigned, unsigned>> |
927 findLinesForRange(const CharSourceRange &R, FileID FID, | 905 findLinesForRange(const CharSourceRange &R, FileID FID, |
928 const SourceManager &SM) { | 906 const SourceManager &SM) { |
929 if (!R.isValid()) return None; | 907 if (!R.isValid()) |
908 return std::nullopt; | |
930 | 909 |
931 SourceLocation Begin = R.getBegin(); | 910 SourceLocation Begin = R.getBegin(); |
932 SourceLocation End = R.getEnd(); | 911 SourceLocation End = R.getEnd(); |
933 if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID) | 912 if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID) |
934 return None; | 913 return std::nullopt; |
935 | 914 |
936 return std::make_pair(SM.getExpansionLineNumber(Begin), | 915 return std::make_pair(SM.getExpansionLineNumber(Begin), |
937 SM.getExpansionLineNumber(End)); | 916 SM.getExpansionLineNumber(End)); |
938 } | 917 } |
939 | 918 |
970 A.first = std::max(Min + Slack, A.first) - Slack; | 949 A.first = std::max(Min + Slack, A.first) - Slack; |
971 A.second = std::min(A.first + MaxRange - 1, Max); | 950 A.second = std::min(A.first + MaxRange - 1, Max); |
972 return A; | 951 return A; |
973 } | 952 } |
974 | 953 |
975 /// Highlight a SourceRange (with ~'s) for any characters on LineNo. | 954 struct LineRange { |
976 static void highlightRange(const CharSourceRange &R, | 955 unsigned LineNo; |
977 unsigned LineNo, FileID FID, | 956 unsigned StartCol; |
978 const SourceColumnMap &map, | 957 unsigned EndCol; |
979 std::string &CaretLine, | 958 }; |
980 const SourceManager &SM, | 959 |
981 const LangOptions &LangOpts) { | 960 /// Highlight \p R (with ~'s) on the current source line. |
982 if (!R.isValid()) return; | 961 static void highlightRange(const LineRange &R, const SourceColumnMap &Map, |
983 | 962 std::string &CaretLine) { |
984 SourceLocation Begin = R.getBegin(); | 963 // Pick the first non-whitespace column. |
985 SourceLocation End = R.getEnd(); | 964 unsigned StartColNo = R.StartCol; |
986 | 965 while (StartColNo < Map.getSourceLine().size() && |
987 unsigned StartLineNo = SM.getExpansionLineNumber(Begin); | 966 (Map.getSourceLine()[StartColNo] == ' ' || |
988 if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) | 967 Map.getSourceLine()[StartColNo] == '\t')) |
989 return; // No intersection. | 968 StartColNo = Map.startOfNextColumn(StartColNo); |
990 | 969 |
991 unsigned EndLineNo = SM.getExpansionLineNumber(End); | 970 // Pick the last non-whitespace column. |
992 if (EndLineNo < LineNo || SM.getFileID(End) != FID) | 971 unsigned EndColNo = |
993 return; // No intersection. | 972 std::min(static_cast<size_t>(R.EndCol), Map.getSourceLine().size()); |
994 | 973 while (EndColNo && (Map.getSourceLine()[EndColNo - 1] == ' ' || |
995 // Compute the column number of the start. | 974 Map.getSourceLine()[EndColNo - 1] == '\t')) |
996 unsigned StartColNo = 0; | 975 EndColNo = Map.startOfPreviousColumn(EndColNo); |
997 if (StartLineNo == LineNo) { | 976 |
998 StartColNo = SM.getExpansionColumnNumber(Begin); | 977 // If the start/end passed each other, then we are trying to highlight a |
999 if (StartColNo) --StartColNo; // Zero base the col #. | 978 // range that just exists in whitespace. That most likely means we have |
1000 } | 979 // a multi-line highlighting range that covers a blank line. |
1001 | 980 if (StartColNo > EndColNo) |
1002 // Compute the column number of the end. | 981 return; |
1003 unsigned EndColNo = map.getSourceLine().size(); | |
1004 if (EndLineNo == LineNo) { | |
1005 EndColNo = SM.getExpansionColumnNumber(End); | |
1006 if (EndColNo) { | |
1007 --EndColNo; // Zero base the col #. | |
1008 | |
1009 // Add in the length of the token, so that we cover multi-char tokens if | |
1010 // this is a token range. | |
1011 if (R.isTokenRange()) | |
1012 EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); | |
1013 } else { | |
1014 EndColNo = CaretLine.size(); | |
1015 } | |
1016 } | |
1017 | |
1018 assert(StartColNo <= EndColNo && "Invalid range!"); | |
1019 | |
1020 // Check that a token range does not highlight only whitespace. | |
1021 if (R.isTokenRange()) { | |
1022 // Pick the first non-whitespace column. | |
1023 while (StartColNo < map.getSourceLine().size() && | |
1024 (map.getSourceLine()[StartColNo] == ' ' || | |
1025 map.getSourceLine()[StartColNo] == '\t')) | |
1026 StartColNo = map.startOfNextColumn(StartColNo); | |
1027 | |
1028 // Pick the last non-whitespace column. | |
1029 if (EndColNo > map.getSourceLine().size()) | |
1030 EndColNo = map.getSourceLine().size(); | |
1031 while (EndColNo && | |
1032 (map.getSourceLine()[EndColNo-1] == ' ' || | |
1033 map.getSourceLine()[EndColNo-1] == '\t')) | |
1034 EndColNo = map.startOfPreviousColumn(EndColNo); | |
1035 | |
1036 // If the start/end passed each other, then we are trying to highlight a | |
1037 // range that just exists in whitespace. That most likely means we have | |
1038 // a multi-line highlighting range that covers a blank line. | |
1039 if (StartColNo > EndColNo) { | |
1040 assert(StartLineNo != EndLineNo && "trying to highlight whitespace"); | |
1041 StartColNo = EndColNo; | |
1042 } | |
1043 } | |
1044 | |
1045 assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); | |
1046 assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); | |
1047 | 982 |
1048 // Fill the range with ~'s. | 983 // Fill the range with ~'s. |
1049 StartColNo = map.byteToContainingColumn(StartColNo); | 984 StartColNo = Map.byteToContainingColumn(StartColNo); |
1050 EndColNo = map.byteToContainingColumn(EndColNo); | 985 EndColNo = Map.byteToContainingColumn(EndColNo); |
1051 | 986 |
1052 assert(StartColNo <= EndColNo && "Invalid range!"); | 987 assert(StartColNo <= EndColNo && "Invalid range!"); |
1053 if (CaretLine.size() < EndColNo) | 988 if (CaretLine.size() < EndColNo) |
1054 CaretLine.resize(EndColNo,' '); | 989 CaretLine.resize(EndColNo, ' '); |
1055 std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); | 990 std::fill(CaretLine.begin() + StartColNo, CaretLine.begin() + EndColNo, '~'); |
1056 } | 991 } |
1057 | 992 |
1058 static std::string buildFixItInsertionLine(FileID FID, | 993 static std::string buildFixItInsertionLine(FileID FID, |
1059 unsigned LineNo, | 994 unsigned LineNo, |
1060 const SourceColumnMap &map, | 995 const SourceColumnMap &map, |
1064 std::string FixItInsertionLine; | 999 std::string FixItInsertionLine; |
1065 if (Hints.empty() || !DiagOpts->ShowFixits) | 1000 if (Hints.empty() || !DiagOpts->ShowFixits) |
1066 return FixItInsertionLine; | 1001 return FixItInsertionLine; |
1067 unsigned PrevHintEndCol = 0; | 1002 unsigned PrevHintEndCol = 0; |
1068 | 1003 |
1069 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); | 1004 for (const auto &H : Hints) { |
1070 I != E; ++I) { | 1005 if (H.CodeToInsert.empty()) |
1071 if (!I->CodeToInsert.empty()) { | 1006 continue; |
1072 // We have an insertion hint. Determine whether the inserted | 1007 |
1073 // code contains no newlines and is on the same line as the caret. | 1008 // We have an insertion hint. Determine whether the inserted |
1074 std::pair<FileID, unsigned> HintLocInfo | 1009 // code contains no newlines and is on the same line as the caret. |
1075 = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); | 1010 std::pair<FileID, unsigned> HintLocInfo = |
1076 if (FID == HintLocInfo.first && | 1011 SM.getDecomposedExpansionLoc(H.RemoveRange.getBegin()); |
1077 LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) && | 1012 if (FID == HintLocInfo.first && |
1078 StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) { | 1013 LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) && |
1079 // Insert the new code into the line just below the code | 1014 StringRef(H.CodeToInsert).find_first_of("\n\r") == StringRef::npos) { |
1080 // that the user wrote. | 1015 // Insert the new code into the line just below the code |
1081 // Note: When modifying this function, be very careful about what is a | 1016 // that the user wrote. |
1082 // "column" (printed width, platform-dependent) and what is a | 1017 // Note: When modifying this function, be very careful about what is a |
1083 // "byte offset" (SourceManager "column"). | 1018 // "column" (printed width, platform-dependent) and what is a |
1084 unsigned HintByteOffset | 1019 // "byte offset" (SourceManager "column"). |
1085 = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; | 1020 unsigned HintByteOffset = |
1086 | 1021 SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; |
1087 // The hint must start inside the source or right at the end | 1022 |
1088 assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); | 1023 // The hint must start inside the source or right at the end |
1089 unsigned HintCol = map.byteToContainingColumn(HintByteOffset); | 1024 assert(HintByteOffset < static_cast<unsigned>(map.bytes()) + 1); |
1090 | 1025 unsigned HintCol = map.byteToContainingColumn(HintByteOffset); |
1091 // If we inserted a long previous hint, push this one forwards, and add | 1026 |
1092 // an extra space to show that this is not part of the previous | 1027 // If we inserted a long previous hint, push this one forwards, and add |
1093 // completion. This is sort of the best we can do when two hints appear | 1028 // an extra space to show that this is not part of the previous |
1094 // to overlap. | 1029 // completion. This is sort of the best we can do when two hints appear |
1095 // | 1030 // to overlap. |
1096 // Note that if this hint is located immediately after the previous | 1031 // |
1097 // hint, no space will be added, since the location is more important. | 1032 // Note that if this hint is located immediately after the previous |
1098 if (HintCol < PrevHintEndCol) | 1033 // hint, no space will be added, since the location is more important. |
1099 HintCol = PrevHintEndCol + 1; | 1034 if (HintCol < PrevHintEndCol) |
1100 | 1035 HintCol = PrevHintEndCol + 1; |
1101 // This should NOT use HintByteOffset, because the source might have | 1036 |
1102 // Unicode characters in earlier columns. | 1037 // This should NOT use HintByteOffset, because the source might have |
1103 unsigned NewFixItLineSize = FixItInsertionLine.size() + | 1038 // Unicode characters in earlier columns. |
1104 (HintCol - PrevHintEndCol) + I->CodeToInsert.size(); | 1039 unsigned NewFixItLineSize = FixItInsertionLine.size() + |
1105 if (NewFixItLineSize > FixItInsertionLine.size()) | 1040 (HintCol - PrevHintEndCol) + |
1106 FixItInsertionLine.resize(NewFixItLineSize, ' '); | 1041 H.CodeToInsert.size(); |
1107 | 1042 if (NewFixItLineSize > FixItInsertionLine.size()) |
1108 std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), | 1043 FixItInsertionLine.resize(NewFixItLineSize, ' '); |
1109 FixItInsertionLine.end() - I->CodeToInsert.size()); | 1044 |
1110 | 1045 std::copy(H.CodeToInsert.begin(), H.CodeToInsert.end(), |
1111 PrevHintEndCol = | 1046 FixItInsertionLine.end() - H.CodeToInsert.size()); |
1112 HintCol + llvm::sys::locale::columnWidth(I->CodeToInsert); | 1047 |
1113 } | 1048 PrevHintEndCol = HintCol + llvm::sys::locale::columnWidth(H.CodeToInsert); |
1114 } | 1049 } |
1115 } | 1050 } |
1116 | 1051 |
1117 expandTabs(FixItInsertionLine, DiagOpts->TabStop); | 1052 expandTabs(FixItInsertionLine, DiagOpts->TabStop); |
1118 | 1053 |
1119 return FixItInsertionLine; | 1054 return FixItInsertionLine; |
1055 } | |
1056 | |
1057 static unsigned getNumDisplayWidth(unsigned N) { | |
1058 unsigned L = 1u, M = 10u; | |
1059 while (M <= N && ++L != std::numeric_limits<unsigned>::digits10 + 1) | |
1060 M *= 10u; | |
1061 | |
1062 return L; | |
1063 } | |
1064 | |
1065 /// Filter out invalid ranges, ranges that don't fit into the window of | |
1066 /// source lines we will print, and ranges from other files. | |
1067 /// | |
1068 /// For the remaining ranges, convert them to simple LineRange structs, | |
1069 /// which only cover one line at a time. | |
1070 static SmallVector<LineRange> | |
1071 prepareAndFilterRanges(const SmallVectorImpl<CharSourceRange> &Ranges, | |
1072 const SourceManager &SM, | |
1073 const std::pair<unsigned, unsigned> &Lines, FileID FID, | |
1074 const LangOptions &LangOpts) { | |
1075 SmallVector<LineRange> LineRanges; | |
1076 | |
1077 for (const CharSourceRange &R : Ranges) { | |
1078 if (R.isInvalid()) | |
1079 continue; | |
1080 SourceLocation Begin = R.getBegin(); | |
1081 SourceLocation End = R.getEnd(); | |
1082 | |
1083 unsigned StartLineNo = SM.getExpansionLineNumber(Begin); | |
1084 if (StartLineNo > Lines.second || SM.getFileID(Begin) != FID) | |
1085 continue; | |
1086 | |
1087 unsigned EndLineNo = SM.getExpansionLineNumber(End); | |
1088 if (EndLineNo < Lines.first || SM.getFileID(End) != FID) | |
1089 continue; | |
1090 | |
1091 unsigned StartColumn = SM.getExpansionColumnNumber(Begin); | |
1092 unsigned EndColumn = SM.getExpansionColumnNumber(End); | |
1093 if (R.isTokenRange()) | |
1094 EndColumn += Lexer::MeasureTokenLength(End, SM, LangOpts); | |
1095 | |
1096 // Only a single line. | |
1097 if (StartLineNo == EndLineNo) { | |
1098 LineRanges.push_back({StartLineNo, StartColumn - 1, EndColumn - 1}); | |
1099 continue; | |
1100 } | |
1101 | |
1102 // Start line. | |
1103 LineRanges.push_back({StartLineNo, StartColumn - 1, ~0u}); | |
1104 | |
1105 // Middle lines. | |
1106 for (unsigned S = StartLineNo + 1; S != EndLineNo; ++S) | |
1107 LineRanges.push_back({S, 0, ~0u}); | |
1108 | |
1109 // End line. | |
1110 LineRanges.push_back({EndLineNo, 0, EndColumn - 1}); | |
1111 } | |
1112 | |
1113 return LineRanges; | |
1120 } | 1114 } |
1121 | 1115 |
1122 /// Emit a code snippet and caret line. | 1116 /// Emit a code snippet and caret line. |
1123 /// | 1117 /// |
1124 /// This routine emits a single line's code snippet and caret line.. | 1118 /// This routine emits a single line's code snippet and caret line.. |
1142 return; | 1136 return; |
1143 if (Loc == LastLoc && Ranges.empty() && Hints.empty() && | 1137 if (Loc == LastLoc && Ranges.empty() && Hints.empty() && |
1144 (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) | 1138 (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) |
1145 return; | 1139 return; |
1146 | 1140 |
1147 // Decompose the location into a FID/Offset pair. | 1141 FileID FID = Loc.getFileID(); |
1148 std::pair<FileID, unsigned> LocInfo = Loc.getDecomposedLoc(); | |
1149 FileID FID = LocInfo.first; | |
1150 const SourceManager &SM = Loc.getManager(); | 1142 const SourceManager &SM = Loc.getManager(); |
1151 | 1143 |
1152 // Get information about the buffer it points into. | 1144 // Get information about the buffer it points into. |
1153 bool Invalid = false; | 1145 bool Invalid = false; |
1154 StringRef BufData = Loc.getBufferData(&Invalid); | 1146 StringRef BufData = Loc.getBufferData(&Invalid); |
1155 if (Invalid) | 1147 if (Invalid) |
1156 return; | 1148 return; |
1149 const char *BufStart = BufData.data(); | |
1150 const char *BufEnd = BufStart + BufData.size(); | |
1157 | 1151 |
1158 unsigned CaretLineNo = Loc.getLineNumber(); | 1152 unsigned CaretLineNo = Loc.getLineNumber(); |
1159 unsigned CaretColNo = Loc.getColumnNumber(); | 1153 unsigned CaretColNo = Loc.getColumnNumber(); |
1160 | 1154 |
1161 // Arbitrarily stop showing snippets when the line is too long. | 1155 // Arbitrarily stop showing snippets when the line is too long. |
1164 return; | 1158 return; |
1165 | 1159 |
1166 // Find the set of lines to include. | 1160 // Find the set of lines to include. |
1167 const unsigned MaxLines = DiagOpts->SnippetLineLimit; | 1161 const unsigned MaxLines = DiagOpts->SnippetLineLimit; |
1168 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo}; | 1162 std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo}; |
1169 for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), | 1163 unsigned DisplayLineNo = |
1170 E = Ranges.end(); | 1164 Ranges.empty() ? Loc.getPresumedLoc().getLine() : ~0u; |
1171 I != E; ++I) | 1165 for (const auto &I : Ranges) { |
1172 if (auto OptionalRange = findLinesForRange(*I, FID, SM)) | 1166 if (auto OptionalRange = findLinesForRange(I, FID, SM)) |
1173 Lines = maybeAddRange(Lines, *OptionalRange, MaxLines); | 1167 Lines = maybeAddRange(Lines, *OptionalRange, MaxLines); |
1174 | 1168 |
1175 for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1; ++LineNo) { | 1169 DisplayLineNo = |
1176 const char *BufStart = BufData.data(); | 1170 std::min(DisplayLineNo, SM.getPresumedLineNumber(I.getBegin())); |
1177 const char *BufEnd = BufStart + BufData.size(); | 1171 } |
1178 | 1172 |
1173 // Our line numbers look like: | |
1174 // " [number] | " | |
1175 // Where [number] is MaxLineNoDisplayWidth columns | |
1176 // and the full thing is therefore MaxLineNoDisplayWidth + 4 columns. | |
1177 unsigned MaxLineNoDisplayWidth = | |
1178 DiagOpts->ShowLineNumbers | |
1179 ? std::max(4u, getNumDisplayWidth(DisplayLineNo + MaxLines)) | |
1180 : 0; | |
1181 auto indentForLineNumbers = [&] { | |
1182 if (MaxLineNoDisplayWidth > 0) | |
1183 OS.indent(MaxLineNoDisplayWidth + 2) << "| "; | |
1184 }; | |
1185 | |
1186 SmallVector<LineRange> LineRanges = | |
1187 prepareAndFilterRanges(Ranges, SM, Lines, FID, LangOpts); | |
1188 | |
1189 for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1; | |
1190 ++LineNo, ++DisplayLineNo) { | |
1179 // Rewind from the current position to the start of the line. | 1191 // Rewind from the current position to the start of the line. |
1180 const char *LineStart = | 1192 const char *LineStart = |
1181 BufStart + | 1193 BufStart + |
1182 SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second; | 1194 SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second; |
1183 if (LineStart == BufEnd) | 1195 if (LineStart == BufEnd) |
1191 // Arbitrarily stop showing snippets when the line is too long. | 1203 // Arbitrarily stop showing snippets when the line is too long. |
1192 // FIXME: Don't print any lines in this case. | 1204 // FIXME: Don't print any lines in this case. |
1193 if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) | 1205 if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) |
1194 return; | 1206 return; |
1195 | 1207 |
1196 // Trim trailing null-bytes. | |
1197 StringRef Line(LineStart, LineEnd - LineStart); | |
1198 while (!Line.empty() && Line.back() == '\0' && | |
1199 (LineNo != CaretLineNo || Line.size() > CaretColNo)) | |
1200 Line = Line.drop_back(); | |
1201 | |
1202 // Copy the line of code into an std::string for ease of manipulation. | 1208 // Copy the line of code into an std::string for ease of manipulation. |
1203 std::string SourceLine(Line.begin(), Line.end()); | 1209 std::string SourceLine(LineStart, LineEnd); |
1210 // Remove trailing null bytes. | |
1211 while (!SourceLine.empty() && SourceLine.back() == '\0' && | |
1212 (LineNo != CaretLineNo || SourceLine.size() > CaretColNo)) | |
1213 SourceLine.pop_back(); | |
1204 | 1214 |
1205 // Build the byte to column map. | 1215 // Build the byte to column map. |
1206 const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); | 1216 const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); |
1207 | 1217 |
1208 // Create a line for the caret that is filled with spaces that is the same | 1218 std::string CaretLine; |
1209 // number of columns as the line of source code. | |
1210 std::string CaretLine(sourceColMap.columns(), ' '); | |
1211 | |
1212 // Highlight all of the characters covered by Ranges with ~ characters. | 1219 // Highlight all of the characters covered by Ranges with ~ characters. |
1213 for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), | 1220 for (const auto &LR : LineRanges) { |
1214 E = Ranges.end(); | 1221 if (LR.LineNo == LineNo) |
1215 I != E; ++I) | 1222 highlightRange(LR, sourceColMap, CaretLine); |
1216 highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts); | 1223 } |
1217 | 1224 |
1218 // Next, insert the caret itself. | 1225 // Next, insert the caret itself. |
1219 if (CaretLineNo == LineNo) { | 1226 if (CaretLineNo == LineNo) { |
1220 CaretColNo = sourceColMap.byteToContainingColumn(CaretColNo - 1); | 1227 size_t Col = sourceColMap.byteToContainingColumn(CaretColNo - 1); |
1221 if (CaretLine.size() < CaretColNo + 1) | 1228 CaretLine.resize(std::max(Col + 1, CaretLine.size()), ' '); |
1222 CaretLine.resize(CaretColNo + 1, ' '); | 1229 CaretLine[Col] = '^'; |
1223 CaretLine[CaretColNo] = '^'; | |
1224 } | 1230 } |
1225 | 1231 |
1226 std::string FixItInsertionLine = buildFixItInsertionLine( | 1232 std::string FixItInsertionLine = buildFixItInsertionLine( |
1227 FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get()); | 1233 FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get()); |
1228 | 1234 |
1235 | 1241 |
1236 // If we are in -fdiagnostics-print-source-range-info mode, we are trying | 1242 // If we are in -fdiagnostics-print-source-range-info mode, we are trying |
1237 // to produce easily machine parsable output. Add a space before the | 1243 // to produce easily machine parsable output. Add a space before the |
1238 // source line and the caret to make it trivial to tell the main diagnostic | 1244 // source line and the caret to make it trivial to tell the main diagnostic |
1239 // line from what the user is intended to see. | 1245 // line from what the user is intended to see. |
1240 if (DiagOpts->ShowSourceRanges) { | 1246 if (DiagOpts->ShowSourceRanges && !SourceLine.empty()) { |
1241 SourceLine = ' ' + SourceLine; | 1247 SourceLine = ' ' + SourceLine; |
1242 CaretLine = ' ' + CaretLine; | 1248 CaretLine = ' ' + CaretLine; |
1243 } | 1249 } |
1244 | 1250 |
1245 // Finally, remove any blank spaces from the end of CaretLine. | |
1246 while (!CaretLine.empty() && CaretLine[CaretLine.size() - 1] == ' ') | |
1247 CaretLine.erase(CaretLine.end() - 1); | |
1248 | |
1249 // Emit what we have computed. | 1251 // Emit what we have computed. |
1250 emitSnippet(SourceLine); | 1252 emitSnippet(SourceLine, MaxLineNoDisplayWidth, DisplayLineNo); |
1251 | 1253 |
1252 if (!CaretLine.empty()) { | 1254 if (!CaretLine.empty()) { |
1255 indentForLineNumbers(); | |
1253 if (DiagOpts->ShowColors) | 1256 if (DiagOpts->ShowColors) |
1254 OS.changeColor(caretColor, true); | 1257 OS.changeColor(caretColor, true); |
1255 OS << CaretLine << '\n'; | 1258 OS << CaretLine << '\n'; |
1256 if (DiagOpts->ShowColors) | 1259 if (DiagOpts->ShowColors) |
1257 OS.resetColor(); | 1260 OS.resetColor(); |
1258 } | 1261 } |
1259 | 1262 |
1260 if (!FixItInsertionLine.empty()) { | 1263 if (!FixItInsertionLine.empty()) { |
1264 indentForLineNumbers(); | |
1261 if (DiagOpts->ShowColors) | 1265 if (DiagOpts->ShowColors) |
1262 // Print fixit line in color | 1266 // Print fixit line in color |
1263 OS.changeColor(fixitColor, false); | 1267 OS.changeColor(fixitColor, false); |
1264 if (DiagOpts->ShowSourceRanges) | 1268 if (DiagOpts->ShowSourceRanges) |
1265 OS << ' '; | 1269 OS << ' '; |
1271 | 1275 |
1272 // Print out any parseable fixit information requested by the options. | 1276 // Print out any parseable fixit information requested by the options. |
1273 emitParseableFixits(Hints, SM); | 1277 emitParseableFixits(Hints, SM); |
1274 } | 1278 } |
1275 | 1279 |
1276 void TextDiagnostic::emitSnippet(StringRef line) { | 1280 void TextDiagnostic::emitSnippet(StringRef SourceLine, |
1277 if (line.empty()) | 1281 unsigned MaxLineNoDisplayWidth, |
1278 return; | 1282 unsigned LineNo) { |
1279 | 1283 // Emit line number. |
1280 size_t i = 0; | 1284 if (MaxLineNoDisplayWidth > 0) { |
1281 | 1285 unsigned LineNoDisplayWidth = getNumDisplayWidth(LineNo); |
1282 std::string to_print; | 1286 OS.indent(MaxLineNoDisplayWidth - LineNoDisplayWidth + 1) |
1283 bool print_reversed = false; | 1287 << LineNo << " | "; |
1284 | 1288 } |
1285 while (i<line.size()) { | 1289 |
1286 std::pair<SmallString<16>,bool> res | 1290 // Print the source line one character at a time. |
1287 = printableTextForNextCharacter(line, &i, DiagOpts->TabStop); | 1291 bool PrintReversed = false; |
1288 bool was_printable = res.second; | 1292 size_t I = 0; |
1289 | 1293 while (I < SourceLine.size()) { |
1290 if (DiagOpts->ShowColors && was_printable == print_reversed) { | 1294 auto [Str, WasPrintable] = |
1291 if (print_reversed) | 1295 printableTextForNextCharacter(SourceLine, &I, DiagOpts->TabStop); |
1292 OS.reverseColor(); | 1296 |
1293 OS << to_print; | 1297 // Toggle inverted colors on or off for this character. |
1294 to_print.clear(); | 1298 if (DiagOpts->ShowColors) { |
1295 if (DiagOpts->ShowColors) | 1299 if (WasPrintable == PrintReversed) { |
1296 OS.resetColor(); | 1300 PrintReversed = !PrintReversed; |
1297 } | 1301 if (PrintReversed) |
1298 | 1302 OS.reverseColor(); |
1299 print_reversed = !was_printable; | 1303 else |
1300 to_print += res.first.str(); | 1304 OS.resetColor(); |
1301 } | 1305 } |
1302 | 1306 } |
1303 if (print_reversed && DiagOpts->ShowColors) | 1307 OS << Str; |
1304 OS.reverseColor(); | 1308 } |
1305 OS << to_print; | 1309 |
1306 if (print_reversed && DiagOpts->ShowColors) | 1310 if (DiagOpts->ShowColors) |
1307 OS.resetColor(); | 1311 OS.resetColor(); |
1308 | 1312 |
1309 OS << '\n'; | 1313 OS << '\n'; |
1310 } | 1314 } |
1311 | 1315 |
1314 if (!DiagOpts->ShowParseableFixits) | 1318 if (!DiagOpts->ShowParseableFixits) |
1315 return; | 1319 return; |
1316 | 1320 |
1317 // We follow FixItRewriter's example in not (yet) handling | 1321 // We follow FixItRewriter's example in not (yet) handling |
1318 // fix-its in macros. | 1322 // fix-its in macros. |
1319 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); | 1323 for (const auto &H : Hints) { |
1320 I != E; ++I) { | 1324 if (H.RemoveRange.isInvalid() || H.RemoveRange.getBegin().isMacroID() || |
1321 if (I->RemoveRange.isInvalid() || | 1325 H.RemoveRange.getEnd().isMacroID()) |
1322 I->RemoveRange.getBegin().isMacroID() || | |
1323 I->RemoveRange.getEnd().isMacroID()) | |
1324 return; | 1326 return; |
1325 } | 1327 } |
1326 | 1328 |
1327 for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); | 1329 for (const auto &H : Hints) { |
1328 I != E; ++I) { | 1330 SourceLocation BLoc = H.RemoveRange.getBegin(); |
1329 SourceLocation BLoc = I->RemoveRange.getBegin(); | 1331 SourceLocation ELoc = H.RemoveRange.getEnd(); |
1330 SourceLocation ELoc = I->RemoveRange.getEnd(); | |
1331 | 1332 |
1332 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); | 1333 std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); |
1333 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); | 1334 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); |
1334 | 1335 |
1335 // Adjust for token ranges. | 1336 // Adjust for token ranges. |
1336 if (I->RemoveRange.isTokenRange()) | 1337 if (H.RemoveRange.isTokenRange()) |
1337 EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); | 1338 EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); |
1338 | 1339 |
1339 // We specifically do not do word-wrapping or tab-expansion here, | 1340 // We specifically do not do word-wrapping or tab-expansion here, |
1340 // because this is supposed to be easy to parse. | 1341 // because this is supposed to be easy to parse. |
1341 PresumedLoc PLoc = SM.getPresumedLoc(BLoc); | 1342 PresumedLoc PLoc = SM.getPresumedLoc(BLoc); |
1347 OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) | 1348 OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) |
1348 << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) | 1349 << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) |
1349 << '-' << SM.getLineNumber(EInfo.first, EInfo.second) | 1350 << '-' << SM.getLineNumber(EInfo.first, EInfo.second) |
1350 << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) | 1351 << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) |
1351 << "}:\""; | 1352 << "}:\""; |
1352 OS.write_escaped(I->CodeToInsert); | 1353 OS.write_escaped(H.CodeToInsert); |
1353 OS << "\"\n"; | 1354 OS << "\"\n"; |
1354 } | 1355 } |
1355 } | 1356 } |