 |
|
 |
|
| Files: |
1 |
|
Branches Taken: |
72.8% |
305 / 419 |
| Generated: |
2010-02-10 01:31 |
|
Branches Executed: |
89.5% |
375 / 419 |
| |
|
Line Coverage: |
83.9% |
323 / 385 |
| |
 |
|
 |
1 : //===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 : //
10 : // This diagnostic client prints out their diagnostic messages.
11 : //
12 : //===----------------------------------------------------------------------===//
13 :
14 : #include "clang/Frontend/TextDiagnosticPrinter.h"
15 : #include "clang/Basic/SourceManager.h"
16 : #include "clang/Frontend/DiagnosticOptions.h"
17 : #include "clang/Lex/Lexer.h"
18 : #include "llvm/Support/MemoryBuffer.h"
19 : #include "llvm/Support/raw_ostream.h"
20 : #include "llvm/ADT/SmallString.h"
21 : #include <algorithm>
22 : using namespace clang;
23 :
24 : static const enum llvm::raw_ostream::Colors noteColor =
25 : llvm::raw_ostream::BLACK;
26 : static const enum llvm::raw_ostream::Colors fixitColor =
27 : llvm::raw_ostream::GREEN;
28 : static const enum llvm::raw_ostream::Colors caretColor =
29 : llvm::raw_ostream::GREEN;
30 : static const enum llvm::raw_ostream::Colors warningColor =
31 : llvm::raw_ostream::MAGENTA;
32 : static const enum llvm::raw_ostream::Colors errorColor = llvm::raw_ostream::RED;
33 : static const enum llvm::raw_ostream::Colors fatalColor = llvm::raw_ostream::RED;
34 : // used for changing only the bold attribute
35 : static const enum llvm::raw_ostream::Colors savedColor =
36 : llvm::raw_ostream::SAVEDCOLOR;
37 :
38 : /// \brief Number of spaces to indent when word-wrapping.
39 : const unsigned WordWrapIndentation = 6;
40 :
41 : TextDiagnosticPrinter::TextDiagnosticPrinter(llvm::raw_ostream &os,
42 : const DiagnosticOptions &diags,
43 2531: bool _OwnsOutputStream)
44 : : OS(os), LangOpts(0), DiagOpts(&diags),
45 : LastCaretDiagnosticWasNote(0),
46 2531: OwnsOutputStream(_OwnsOutputStream) {
47 2531: }
48 :
49 2462: TextDiagnosticPrinter::~TextDiagnosticPrinter() {
0: branch 0 not taken
2462: branch 1 taken
2462: branch 2 taken
2462: branch 3 taken
2462: branch 4 taken
2462: branch 5 taken
50 2462: if (OwnsOutputStream)
0: branch 0 not taken
0: branch 1 not taken
0: branch 3 not taken
0: branch 4 not taken
0: branch 6 not taken
0: branch 7 not taken
51 0: delete &OS;
2462: branch 1 taken
0: branch 2 not taken
0: branch 5 not taken
0: branch 6 not taken
0: branch 9 not taken
0: branch 10 not taken
52 2462: }
53 :
54 : void TextDiagnosticPrinter::
55 47: PrintIncludeStack(SourceLocation Loc, const SourceManager &SM) {
25: branch 1 taken
22: branch 2 taken
56 47: if (Loc.isInvalid()) return;
57 :
58 25: PresumedLoc PLoc = SM.getPresumedLoc(Loc);
59 :
60 : // Print out the other include frames first.
61 25: PrintIncludeStack(PLoc.getIncludeLoc(), SM);
62 :
25: branch 0 taken
0: branch 1 not taken
63 25: if (DiagOpts->ShowLocation)
64 : OS << "In file included from " << PLoc.getFilename()
65 25: << ':' << PLoc.getLine() << ":\n";
66 : else
67 0: OS << "In included file:\n";
68 : }
69 :
70 : /// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s)
71 : /// any characters in LineNo that intersect the SourceRange.
72 : void TextDiagnosticPrinter::HighlightRange(const SourceRange &R,
73 : const SourceManager &SM,
74 : unsigned LineNo, FileID FID,
75 : std::string &CaretLine,
76 260: const std::string &SourceLine) {
77 : assert(CaretLine.size() == SourceLine.size() &&
260: branch 2 taken
0: branch 3 not taken
78 260: "Expect a correspondence between source and caret line!");
233: branch 1 taken
27: branch 2 taken
79 260: if (!R.isValid()) return;
80 :
81 233: SourceLocation Begin = SM.getInstantiationLoc(R.getBegin());
82 233: SourceLocation End = SM.getInstantiationLoc(R.getEnd());
83 :
84 : // If the End location and the start location are the same and are a macro
85 : // location, then the range was something that came from a macro expansion
86 : // or _Pragma. If this is an object-like macro, the best we can do is to
87 : // highlight the range. If this is a function-like macro, we'd also like to
88 : // highlight the arguments.
127: branch 1 taken
106: branch 2 taken
7: branch 5 taken
120: branch 6 taken
7: branch 7 taken
226: branch 8 taken
89 233: if (Begin == End && R.getEnd().isMacroID())
90 7: End = SM.getInstantiationRange(R.getEnd()).second;
91 :
92 233: unsigned StartLineNo = SM.getInstantiationLineNumber(Begin);
232: branch 0 taken
1: branch 1 taken
0: branch 4 not taken
232: branch 5 taken
1: branch 6 taken
232: branch 7 taken
93 233: if (StartLineNo > LineNo || SM.getFileID(Begin) != FID)
94 1: return; // No intersection.
95 :
96 232: unsigned EndLineNo = SM.getInstantiationLineNumber(End);
229: branch 0 taken
3: branch 1 taken
0: branch 4 not taken
229: branch 5 taken
3: branch 6 taken
229: branch 7 taken
97 232: if (EndLineNo < LineNo || SM.getFileID(End) != FID)
98 3: return; // No intersection.
99 :
100 : // Compute the column number of the start.
101 229: unsigned StartColNo = 0;
229: branch 0 taken
0: branch 1 not taken
102 229: if (StartLineNo == LineNo) {
103 229: StartColNo = SM.getInstantiationColumnNumber(Begin);
229: branch 0 taken
0: branch 1 not taken
104 229: if (StartColNo) --StartColNo; // Zero base the col #.
105 : }
106 :
107 : // Pick the first non-whitespace column.
229: branch 1 taken
0: branch 2 not taken
229: branch 4 taken
0: branch 5 not taken
0: branch 7 not taken
229: branch 8 taken
0: branch 9 not taken
229: branch 10 taken
108 458: while (StartColNo < SourceLine.size() &&
109 : (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t'))
110 0: ++StartColNo;
111 :
112 : // Compute the column number of the end.
113 229: unsigned EndColNo = CaretLine.size();
227: branch 0 taken
2: branch 1 taken
114 229: if (EndLineNo == LineNo) {
115 227: EndColNo = SM.getInstantiationColumnNumber(End);
227: branch 0 taken
0: branch 1 not taken
116 227: if (EndColNo) {
117 227: --EndColNo; // Zero base the col #.
118 :
119 : // Add in the length of the token, so that we cover multi-char tokens.
120 227: EndColNo += Lexer::MeasureTokenLength(End, SM, *LangOpts);
121 : } else {
122 0: EndColNo = CaretLine.size();
123 : }
124 : }
125 :
126 : // Pick the last non-whitespace column.
228: branch 1 taken
1: branch 2 taken
127 229: if (EndColNo <= SourceLine.size())
228: branch 0 taken
0: branch 1 not taken
228: branch 3 taken
0: branch 4 not taken
0: branch 6 not taken
228: branch 7 taken
0: branch 8 not taken
228: branch 9 taken
128 456: while (EndColNo-1 &&
129 : (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
130 0: --EndColNo;
131 : else
132 1: EndColNo = SourceLine.size();
133 :
134 : // Fill the range with ~'s.
0: branch 0 not taken
229: branch 1 taken
135 229: assert(StartColNo <= EndColNo && "Invalid range!");
1934: branch 0 taken
229: branch 1 taken
136 2163: for (unsigned i = StartColNo; i < EndColNo; ++i)
137 1934: CaretLine[i] = '~';
138 : }
139 :
140 : /// \brief When the source code line we want to print is too long for
141 : /// the terminal, select the "interesting" region.
142 : static void SelectInterestingSourceRegion(std::string &SourceLine,
143 : std::string &CaretLine,
144 : std::string &FixItInsertionLine,
145 : unsigned EndOfCaretToken,
146 8: unsigned Columns) {
2: branch 2 taken
6: branch 3 taken
147 8: if (CaretLine.size() > SourceLine.size())
148 2: SourceLine.resize(CaretLine.size(), ' ');
149 :
150 : // Find the slice that we need to display the full caret line
151 : // correctly.
152 8: unsigned CaretStart = 0, CaretEnd = CaretLine.size();
460: branch 0 taken
0: branch 1 not taken
153 460: for (; CaretStart != CaretEnd; ++CaretStart)
8: branch 2 taken
452: branch 3 taken
154 460: if (!isspace(CaretLine[CaretStart]))
155 8: break;
156 :
304: branch 0 taken
0: branch 1 not taken
157 304: for (; CaretEnd != CaretStart; --CaretEnd)
8: branch 2 taken
296: branch 3 taken
158 304: if (!isspace(CaretLine[CaretEnd - 1]))
159 8: break;
160 :
161 : // Make sure we don't chop the string shorter than the caret token
162 : // itself.
2: branch 0 taken
6: branch 1 taken
163 8: if (CaretEnd < EndOfCaretToken)
164 2: CaretEnd = EndOfCaretToken;
165 :
166 : // If we have a fix-it line, make sure the slice includes all of the
167 : // fix-it information.
2: branch 1 taken
6: branch 2 taken
168 8: if (!FixItInsertionLine.empty()) {
169 2: unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size();
48: branch 0 taken
0: branch 1 not taken
170 48: for (; FixItStart != FixItEnd; ++FixItStart)
2: branch 2 taken
46: branch 3 taken
171 48: if (!isspace(FixItInsertionLine[FixItStart]))
172 2: break;
173 :
4: branch 0 taken
0: branch 1 not taken
174 4: for (; FixItEnd != FixItStart; --FixItEnd)
2: branch 2 taken
2: branch 3 taken
175 4: if (!isspace(FixItInsertionLine[FixItEnd - 1]))
176 2: break;
177 :
0: branch 0 not taken
2: branch 1 taken
178 2: if (FixItStart < CaretStart)
179 0: CaretStart = FixItStart;
0: branch 0 not taken
2: branch 1 taken
180 2: if (FixItEnd > CaretEnd)
181 0: CaretEnd = FixItEnd;
182 : }
183 :
184 : // CaretLine[CaretStart, CaretEnd) contains all of the interesting
185 : // parts of the caret line. While this slice is smaller than the
186 : // number of columns we have, try to grow the slice to encompass
187 : // more context.
188 :
189 : // If the end of the interesting region comes before we run out of
190 : // space in the terminal, start at the beginning of the line.
3: branch 0 taken
5: branch 1 taken
1: branch 2 taken
2: branch 3 taken
191 8: if (Columns > 3 && CaretEnd < Columns - 3)
192 1: CaretStart = 0;
193 :
194 8: unsigned TargetColumns = Columns;
3: branch 0 taken
5: branch 1 taken
195 8: if (TargetColumns > 8)
196 3: TargetColumns -= 8; // Give us extra room for the ellipses.
197 8: unsigned SourceLength = SourceLine.size();
29: branch 0 taken
5: branch 1 taken
198 42: while ((CaretEnd - CaretStart) < TargetColumns) {
199 29: bool ExpandedRegion = false;
200 : // Move the start of the interesting region left until we've
201 : // pulled in something else interesting.
0: branch 0 not taken
29: branch 1 taken
202 29: if (CaretStart == 1)
203 0: CaretStart = 0;
15: branch 0 taken
14: branch 1 taken
204 29: else if (CaretStart > 1) {
205 15: unsigned NewStart = CaretStart - 1;
206 :
207 : // Skip over any whitespace we see here; we're looking for
208 : // another bit of interesting text.
31: branch 0 taken
0: branch 1 not taken
16: branch 4 taken
15: branch 5 taken
16: branch 6 taken
15: branch 7 taken
209 46: while (NewStart && isspace(SourceLine[NewStart]))
210 16: --NewStart;
211 :
212 : // Skip over this bit of "interesting" text.
105: branch 0 taken
0: branch 1 not taken
90: branch 4 taken
15: branch 5 taken
90: branch 6 taken
15: branch 7 taken
213 120: while (NewStart && !isspace(SourceLine[NewStart]))
214 90: --NewStart;
215 :
216 : // Move up to the non-whitespace character we just saw.
15: branch 0 taken
0: branch 1 not taken
217 15: if (NewStart)
218 15: ++NewStart;
219 :
220 : // If we're still within our limit, update the starting
221 : // position within the source/caret line.
13: branch 0 taken
2: branch 1 taken
222 15: if (CaretEnd - NewStart <= TargetColumns) {
223 13: CaretStart = NewStart;
224 13: ExpandedRegion = true;
225 : }
226 : }
227 :
228 : // Move the end of the interesting region right until we've
229 : // pulled in something else interesting.
18: branch 0 taken
11: branch 1 taken
230 29: if (CaretEnd != SourceLength) {
0: branch 0 not taken
18: branch 1 taken
231 18: assert(CaretEnd < SourceLength && "Unexpected caret position!");
232 18: unsigned NewEnd = CaretEnd;
233 :
234 : // Skip over any whitespace we see here; we're looking for
235 : // another bit of interesting text.
34: branch 0 taken
0: branch 1 not taken
16: branch 4 taken
18: branch 5 taken
16: branch 6 taken
18: branch 7 taken
236 52: while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1]))
237 16: ++NewEnd;
238 :
239 : // Skip over this bit of "interesting" text.
122: branch 0 taken
2: branch 1 taken
106: branch 4 taken
16: branch 5 taken
106: branch 6 taken
18: branch 7 taken
240 142: while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1]))
241 106: ++NewEnd;
242 :
15: branch 0 taken
3: branch 1 taken
243 18: if (NewEnd - CaretStart <= TargetColumns) {
244 15: CaretEnd = NewEnd;
245 15: ExpandedRegion = true;
246 : }
247 : }
248 :
3: branch 0 taken
26: branch 1 taken
249 29: if (!ExpandedRegion)
250 3: break;
251 : }
252 :
253 : // [CaretStart, CaretEnd) is the slice we want. Update the various
254 : // output lines to show only this slice, with two-space padding
255 : // before the lines so that it looks nicer.
6: branch 1 taken
2: branch 2 taken
256 8: if (CaretEnd < SourceLine.size())
257 6: SourceLine.replace(CaretEnd, std::string::npos, "...");
6: branch 1 taken
2: branch 2 taken
258 8: if (CaretEnd < CaretLine.size())
259 6: CaretLine.erase(CaretEnd, std::string::npos);
0: branch 1 not taken
8: branch 2 taken
260 8: if (FixItInsertionLine.size() > CaretEnd)
261 0: FixItInsertionLine.erase(CaretEnd, std::string::npos);
262 :
7: branch 0 taken
1: branch 1 taken
263 8: if (CaretStart > 2) {
264 7: SourceLine.replace(0, CaretStart, " ...");
265 7: CaretLine.replace(0, CaretStart, " ");
1: branch 1 taken
6: branch 2 taken
266 7: if (FixItInsertionLine.size() >= CaretStart)
267 1: FixItInsertionLine.replace(0, CaretStart, " ");
268 : }
269 8: }
270 :
271 : void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc,
272 : SourceRange *Ranges,
273 : unsigned NumRanges,
274 : SourceManager &SM,
275 : const CodeModificationHint *Hints,
276 : unsigned NumHints,
277 672: unsigned Columns) {
0: branch 0 not taken
672: branch 1 taken
278 672: assert(LangOpts && "Unexpected diagnostic outside source file processing");
672: branch 1 taken
0: branch 2 not taken
279 672: assert(!Loc.isInvalid() && "must have a valid source location here");
280 :
281 : // If this is a macro ID, first emit information about where this was
282 : // instantiated (recursively) then emit information about where the token was
283 : // spelled from.
19: branch 1 taken
653: branch 2 taken
284 672: if (!Loc.isFileID()) {
285 19: SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first;
286 : // FIXME: Map ranges?
287 19: EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, Columns);
288 :
289 : // Map the location.
290 19: Loc = SM.getImmediateSpellingLoc(Loc);
291 :
292 : // Map the ranges.
15: branch 0 taken
19: branch 1 taken
293 34: for (unsigned i = 0; i != NumRanges; ++i) {
294 15: SourceLocation S = Ranges[i].getBegin(), E = Ranges[i].getEnd();
6: branch 1 taken
9: branch 2 taken
295 15: if (S.isMacroID()) S = SM.getImmediateSpellingLoc(S);
6: branch 1 taken
9: branch 2 taken
296 15: if (E.isMacroID()) E = SM.getImmediateSpellingLoc(E);
297 15: Ranges[i] = SourceRange(S, E);
298 : }
299 :
300 : // Get the pretty name, according to #line directives etc.
301 19: PresumedLoc PLoc = SM.getPresumedLoc(Loc);
302 :
303 : // If this diagnostic is not in the main file, print out the "included from"
304 : // lines.
0: branch 2 not taken
19: branch 3 taken
305 19: if (LastWarningLoc != PLoc.getIncludeLoc()) {
306 0: LastWarningLoc = PLoc.getIncludeLoc();
307 0: PrintIncludeStack(LastWarningLoc, SM);
308 : }
309 :
19: branch 0 taken
0: branch 1 not taken
310 19: if (DiagOpts->ShowLocation) {
311 : // Emit the file/line/column that this expansion came from.
312 19: OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':';
19: branch 0 taken
0: branch 1 not taken
313 19: if (DiagOpts->ShowColumn)
314 19: OS << PLoc.getColumn() << ':';
315 19: OS << ' ';
316 : }
317 19: OS << "note: instantiated from:\n";
318 :
319 19: EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, Hints, NumHints, Columns);
320 19: return;
321 : }
322 :
323 : // Decompose the location into a FID/Offset pair.
324 653: std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
325 653: FileID FID = LocInfo.first;
326 653: unsigned FileOffset = LocInfo.second;
327 :
328 : // Get information about the buffer it points into.
329 653: std::pair<const char*, const char*> BufferInfo = SM.getBufferData(FID);
330 653: const char *BufStart = BufferInfo.first;
331 :
332 653: unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
333 : unsigned CaretEndColNo
334 653: = ColNo + Lexer::MeasureTokenLength(Loc, SM, *LangOpts);
335 :
336 : // Rewind from the current position to the start of the line.
337 653: const char *TokPtr = BufStart+FileOffset;
338 653: const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based.
339 :
340 :
341 : // Compute the line end. Scan forward from the error position to the end of
342 : // the line.
343 653: const char *LineEnd = TokPtr;
17422: branch 0 taken
651: branch 1 taken
17422: branch 2 taken
0: branch 3 not taken
17420: branch 4 taken
2: branch 5 taken
344 18726: while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0')
345 17420: ++LineEnd;
346 :
347 : // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past
348 : // the source line length as currently being computed. See
349 : // test/Misc/message-length.c.
350 653: CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart));
351 :
352 : // Copy the line of code into an std::string for ease of manipulation.
353 653: std::string SourceLine(LineStart, LineEnd);
354 :
355 : // Create a line for the caret that is filled with spaces that is the same
356 : // length as the line of source code.
357 653: std::string CaretLine(LineEnd-LineStart, ' ');
358 :
359 : // Highlight all of the characters covered by Ranges with ~ characters.
212: branch 0 taken
441: branch 1 taken
360 653: if (NumRanges) {
361 212: unsigned LineNo = SM.getLineNumber(FID, FileOffset);
362 :
260: branch 0 taken
212: branch 1 taken
363 472: for (unsigned i = 0, e = NumRanges; i != e; ++i)
364 260: HighlightRange(Ranges[i], SM, LineNo, FID, CaretLine, SourceLine);
365 : }
366 :
367 : // Next, insert the caret itself.
646: branch 1 taken
7: branch 2 taken
368 653: if (ColNo-1 < CaretLine.size())
369 646: CaretLine[ColNo-1] = '^';
370 : else
371 7: CaretLine.push_back('^');
372 :
373 : // Scan the source line, looking for tabs. If we find any, manually expand
374 : // them to spaces and update the CaretLine to match.
25639: branch 1 taken
653: branch 2 taken
375 26292: for (unsigned i = 0; i != SourceLine.size(); ++i) {
37: branch 1 taken
25602: branch 2 taken
376 25639: if (SourceLine[i] != '\t') continue;
377 :
378 : // Replace this tab with at least one space.
379 37: SourceLine[i] = ' ';
380 :
381 : // Compute the number of spaces we need to insert.
382 37: unsigned TabStop = DiagOpts->TabStop;
383 : assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop &&
37: branch 0 taken
0: branch 1 not taken
0: branch 2 not taken
37: branch 3 taken
384 37: "Invalid -ftabstop value");
385 37: unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1);
0: branch 0 not taken
37: branch 1 taken
386 37: assert(NumSpaces < TabStop && "Invalid computation of space amt");
387 :
388 : // Insert spaces into the SourceLine.
389 37: SourceLine.insert(i+1, NumSpaces, ' ');
390 :
391 : // Insert spaces or ~'s into CaretLine.
3: branch 1 taken
34: branch 2 taken
392 37: CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' ');
393 : }
394 :
395 : // If we are in -fdiagnostics-print-source-range-info mode, we are trying to
396 : // produce easily machine parsable output. Add a space before the source line
397 : // and the caret to make it trivial to tell the main diagnostic line from what
398 : // the user is intended to see.
0: branch 0 not taken
653: branch 1 taken
399 653: if (DiagOpts->ShowSourceRanges) {
400 0: SourceLine = ' ' + SourceLine;
401 0: CaretLine = ' ' + CaretLine;
402 : }
403 :
404 653: std::string FixItInsertionLine;
80: branch 0 taken
573: branch 1 taken
80: branch 2 taken
0: branch 3 not taken
405 653: if (NumHints && DiagOpts->ShowFixits) {
99: branch 0 taken
78: branch 1 taken
406 177: for (const CodeModificationHint *Hint = Hints, *LastHint = Hints + NumHints;
407 : Hint != LastHint; ++Hint) {
93: branch 1 taken
6: branch 2 taken
408 99: if (Hint->InsertionLoc.isValid()) {
409 : // We have an insertion hint. Determine whether the inserted
410 : // code is on the same line as the caret.
411 : std::pair<FileID, unsigned> HintLocInfo
412 93: = SM.getDecomposedInstantiationLoc(Hint->InsertionLoc);
91: branch 2 taken
2: branch 3 taken
413 93: if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) ==
414 : SM.getLineNumber(FID, FileOffset)) {
415 : // Insert the new code into the line just below the code
416 : // that the user wrote.
417 : unsigned HintColNo
418 91: = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second);
419 : unsigned LastColumnModified
420 91: = HintColNo - 1 + Hint->CodeToInsert.size();
88: branch 1 taken
3: branch 2 taken
421 91: if (LastColumnModified > FixItInsertionLine.size())
422 88: FixItInsertionLine.resize(LastColumnModified, ' ');
423 : std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(),
424 91: FixItInsertionLine.begin() + HintColNo - 1);
425 : } else {
426 2: FixItInsertionLine.clear();
427 2: break;
428 : }
429 : }
430 : }
431 : // Now that we have the entire fixit line, expand the tabs in it.
432 : // Since we don't want to insert spaces in the middle of a word,
433 : // find each word and the column it should line up with and insert
434 : // spaces until they match.
72: branch 1 taken
8: branch 2 taken
435 80: if (!FixItInsertionLine.empty()) {
436 72: unsigned FixItPos = 0;
437 72: unsigned LinePos = 0;
438 72: unsigned TabExpandedCol = 0;
439 72: unsigned LineLength = LineEnd - LineStart;
440 :
103: branch 1 taken
70: branch 2 taken
101: branch 3 taken
2: branch 4 taken
101: branch 5 taken
72: branch 6 taken
441 245: while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) {
442 : // Find the next word in the FixIt line.
811: branch 1 taken
7: branch 2 taken
717: branch 4 taken
94: branch 5 taken
717: branch 6 taken
101: branch 7 taken
443 919: while (FixItPos < FixItInsertionLine.size() &&
444 : FixItInsertionLine[FixItPos] == ' ')
445 717: ++FixItPos;
446 101: unsigned CharDistance = FixItPos - TabExpandedCol;
447 :
448 : // Walk forward in the source line, keeping track of
449 : // the tab-expanded column.
776: branch 0 taken
101: branch 1 taken
450 877: for (unsigned I = 0; I < CharDistance; ++I, ++LinePos)
774: branch 0 taken
2: branch 1 taken
759: branch 2 taken
15: branch 3 taken
451 1537: if (LinePos >= LineLength || LineStart[LinePos] != '\t')
452 761: ++TabExpandedCol;
453 : else
454 : TabExpandedCol =
455 15: (TabExpandedCol/DiagOpts->TabStop + 1) * DiagOpts->TabStop;
456 :
457 : // Adjust the fixit line to match this column.
458 101: FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' ');
459 101: FixItPos = TabExpandedCol;
460 :
461 : // Walk to the end of the word.
342: branch 1 taken
70: branch 2 taken
311: branch 4 taken
31: branch 5 taken
311: branch 6 taken
101: branch 7 taken
462 513: while (FixItPos < FixItInsertionLine.size() &&
463 : FixItInsertionLine[FixItPos] != ' ')
464 311: ++FixItPos;
465 : }
466 : }
467 : }
468 :
469 : // If the source line is too long for our terminal, select only the
470 : // "interesting" source region within that line.
10: branch 0 taken
643: branch 1 taken
8: branch 3 taken
2: branch 4 taken
8: branch 5 taken
645: branch 6 taken
471 653: if (Columns && SourceLine.size() > Columns)
472 : SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine,
473 8: CaretEndColNo, Columns);
474 :
475 : // Finally, remove any blank spaces from the end of CaretLine.
14561: branch 2 taken
653: branch 3 taken
476 15867: while (CaretLine[CaretLine.size()-1] == ' ')
477 14561: CaretLine.erase(CaretLine.end()-1);
478 :
479 : // Emit what we have computed.
480 653: OS << SourceLine << '\n';
481 :
0: branch 0 not taken
653: branch 1 taken
482 653: if (DiagOpts->ShowColors)
483 0: OS.changeColor(caretColor, true);
484 653: OS << CaretLine << '\n';
0: branch 0 not taken
653: branch 1 taken
485 653: if (DiagOpts->ShowColors)
486 0: OS.resetColor();
487 :
72: branch 1 taken
581: branch 2 taken
488 653: if (!FixItInsertionLine.empty()) {
0: branch 0 not taken
72: branch 1 taken
489 72: if (DiagOpts->ShowColors)
490 : // Print fixit line in color
491 0: OS.changeColor(fixitColor, false);
0: branch 0 not taken
72: branch 1 taken
492 72: if (DiagOpts->ShowSourceRanges)
493 0: OS << ' ';
494 72: OS << FixItInsertionLine << '\n';
0: branch 0 not taken
72: branch 1 taken
495 72: if (DiagOpts->ShowColors)
496 0: OS.resetColor();
497 653: }
498 : }
499 :
500 : /// \brief Skip over whitespace in the string, starting at the given
501 : /// index.
502 : ///
503 : /// \returns The index of the first non-whitespace character that is
504 : /// greater than or equal to Idx or, if no such character exists,
505 : /// returns the end of the string.
506 : static unsigned skipWhitespace(unsigned Idx,
507 : const llvm::SmallVectorImpl<char> &Str,
508 99: unsigned Length) {
188: branch 0 taken
0: branch 1 not taken
89: branch 4 taken
99: branch 5 taken
89: branch 6 taken
99: branch 7 taken
509 287: while (Idx < Length && isspace(Str[Idx]))
510 89: ++Idx;
511 99: return Idx;
512 : }
513 :
514 : /// \brief If the given character is the start of some kind of
515 : /// balanced punctuation (e.g., quotes or parentheses), return the
516 : /// character that will terminate the punctuation.
517 : ///
518 : /// \returns The ending punctuation character, if any, or the NULL
519 : /// character if the input character does not start any punctuation.
520 544: static inline char findMatchingPunctuation(char c) {
22: branch 0 taken
0: branch 1 not taken
0: branch 2 not taken
20: branch 3 taken
0: branch 4 not taken
0: branch 5 not taken
502: branch 6 taken
521 544: switch (c) {
522 22: case '\'': return '\'';
523 0: case '`': return '\'';
524 0: case '"': return '"';
525 20: case '(': return ')';
526 0: case '[': return ']';
527 0: case '{': return '}';
528 : default: break;
529 : }
530 :
531 502: return 0;
532 : }
533 :
534 : /// \brief Find the end of the word starting at the given offset
535 : /// within a string.
536 : ///
537 : /// \returns the index pointing one character past the end of the
538 : /// word.
539 : static unsigned findEndOfWord(unsigned Start,
540 : const llvm::SmallVectorImpl<char> &Str,
541 : unsigned Length, unsigned Column,
542 118: unsigned Columns) {
118: branch 1 taken
0: branch 2 not taken
543 118: assert(Start < Str.size() && "Invalid start position!");
544 118: unsigned End = Start + 1;
545 :
546 : // If we are already at the end of the string, take that as the word.
0: branch 1 not taken
118: branch 2 taken
547 118: if (End == Str.size())
548 0: return End;
549 :
550 : // Determine if the start of the string is actually opening
551 : // punctuation, e.g., a quote or parentheses.
552 118: char EndPunct = findMatchingPunctuation(Str[Start]);
92: branch 0 taken
26: branch 1 taken
553 118: if (!EndPunct) {
554 : // This is a normal word. Just find the first space character.
537: branch 0 taken
7: branch 1 taken
452: branch 4 taken
85: branch 5 taken
452: branch 6 taken
92: branch 7 taken
555 636: while (End < Length && !isspace(Str[End]))
556 452: ++End;
557 92: return End;
558 : }
559 :
560 : // We have the start of a balanced punctuation sequence (quotes,
561 : // parentheses, etc.). Determine the full sequence is.
562 26: llvm::SmallVector<char, 16> PunctuationEndStack;
563 26: PunctuationEndStack.push_back(EndPunct);
488: branch 0 taken
6: branch 1 taken
468: branch 3 taken
20: branch 4 taken
468: branch 5 taken
26: branch 6 taken
564 520: while (End < Length && !PunctuationEndStack.empty()) {
42: branch 2 taken
426: branch 3 taken
565 468: if (Str[End] == PunctuationEndStack.back())
566 42: PunctuationEndStack.pop_back();
16: branch 2 taken
410: branch 3 taken
567 426: else if (char SubEndPunct = findMatchingPunctuation(Str[End]))
568 16: PunctuationEndStack.push_back(SubEndPunct);
569 :
570 468: ++End;
571 : }
572 :
573 : // Find the first space character after the punctuation ended.
42: branch 0 taken
7: branch 1 taken
23: branch 4 taken
19: branch 5 taken
23: branch 6 taken
26: branch 7 taken
574 75: while (End < Length && !isspace(Str[End]))
575 23: ++End;
576 :
577 26: unsigned PunctWordLength = End - Start;
20: branch 0 taken
6: branch 1 taken
1: branch 2 taken
19: branch 3 taken
578 26: if (// If the word fits on this line
579 : Column + PunctWordLength <= Columns ||
580 : // ... or the word is "short enough" to take up the next line
581 : // without too much ugly white space
582 : PunctWordLength < Columns/3)
583 7: return End; // Take the whole thing as a single "word".
584 :
585 : // The whole quoted/parenthesized string is too long to print as a
586 : // single "word". Instead, find the "word" that starts just after
587 : // the punctuation and use that end-point instead. This will recurse
588 : // until it finds something small enough to consider a word.
589 19: return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns);
590 : }
591 :
592 : /// \brief Print the given string to a stream, word-wrapping it to
593 : /// some number of columns in the process.
594 : ///
595 : /// \brief OS the stream to which the word-wrapping string will be
596 : /// emitted.
597 : ///
598 : /// \brief Str the string to word-wrap and output.
599 : ///
600 : /// \brief Columns the number of columns to word-wrap to.
601 : ///
602 : /// \brief Column the column number at which the first character of \p
603 : /// Str will be printed. This will be non-zero when part of the first
604 : /// line has already been printed.
605 : ///
606 : /// \brief Indentation the number of spaces to indent any lines beyond
607 : /// the first line.
608 : ///
609 : /// \returns true if word-wrapping was required, or false if the
610 : /// string fit on the first line.
611 : static bool PrintWordWrapped(llvm::raw_ostream &OS,
612 : const llvm::SmallVectorImpl<char> &Str,
613 : unsigned Columns,
614 : unsigned Column = 0,
615 10: unsigned Indentation = WordWrapIndentation) {
616 10: unsigned Length = Str.size();
617 :
618 : // If there is a newline in this message somewhere, find that
619 : // newline and split the message into the part before the newline
620 : // (which will be word-wrapped) and the part from the newline one
621 : // (which will be emitted unchanged).
774: branch 0 taken
10: branch 1 taken
622 784: for (unsigned I = 0; I != Length; ++I)
0: branch 1 not taken
774: branch 2 taken
623 774: if (Str[I] == '\n') {
624 0: Length = I;
625 0: break;
626 : }
627 :
628 : // The string used to indent each line.
629 10: llvm::SmallString<16> IndentStr;
630 10: IndentStr.assign(Indentation, ' ');
631 10: bool Wrapped = false;
99: branch 0 taken
10: branch 1 taken
632 109: for (unsigned WordStart = 0, WordEnd; WordStart < Length;
633 : WordStart = WordEnd) {
634 : // Find the beginning of the next word.
635 99: WordStart = skipWhitespace(WordStart, Str, Length);
0: branch 0 not taken
99: branch 1 taken
636 99: if (WordStart == Length)
637 0: break;
638 :
639 : // Find the end of this word.
640 99: WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns);
641 :
642 : // Does this word fit on the current line?
643 99: unsigned WordLength = WordEnd - WordStart;
40: branch 0 taken
59: branch 1 taken
644 99: if (Column + WordLength < Columns) {
645 : // This word fits on the current line; print it there.
35: branch 0 taken
5: branch 1 taken
646 40: if (WordStart) {
647 35: OS << ' ';
648 35: Column += 1;
649 : }
650 40: OS.write(&Str[WordStart], WordLength);
651 40: Column += WordLength;
652 40: continue;
653 : }
654 :
655 : // This word does not fit on the current line, so wrap to the next
656 : // line.
657 59: OS << '\n';
658 59: OS.write(&IndentStr[0], Indentation);
659 59: OS.write(&Str[WordStart], WordLength);
660 59: Column = Indentation + WordLength;
661 59: Wrapped = true;
662 : }
663 :
10: branch 1 taken
0: branch 2 not taken
664 10: if (Length == Str.size())
665 10: return Wrapped; // We're done.
666 :
667 : // There is a newline in the message, followed by something that
668 : // will not be word-wrapped. Print that.
669 0: OS.write(&Str[Length], Str.size() - Length);
670 0: return true;
671 : }
672 :
673 : void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level,
674 770: const DiagnosticInfo &Info) {
675 : // Keeps track of the the starting position of the location
676 : // information (e.g., "foo.c:10:4:") that precedes the error
677 : // message. We use this information to determine how long the
678 : // file+line+column number prefix is.
679 770: uint64_t StartOfLocationInfo = OS.tell();
680 :
681 : // If the location is specified, print out a file/line/col and include trace
682 : // if enabled.
744: branch 2 taken
26: branch 3 taken
683 770: if (Info.getLocation().isValid()) {
684 744: const SourceManager &SM = Info.getLocation().getManager();
685 744: PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
686 744: unsigned LineNo = PLoc.getLine();
687 :
688 : // First, if this diagnostic is not in the main file, print out the
689 : // "included from" lines.
22: branch 2 taken
722: branch 3 taken
690 744: if (LastWarningLoc != PLoc.getIncludeLoc()) {
691 22: LastWarningLoc = PLoc.getIncludeLoc();
692 22: PrintIncludeStack(LastWarningLoc, SM);
693 22: StartOfLocationInfo = OS.tell();
694 : }
695 :
696 : // Compute the column number.
744: branch 0 taken
0: branch 1 not taken
697 744: if (DiagOpts->ShowLocation) {
0: branch 0 not taken
744: branch 1 taken
698 744: if (DiagOpts->ShowColors)
699 0: OS.changeColor(savedColor, true);
700 :
701 : // Emit a Visual Studio compatible line number syntax.
744: branch 0 taken
0: branch 1 not taken
4: branch 2 taken
740: branch 3 taken
702 748: if (LangOpts && LangOpts->Microsoft) {
703 4: OS << PLoc.getFilename() << '(' << LineNo << ')';
704 4: OS << " : ";
705 : } else {
706 740: OS << PLoc.getFilename() << ':' << LineNo << ':';
740: branch 0 taken
0: branch 1 not taken
707 740: if (DiagOpts->ShowColumn)
740: branch 1 taken
0: branch 2 not taken
708 740: if (unsigned ColNo = PLoc.getColumn())
709 740: OS << ColNo << ':';
710 : }
0: branch 0 not taken
744: branch 1 taken
0: branch 3 not taken
0: branch 4 not taken
0: branch 5 not taken
744: branch 6 taken
711 744: if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) {
712 : FileID CaretFileID =
713 0: SM.getFileID(SM.getInstantiationLoc(Info.getLocation()));
714 0: bool PrintedRange = false;
715 :
0: branch 1 not taken
0: branch 2 not taken
716 0: for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) {
717 : // Ignore invalid ranges.
0: branch 2 not taken
0: branch 3 not taken
718 0: if (!Info.getRange(i).isValid()) continue;
719 :
720 0: SourceLocation B = Info.getRange(i).getBegin();
721 0: SourceLocation E = Info.getRange(i).getEnd();
722 0: B = SM.getInstantiationLoc(B);
723 0: E = SM.getInstantiationLoc(E);
724 :
725 : // If the End location and the start location are the same and are a
726 : // macro location, then the range was something that came from a macro
727 : // expansion or _Pragma. If this is an object-like macro, the best we
728 : // can do is to highlight the range. If this is a function-like
729 : // macro, we'd also like to highlight the arguments.
0: branch 1 not taken
0: branch 2 not taken
0: branch 6 not taken
0: branch 7 not taken
0: branch 8 not taken
0: branch 9 not taken
730 0: if (B == E && Info.getRange(i).getEnd().isMacroID())
731 0: E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second;
732 :
733 0: std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
734 0: std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
735 :
736 : // If the start or end of the range is in another file, just discard
737 : // it.
0: branch 1 not taken
0: branch 2 not taken
0: branch 4 not taken
0: branch 5 not taken
0: branch 6 not taken
0: branch 7 not taken
738 0: if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
739 0: continue;
740 :
741 : // Add in the length of the token, so that we cover multi-char tokens.
742 0: unsigned TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts);
743 :
744 : OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
745 : << SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
746 : << SM.getLineNumber(EInfo.first, EInfo.second) << ':'
747 0: << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}';
748 0: PrintedRange = true;
749 : }
750 :
0: branch 0 not taken
0: branch 1 not taken
751 0: if (PrintedRange)
752 0: OS << ':';
753 : }
754 744: OS << ' ';
0: branch 0 not taken
744: branch 1 taken
755 744: if (DiagOpts->ShowColors)
756 0: OS.resetColor();
757 : }
758 : }
759 :
0: branch 0 not taken
770: branch 1 taken
760 770: if (DiagOpts->ShowColors) {
761 : // Print diagnostic category in bold and color
0: branch 0 not taken
0: branch 1 not taken
0: branch 2 not taken
0: branch 3 not taken
0: branch 4 not taken
0: branch 5 not taken
762 0: switch (Level) {
763 0: case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
764 0: case Diagnostic::Note: OS.changeColor(noteColor, true); break;
765 0: case Diagnostic::Warning: OS.changeColor(warningColor, true); break;
766 0: case Diagnostic::Error: OS.changeColor(errorColor, true); break;
767 0: case Diagnostic::Fatal: OS.changeColor(fatalColor, true); break;
768 : }
769 : }
770 :
0: branch 0 not taken
186: branch 1 taken
450: branch 2 taken
131: branch 3 taken
3: branch 4 taken
0: branch 5 not taken
771 770: switch (Level) {
772 0: case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type");
773 186: case Diagnostic::Note: OS << "note: "; break;
774 450: case Diagnostic::Warning: OS << "warning: "; break;
775 131: case Diagnostic::Error: OS << "error: "; break;
776 3: case Diagnostic::Fatal: OS << "fatal error: "; break;
777 : }
778 :
0: branch 0 not taken
770: branch 1 taken
779 770: if (DiagOpts->ShowColors)
780 0: OS.resetColor();
781 :
782 770: llvm::SmallString<100> OutStr;
783 770: Info.FormatDiagnostic(OutStr);
784 :
18: branch 0 taken
752: branch 1 taken
785 770: if (DiagOpts->ShowOptionNames)
12: branch 2 taken
6: branch 3 taken
786 18: if (const char *Opt = Diagnostic::getWarningOptionForDiag(Info.getID())) {
787 12: OutStr += " [-W";
788 12: OutStr += Opt;
789 12: OutStr += ']';
790 : }
791 :
0: branch 0 not taken
770: branch 1 taken
792 770: if (DiagOpts->ShowColors) {
793 : // Print warnings, errors and fatal errors in bold, no color
0: branch 0 not taken
0: branch 1 not taken
0: branch 2 not taken
0: branch 3 not taken
794 0: switch (Level) {
795 0: case Diagnostic::Warning: OS.changeColor(savedColor, true); break;
796 0: case Diagnostic::Error: OS.changeColor(savedColor, true); break;
797 0: case Diagnostic::Fatal: OS.changeColor(savedColor, true); break;
798 : default: break; //don't bold notes
799 : }
800 : }
801 :
10: branch 0 taken
760: branch 1 taken
802 770: if (DiagOpts->MessageLength) {
803 : // We will be word-wrapping the error message, so compute the
804 : // column number where we currently are (after printing the
805 : // location information).
806 10: unsigned Column = OS.tell() - StartOfLocationInfo;
807 10: PrintWordWrapped(OS, OutStr, DiagOpts->MessageLength, Column);
808 : } else {
809 760: OS.write(OutStr.begin(), OutStr.size());
810 : }
811 770: OS << '\n';
0: branch 0 not taken
770: branch 1 taken
812 770: if (DiagOpts->ShowColors)
813 0: OS.resetColor();
814 :
815 : // If caret diagnostics are enabled and we have location, we want to
816 : // emit the caret. However, we only do this if the location moved
817 : // from the last diagnostic, if the last diagnostic was a note that
818 : // was part of a different warning or error diagnostic, or if the
819 : // diagnostic has ranges. We don't want to emit the same caret
820 : // multiple times if one loc has multiple diagnostics.
766: branch 0 taken
4: branch 1 taken
740: branch 4 taken
26: branch 5 taken
120: branch 8 taken
620: branch 9 taken
119: branch 11 taken
1: branch 12 taken
6: branch 13 taken
113: branch 14 taken
0: branch 15 not taken
6: branch 16 taken
7: branch 18 taken
106: branch 19 taken
634: branch 20 taken
136: branch 21 taken
821 770: if (DiagOpts->ShowCarets && Info.getLocation().isValid() &&
822 : ((LastLoc != Info.getLocation()) || Info.getNumRanges() ||
823 : (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) ||
824 : Info.getNumCodeModificationHints())) {
825 : // Cache the LastLoc, it allows us to omit duplicate source/caret spewage.
826 634: LastLoc = Info.getLocation();
827 634: LastCaretDiagnosticWasNote = (Level == Diagnostic::Note);
828 :
829 : // Get the ranges into a local array we can hack on.
12680: branch 1 taken
634: branch 2 taken
830 634: SourceRange Ranges[20];
831 634: unsigned NumRanges = Info.getNumRanges();
0: branch 0 not taken
634: branch 1 taken
832 634: assert(NumRanges < 20 && "Out of space");
207: branch 0 taken
634: branch 1 taken
833 841: for (unsigned i = 0; i != NumRanges; ++i)
834 207: Ranges[i] = Info.getRange(i);
835 :
836 634: unsigned NumHints = Info.getNumCodeModificationHints();
99: branch 0 taken
634: branch 1 taken
837 733: for (unsigned idx = 0; idx < NumHints; ++idx) {
838 99: const CodeModificationHint &Hint = Info.getCodeModificationHint(idx);
38: branch 1 taken
61: branch 2 taken
839 99: if (Hint.RemoveRange.isValid()) {
0: branch 0 not taken
38: branch 1 taken
840 38: assert(NumRanges < 20 && "Out of space");
841 38: Ranges[NumRanges++] = Hint.RemoveRange;
842 : }
843 : }
844 :
845 : EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(),
846 : Info.getCodeModificationHints(),
847 : Info.getNumCodeModificationHints(),
848 634: DiagOpts->MessageLength);
849 : }
850 :
851 770: OS.flush();
852 770: }
Generated: 2010-02-10 01:31 by zcov