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 }