TextDiagnosticPrinter.cpp 8.1 KB

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