TextDiagnosticPrinter.cpp revision aa5bf2e8dc01f9835efef76222dc440a5c18b160
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/Lex/Lexer.h" 17#include "llvm/Support/raw_ostream.h" 18#include "llvm/ADT/SmallString.h" 19#include <algorithm> 20using namespace clang; 21 22void TextDiagnosticPrinter:: 23PrintIncludeStack(SourceLocation Loc, const SourceManager &SM) { 24 if (Loc.isInvalid()) return; 25 26 PresumedLoc PLoc = SM.getPresumedLoc(Loc); 27 28 // Print out the other include frames first. 29 PrintIncludeStack(PLoc.getIncludeLoc(), SM); 30 31 OS << "In file included from " << PLoc.getFilename() 32 << ':' << PLoc.getLine() << ":\n"; 33} 34 35/// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s) 36/// any characters in LineNo that intersect the SourceRange. 37void TextDiagnosticPrinter::HighlightRange(const SourceRange &R, 38 const SourceManager &SM, 39 unsigned LineNo, FileID FID, 40 std::string &CaretLine, 41 const std::string &SourceLine) { 42 assert(CaretLine.size() == SourceLine.size() && 43 "Expect a correspondence between source and caret line!"); 44 if (!R.isValid()) return; 45 46 SourceLocation Begin = SM.getInstantiationLoc(R.getBegin()); 47 SourceLocation End = SM.getInstantiationLoc(R.getEnd()); 48 49 // If the End location and the start location are the same and are a macro 50 // location, then the range was something that came from a macro expansion 51 // or _Pragma. If this is an object-like macro, the best we can do is to 52 // highlight the range. If this is a function-like macro, we'd also like to 53 // highlight the arguments. 54 if (Begin == End && R.getEnd().isMacroID()) 55 End = SM.getInstantiationRange(R.getEnd()).second; 56 57 unsigned StartLineNo = SM.getInstantiationLineNumber(Begin); 58 if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) 59 return; // No intersection. 60 61 unsigned EndLineNo = SM.getInstantiationLineNumber(End); 62 if (EndLineNo < LineNo || SM.getFileID(End) != FID) 63 return; // No intersection. 64 65 // Compute the column number of the start. 66 unsigned StartColNo = 0; 67 if (StartLineNo == LineNo) { 68 StartColNo = SM.getInstantiationColumnNumber(Begin); 69 if (StartColNo) --StartColNo; // Zero base the col #. 70 } 71 72 // Pick the first non-whitespace column. 73 while (StartColNo < SourceLine.size() && 74 (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t')) 75 ++StartColNo; 76 77 // Compute the column number of the end. 78 unsigned EndColNo = CaretLine.size(); 79 if (EndLineNo == LineNo) { 80 EndColNo = SM.getInstantiationColumnNumber(End); 81 if (EndColNo) { 82 --EndColNo; // Zero base the col #. 83 84 // Add in the length of the token, so that we cover multi-char tokens. 85 EndColNo += Lexer::MeasureTokenLength(End, SM, *LangOpts); 86 } else { 87 EndColNo = CaretLine.size(); 88 } 89 } 90 91 // Pick the last non-whitespace column. 92 if (EndColNo <= SourceLine.size()) 93 while (EndColNo-1 && 94 (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t')) 95 --EndColNo; 96 else 97 EndColNo = SourceLine.size(); 98 99 // Fill the range with ~'s. 100 assert(StartColNo <= EndColNo && "Invalid range!"); 101 for (unsigned i = StartColNo; i < EndColNo; ++i) 102 CaretLine[i] = '~'; 103} 104 105void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, 106 SourceRange *Ranges, 107 unsigned NumRanges, 108 SourceManager &SM, 109 const CodeModificationHint *Hints, 110 unsigned NumHints) { 111 assert(!Loc.isInvalid() && "must have a valid source location here"); 112 113 // We always emit diagnostics about the instantiation points, not the spelling 114 // points. This more closely correlates to what the user writes. 115 if (!Loc.isFileID()) { 116 SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first; 117 EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM); 118 119 // Map the location through the macro. 120 Loc = SM.getInstantiationLoc(SM.getImmediateSpellingLoc(Loc)); 121 122 // Map the ranges. 123 for (unsigned i = 0; i != NumRanges; ++i) { 124 SourceLocation S = Ranges[i].getBegin(), E = Ranges[i].getEnd(); 125 if (S.isMacroID()) 126 S = SM.getInstantiationLoc(SM.getImmediateSpellingLoc(S)); 127 if (E.isMacroID()) 128 E = SM.getInstantiationLoc(SM.getImmediateSpellingLoc(E)); 129 Ranges[i] = SourceRange(S, E); 130 } 131 132 // Emit the file/line/column that this expansion came from. 133 OS << SM.getBufferName(Loc) << ':' << SM.getInstantiationLineNumber(Loc) 134 << ':'; 135 if (ShowColumn) 136 OS << SM.getInstantiationColumnNumber(Loc) << ':'; 137 OS << " note: instantiated from:\n"; 138 } 139 140 // Decompose the location into a FID/Offset pair. 141 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 142 FileID FID = LocInfo.first; 143 unsigned FileOffset = LocInfo.second; 144 145 // Get information about the buffer it points into. 146 std::pair<const char*, const char*> BufferInfo = SM.getBufferData(FID); 147 const char *BufStart = BufferInfo.first; 148 149 unsigned ColNo = SM.getColumnNumber(FID, FileOffset); 150 151 // Rewind from the current position to the start of the line. 152 const char *TokPtr = BufStart+FileOffset; 153 const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. 154 155 156 // Compute the line end. Scan forward from the error position to the end of 157 // the line. 158 const char *LineEnd = TokPtr; 159 while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') 160 ++LineEnd; 161 162 // Copy the line of code into an std::string for ease of manipulation. 163 std::string SourceLine(LineStart, LineEnd); 164 165 // Create a line for the caret that is filled with spaces that is the same 166 // length as the line of source code. 167 std::string CaretLine(LineEnd-LineStart, ' '); 168 169 // Highlight all of the characters covered by Ranges with ~ characters. 170 if (NumRanges) { 171 unsigned LineNo = SM.getLineNumber(FID, FileOffset); 172 173 for (unsigned i = 0, e = NumRanges; i != e; ++i) 174 HighlightRange(Ranges[i], SM, LineNo, FID, CaretLine, SourceLine); 175 } 176 177 // Next, insert the caret itself. 178 if (ColNo-1 < CaretLine.size()) 179 CaretLine[ColNo-1] = '^'; 180 else 181 CaretLine.push_back('^'); 182 183 // Scan the source line, looking for tabs. If we find any, manually expand 184 // them to 8 characters and update the CaretLine to match. 185 for (unsigned i = 0; i != SourceLine.size(); ++i) { 186 if (SourceLine[i] != '\t') continue; 187 188 // Replace this tab with at least one space. 189 SourceLine[i] = ' '; 190 191 // Compute the number of spaces we need to insert. 192 unsigned NumSpaces = ((i+8)&~7) - (i+1); 193 assert(NumSpaces < 8 && "Invalid computation of space amt"); 194 195 // Insert spaces into the SourceLine. 196 SourceLine.insert(i+1, NumSpaces, ' '); 197 198 // Insert spaces or ~'s into CaretLine. 199 CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' '); 200 } 201 202 // Finally, remove any blank spaces from the end of CaretLine. 203 while (CaretLine[CaretLine.size()-1] == ' ') 204 CaretLine.erase(CaretLine.end()-1); 205 206 // Emit what we have computed. 207 OS << SourceLine << '\n'; 208 OS << CaretLine << '\n'; 209 210 if (NumHints && PrintFixItInfo) { 211 std::string InsertionLine; 212 for (const CodeModificationHint *Hint = Hints, *LastHint = Hints + NumHints; 213 Hint != LastHint; ++Hint) { 214 if (Hint->InsertionLoc.isValid()) { 215 // We have an insertion hint. Determine whether the inserted 216 // code is on the same line as the caret. 217 std::pair<FileID, unsigned> HintLocInfo 218 = SM.getDecomposedInstantiationLoc(Hint->InsertionLoc); 219 if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) == 220 SM.getLineNumber(FID, FileOffset)) { 221 // Insert the new code into the line just below the code 222 // that the user wrote. 223 unsigned HintColNo 224 = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); 225 unsigned LastColumnModified 226 = HintColNo - 1 + Hint->CodeToInsert.size(); 227 if (LastColumnModified > InsertionLine.size()) 228 InsertionLine.resize(LastColumnModified, ' '); 229 std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(), 230 InsertionLine.begin() + HintColNo - 1); 231 } 232 } 233 } 234 235 if (!InsertionLine.empty()) 236 OS << InsertionLine << '\n'; 237 } 238} 239 240 241void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, 242 const DiagnosticInfo &Info) { 243 // If the location is specified, print out a file/line/col and include trace 244 // if enabled. 245 if (Info.getLocation().isValid()) { 246 const SourceManager &SM = Info.getLocation().getManager(); 247 PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); 248 unsigned LineNo = PLoc.getLine(); 249 250 // First, if this diagnostic is not in the main file, print out the 251 // "included from" lines. 252 if (LastWarningLoc != PLoc.getIncludeLoc()) { 253 LastWarningLoc = PLoc.getIncludeLoc(); 254 PrintIncludeStack(LastWarningLoc, SM); 255 } 256 257 // Compute the column number. 258 if (ShowLocation) { 259 OS << PLoc.getFilename() << ':' << LineNo << ':'; 260 if (ShowColumn) 261 if (unsigned ColNo = PLoc.getColumn()) 262 OS << ColNo << ':'; 263 264 if (PrintRangeInfo && Info.getNumRanges()) { 265 FileID CaretFileID = 266 SM.getFileID(SM.getInstantiationLoc(Info.getLocation())); 267 bool PrintedRange = false; 268 269 for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) { 270 SourceLocation B = Info.getRange(i).getBegin(); 271 SourceLocation E = Info.getRange(i).getEnd(); 272 std::pair<FileID, unsigned> BInfo=SM.getDecomposedInstantiationLoc(B); 273 274 E = SM.getInstantiationLoc(E); 275 std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); 276 277 // If the start or end of the range is in another file, just discard 278 // it. 279 if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) 280 continue; 281 282 // Add in the length of the token, so that we cover multi-char tokens. 283 unsigned TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts); 284 285 OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' 286 << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' 287 << SM.getLineNumber(EInfo.first, EInfo.second) << ':' 288 << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}'; 289 PrintedRange = true; 290 } 291 292 if (PrintedRange) 293 OS << ':'; 294 } 295 OS << ' '; 296 } 297 } 298 299 switch (Level) { 300 case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); 301 case Diagnostic::Note: OS << "note: "; break; 302 case Diagnostic::Warning: OS << "warning: "; break; 303 case Diagnostic::Error: OS << "error: "; break; 304 case Diagnostic::Fatal: OS << "fatal error: "; break; 305 } 306 307 llvm::SmallString<100> OutStr; 308 Info.FormatDiagnostic(OutStr); 309 OS.write(OutStr.begin(), OutStr.size()); 310 311 if (PrintDiagnosticOption) 312 if (const char *Option = Diagnostic::getWarningOptionForDiag(Info.getID())) 313 OS << " [-W" << Option << ']'; 314 315 OS << '\n'; 316 317 // If caret diagnostics are enabled and we have location, we want to 318 // emit the caret. However, we only do this if the location moved 319 // from the last diagnostic, if the last diagnostic was a note that 320 // was part of a different warning or error diagnostic, or if the 321 // diagnostic has ranges. We don't want to emit the same caret 322 // multiple times if one loc has multiple diagnostics. 323 if (CaretDiagnostics && Info.getLocation().isValid() && 324 ((LastLoc != Info.getLocation()) || Info.getNumRanges() || 325 (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) || 326 Info.getNumCodeModificationHints())) { 327 // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. 328 LastLoc = Info.getLocation(); 329 LastCaretDiagnosticWasNote = (Level == Diagnostic::Note); 330 331 // Get the ranges into a local array we can hack on. 332 SourceRange Ranges[20]; 333 unsigned NumRanges = Info.getNumRanges(); 334 assert(NumRanges < 20 && "Out of space"); 335 for (unsigned i = 0; i != NumRanges; ++i) 336 Ranges[i] = Info.getRange(i); 337 338 unsigned NumHints = Info.getNumCodeModificationHints(); 339 for (unsigned idx = 0; idx < NumHints; ++idx) { 340 const CodeModificationHint &Hint = Info.getCodeModificationHint(idx); 341 if (Hint.RemoveRange.isValid()) { 342 assert(NumRanges < 20 && "Out of space"); 343 Ranges[NumRanges++] = Hint.RemoveRange; 344 } 345 } 346 347 EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), 348 Info.getCodeModificationHints(), 349 Info.getNumCodeModificationHints()); 350 } 351 352 OS.flush(); 353} 354