| Chris Lattner | 4b00965 | 2007-07-25 00:24:17 +0000 | [diff] [blame] | 1 | //===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===// | 
 | 2 | // | 
 | 3 | //                     The LLVM Compiler Infrastructure | 
 | 4 | // | 
 | 5 | // This file was developed by Bill Wendling and is distributed under the | 
 | 6 | // University of Illinois Open Source License. See LICENSE.TXT for details. | 
 | 7 | // | 
 | 8 | //===----------------------------------------------------------------------===// | 
 | 9 | // | 
 | 10 | // This diagnostic client prints out their diagnostic messages. | 
 | 11 | // | 
 | 12 | //===----------------------------------------------------------------------===// | 
 | 13 |  | 
 | 14 | #include "TextDiagnosticPrinter.h" | 
 | 15 | #include "clang/Basic/FileManager.h" | 
 | 16 | #include "clang/Basic/SourceManager.h" | 
 | 17 | #include "clang/Lex/HeaderSearch.h" | 
 | 18 | #include "clang/Lex/Lexer.h" | 
 | 19 | #include "llvm/Support/CommandLine.h" | 
 | 20 | #include "llvm/Support/MemoryBuffer.h" | 
 | 21 | #include <iostream> | 
 | 22 | #include <string> | 
 | 23 | using namespace clang; | 
 | 24 |  | 
 | 25 | static llvm::cl::opt<bool> | 
 | 26 | NoShowColumn("fno-show-column", | 
 | 27 |              llvm::cl::desc("Do not include column number on diagnostics")); | 
 | 28 | static llvm::cl::opt<bool> | 
 | 29 | NoCaretDiagnostics("fno-caret-diagnostics", | 
 | 30 |                    llvm::cl::desc("Do not include source line and caret with" | 
 | 31 |                                   " diagnostics")); | 
 | 32 |  | 
 | 33 | void TextDiagnosticPrinter:: | 
 | 34 | PrintIncludeStack(SourceLocation Pos) { | 
 | 35 |   if (Pos.isInvalid()) return; | 
 | 36 |  | 
 | 37 |   Pos = SourceMgr.getLogicalLoc(Pos); | 
 | 38 |  | 
 | 39 |   // Print out the other include frames first. | 
 | 40 |   PrintIncludeStack(SourceMgr.getIncludeLoc(Pos)); | 
 | 41 |   unsigned LineNo = SourceMgr.getLineNumber(Pos); | 
 | 42 |    | 
 | 43 |   std::cerr << "In file included from " << SourceMgr.getSourceName(Pos) | 
 | 44 |             << ":" << LineNo << ":\n"; | 
 | 45 | } | 
 | 46 |  | 
 | 47 | /// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s) | 
 | 48 | /// any characters in LineNo that intersect the SourceRange. | 
 | 49 | void TextDiagnosticPrinter::HighlightRange(const SourceRange &R,  | 
 | 50 |                                            unsigned LineNo, | 
 | 51 |                                            std::string &CaratLine, | 
 | 52 |                                            const std::string &SourceLine) { | 
 | 53 |   assert(CaratLine.size() == SourceLine.size() && | 
 | 54 |          "Expect a correspondence between source and carat line!"); | 
 | 55 |   if (!R.isValid()) return; | 
 | 56 |  | 
 | 57 |   unsigned StartLineNo = SourceMgr.getLogicalLineNumber(R.Begin()); | 
 | 58 |   if (StartLineNo > LineNo) return;  // No intersection. | 
 | 59 |    | 
 | 60 |   unsigned EndLineNo = SourceMgr.getLogicalLineNumber(R.End()); | 
 | 61 |   if (EndLineNo < LineNo) return;  // No intersection. | 
 | 62 |    | 
 | 63 |   // Compute the column number of the start. | 
 | 64 |   unsigned StartColNo = 0; | 
 | 65 |   if (StartLineNo == LineNo) { | 
 | 66 |     StartColNo = SourceMgr.getLogicalColumnNumber(R.Begin()); | 
 | 67 |     if (StartColNo) --StartColNo;  // Zero base the col #. | 
 | 68 |   } | 
 | 69 |  | 
 | 70 |   // Pick the first non-whitespace column. | 
 | 71 |   while (StartColNo < SourceLine.size() && | 
 | 72 |          (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t')) | 
 | 73 |     ++StartColNo; | 
 | 74 |    | 
 | 75 |   // Compute the column number of the end. | 
 | 76 |   unsigned EndColNo = CaratLine.size(); | 
 | 77 |   if (EndLineNo == LineNo) { | 
 | 78 |     EndColNo = SourceMgr.getLogicalColumnNumber(R.End()); | 
 | 79 |     if (EndColNo) { | 
 | 80 |       --EndColNo;  // Zero base the col #. | 
 | 81 |        | 
 | 82 |       // Add in the length of the token, so that we cover multi-char tokens. | 
 | 83 |       EndColNo += GetTokenLength(R.End()); | 
 | 84 |     } else { | 
 | 85 |       EndColNo = CaratLine.size(); | 
 | 86 |     } | 
 | 87 |   } | 
 | 88 |    | 
 | 89 |   // Pick the last non-whitespace column. | 
 | 90 |   while (EndColNo-1 && | 
 | 91 |          (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t')) | 
 | 92 |     --EndColNo; | 
 | 93 |    | 
 | 94 |   // Fill the range with ~'s. | 
 | 95 |   assert(StartColNo <= EndColNo && "Invalid range!"); | 
 | 96 |   for (unsigned i = StartColNo; i != EndColNo; ++i) | 
 | 97 |     CaratLine[i] = '~'; | 
 | 98 | } | 
 | 99 |  | 
 | 100 | /// GetTokenLength - Given the source location of a token, determine its length. | 
 | 101 | /// This is a fully general function that uses a lexer to relex the token. | 
 | 102 | unsigned TextDiagnosticPrinter::GetTokenLength(SourceLocation Loc) { | 
 | 103 |   // If this comes from a macro expansion, we really do want the macro name, not | 
 | 104 |   // the token this macro expanded to. | 
 | 105 |   Loc = SourceMgr.getLogicalLoc(Loc); | 
 | 106 |   const char *StrData = SourceMgr.getCharacterData(Loc); | 
 | 107 |    | 
 | 108 |   // TODO: this could be special cased for common tokens like identifiers, ')', | 
 | 109 |   // etc to make this faster, if it mattered.  This could use  | 
 | 110 |   // Lexer::isObviouslySimpleCharacter for example. | 
 | 111 |    | 
 | 112 |   // Create a lexer starting at the beginning of this token. | 
 | 113 |   Lexer TheLexer(Loc, *ThePreprocessor, StrData); | 
 | 114 |   Token TheTok; | 
 | 115 |   TheLexer.LexRawToken(TheTok); | 
 | 116 |   return TheTok.getLength(); | 
 | 117 | } | 
 | 118 |  | 
 | 119 | void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level,  | 
 | 120 |                                              SourceLocation Pos, | 
 | 121 |                                              diag::kind ID, | 
 | 122 |                                              const std::string *Strs, | 
 | 123 |                                              unsigned NumStrs, | 
 | 124 |                                              const SourceRange *Ranges, | 
 | 125 |                                              unsigned NumRanges) { | 
 | 126 |   unsigned LineNo = 0, ColNo = 0; | 
 | 127 |   const char *LineStart = 0, *LineEnd = 0; | 
 | 128 |    | 
 | 129 |   if (Pos.isValid()) { | 
 | 130 |     SourceLocation LPos = SourceMgr.getLogicalLoc(Pos); | 
 | 131 |     LineNo = SourceMgr.getLineNumber(LPos); | 
 | 132 |      | 
 | 133 |     // First, if this diagnostic is not in the main file, print out the | 
 | 134 |     // "included from" lines. | 
 | 135 |     if (LastWarningLoc != SourceMgr.getIncludeLoc(LPos)) { | 
 | 136 |       LastWarningLoc = SourceMgr.getIncludeLoc(LPos); | 
 | 137 |       PrintIncludeStack(LastWarningLoc); | 
 | 138 |     } | 
 | 139 |    | 
 | 140 |     // Compute the column number.  Rewind from the current position to the start | 
 | 141 |     // of the line. | 
 | 142 |     ColNo = SourceMgr.getColumnNumber(LPos); | 
 | 143 |     const char *TokLogicalPtr = SourceMgr.getCharacterData(LPos); | 
 | 144 |     LineStart = TokLogicalPtr-ColNo+1;  // Column # is 1-based | 
 | 145 |    | 
 | 146 |     // Compute the line end.  Scan forward from the error position to the end of | 
 | 147 |     // the line. | 
 | 148 |     const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(LPos.getFileID()); | 
 | 149 |     const char *BufEnd = Buffer->getBufferEnd(); | 
 | 150 |     LineEnd = TokLogicalPtr; | 
 | 151 |     while (LineEnd != BufEnd &&  | 
 | 152 |            *LineEnd != '\n' && *LineEnd != '\r') | 
 | 153 |       ++LineEnd; | 
 | 154 |    | 
 | 155 |     std::cerr << Buffer->getBufferIdentifier()  | 
 | 156 |               << ":" << LineNo << ":"; | 
 | 157 |     if (ColNo && !NoShowColumn)  | 
 | 158 |       std::cerr << ColNo << ":"; | 
 | 159 |     std::cerr << " "; | 
 | 160 |   } | 
 | 161 |    | 
 | 162 |   switch (Level) { | 
 | 163 |   default: assert(0 && "Unknown diagnostic type!"); | 
 | 164 |   case Diagnostic::Note:    std::cerr << "note: "; break; | 
 | 165 |   case Diagnostic::Warning: std::cerr << "warning: "; break; | 
 | 166 |   case Diagnostic::Error:   std::cerr << "error: "; break; | 
 | 167 |   case Diagnostic::Fatal:   std::cerr << "fatal error: "; break; | 
 | 168 |   case Diagnostic::Sorry:   std::cerr << "sorry, unimplemented: "; | 
 | 169 |     break; | 
 | 170 |   } | 
 | 171 |    | 
 | 172 |   std::cerr << FormatDiagnostic(Level, ID, Strs, NumStrs) << "\n"; | 
 | 173 |    | 
 | 174 |   if (!NoCaretDiagnostics && Pos.isValid()) { | 
 | 175 |     // Get the line of the source file. | 
 | 176 |     std::string SourceLine(LineStart, LineEnd); | 
 | 177 |      | 
 | 178 |     // Create a line for the carat that is filled with spaces that is the same | 
 | 179 |     // length as the line of source code. | 
 | 180 |     std::string CaratLine(LineEnd-LineStart, ' '); | 
 | 181 |      | 
 | 182 |     // Highlight all of the characters covered by Ranges with ~ characters. | 
 | 183 |     for (unsigned i = 0; i != NumRanges; ++i) | 
 | 184 |       HighlightRange(Ranges[i], LineNo, CaratLine, SourceLine); | 
 | 185 |      | 
 | 186 |     // Next, insert the carat itself. | 
 | 187 |     if (ColNo-1 < CaratLine.size()) | 
 | 188 |       CaratLine[ColNo-1] = '^'; | 
 | 189 |     else | 
 | 190 |       CaratLine.push_back('^'); | 
 | 191 |      | 
 | 192 |     // Scan the source line, looking for tabs.  If we find any, manually expand | 
 | 193 |     // them to 8 characters and update the CaratLine to match. | 
 | 194 |     for (unsigned i = 0; i != SourceLine.size(); ++i) { | 
 | 195 |       if (SourceLine[i] != '\t') continue; | 
 | 196 |        | 
 | 197 |       // Replace this tab with at least one space. | 
 | 198 |       SourceLine[i] = ' '; | 
 | 199 |        | 
 | 200 |       // Compute the number of spaces we need to insert. | 
 | 201 |       unsigned NumSpaces = ((i+8)&~7) - (i+1); | 
 | 202 |       assert(NumSpaces < 8 && "Invalid computation of space amt"); | 
 | 203 |        | 
 | 204 |       // Insert spaces into the SourceLine. | 
 | 205 |       SourceLine.insert(i+1, NumSpaces, ' '); | 
 | 206 |        | 
 | 207 |       // Insert spaces or ~'s into CaratLine. | 
 | 208 |       CaratLine.insert(i+1, NumSpaces, CaratLine[i] == '~' ? '~' : ' '); | 
 | 209 |     } | 
 | 210 |      | 
 | 211 |     // Finally, remove any blank spaces from the end of CaratLine. | 
 | 212 |     while (CaratLine[CaratLine.size()-1] == ' ') | 
 | 213 |       CaratLine.erase(CaratLine.end()-1); | 
 | 214 |      | 
 | 215 |     // Emit what we have computed. | 
 | 216 |     std::cerr << SourceLine << "\n"; | 
 | 217 |     std::cerr << CaratLine << "\n"; | 
 | 218 |   } | 
 | 219 | } |