TextDiagnosticPrinter.cpp revision df667e71b1daadeacb230cf94fc717843f1a138a
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); 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) { 211 std::string InsertionLine; 212 for (const CodeModificationHint *Hint = Hints, 213 *LastHint = Hints + NumHints; 214 Hint != LastHint; ++Hint) { 215 if (Hint->InsertionLoc.isValid()) { 216 // We have an insertion hint. Determine whether the inserted 217 // code is on the same line as the caret. 218 std::pair<FileID, unsigned> HintLocInfo 219 = SM.getDecomposedInstantiationLoc(Hint->InsertionLoc); 220 if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) == 221 SM.getLineNumber(FID, FileOffset)) { 222 // Insert the new code into the line just below the code 223 // that the user wrote. 224 unsigned HintColNo 225 = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); 226 unsigned LastColumnModified 227 = HintColNo - 1 + Hint->CodeToInsert.size(); 228 if (LastColumnModified > InsertionLine.size()) 229 InsertionLine.resize(LastColumnModified, ' '); 230 std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(), 231 InsertionLine.begin() + HintColNo - 1); 232 } 233 } 234 } 235 236 if (!InsertionLine.empty()) 237 OS << InsertionLine << '\n'; 238 } 239} 240 241 242void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, 243 const DiagnosticInfo &Info) { 244 // If the location is specified, print out a file/line/col and include trace 245 // if enabled. 246 if (Info.getLocation().isValid()) { 247 const SourceManager &SM = Info.getLocation().getManager(); 248 PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); 249 unsigned LineNo = PLoc.getLine(); 250 251 // First, if this diagnostic is not in the main file, print out the 252 // "included from" lines. 253 if (LastWarningLoc != PLoc.getIncludeLoc()) { 254 LastWarningLoc = PLoc.getIncludeLoc(); 255 PrintIncludeStack(LastWarningLoc, SM); 256 } 257 258 // Compute the column number. 259 if (ShowLocation) { 260 OS << PLoc.getFilename() << ':' << LineNo << ':'; 261 if (ShowColumn) 262 if (unsigned ColNo = PLoc.getColumn()) 263 OS << ColNo << ':'; 264 OS << ' '; 265 } 266 } 267 268 switch (Level) { 269 case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); 270 case Diagnostic::Note: OS << "note: "; break; 271 case Diagnostic::Warning: OS << "warning: "; break; 272 case Diagnostic::Error: OS << "error: "; break; 273 case Diagnostic::Fatal: OS << "fatal error: "; break; 274 } 275 276 llvm::SmallString<100> OutStr; 277 Info.FormatDiagnostic(OutStr); 278 OS.write(OutStr.begin(), OutStr.size()); 279 OS << '\n'; 280 281 // If caret diagnostics are enabled and we have location, we want to 282 // emit the caret. However, we only do this if the location moved 283 // from the last diagnostic, if the last diagnostic was a note that 284 // was part of a different warning or error diagnostic, or if the 285 // diagnostic has ranges. We don't want to emit the same caret 286 // multiple times if one loc has multiple diagnostics. 287 if (CaretDiagnostics && Info.getLocation().isValid() && 288 ((LastLoc != Info.getLocation()) || Info.getNumRanges() || 289 (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) || 290 Info.getNumCodeModificationHints())) { 291 // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. 292 LastLoc = Info.getLocation(); 293 LastCaretDiagnosticWasNote = (Level == Diagnostic::Note); 294 295 // Get the ranges into a local array we can hack on. 296 SourceRange Ranges[20]; 297 unsigned NumRanges = Info.getNumRanges(); 298 assert(NumRanges < 20 && "Out of space"); 299 for (unsigned i = 0; i != NumRanges; ++i) 300 Ranges[i] = Info.getRange(i); 301 302 unsigned NumHints = Info.getNumCodeModificationHints(); 303 for (unsigned idx = 0; idx < NumHints; ++idx) { 304 const CodeModificationHint &Hint = Info.getCodeModificationHint(idx); 305 if (Hint.RemoveRange.isValid()) { 306 assert(NumRanges < 20 && "Out of space"); 307 Ranges[NumRanges++] = Hint.RemoveRange; 308 } 309 } 310 311 EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), 312 Info.getCodeModificationHints(), 313 Info.getNumCodeModificationHints()); 314 } 315 316 OS.flush(); 317} 318