From: Chris Lattner Date: Fri, 13 Mar 2009 01:08:23 +0000 (+0000) Subject: implement a new -fprint-source-range-info option, which X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1fbee5d02860d3c72a713d3dfb4179fa9f400cb3;p=clang implement a new -fprint-source-range-info option, which defaults to off. When enabled, it emits range info along with the file/line/col information for a diagnostic. This allows tools that textually parse the output of clang to know where the ranges are, even if they span multiple lines. For example, with: $ clang exprs.c -fprint-source-range-info We now produce: exprs.c:21:11:{21:12-21:13}: warning: use of unary operator that may be intended as compound assignment (+=) var =+ 5; // expected-warning {{use of unary operator that may be intended as compound assignment (+=)}} ^~ exprs.c:22:11:{22:12-22:13}: warning: use of unary operator that may be intended as compound assignment (-=) var =- 5; // expected-warning {{use of unary operator that may be intended as compound assignment (-=)}} ^~ exprs.c:36:13:{36:3-36:12}: error: assignment to cast is illegal, lvalue casts are not supported (float*)X = P; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}} ~~~~~~~~~ ^ exprs.c:41:4:{41:3-41:4}: error: called object type 'int' is not a function or function pointer X(); // expected-error {{called object type 'int' is not a function or function pointer}} ~^ exprs.c:45:15:{45:8-45:14}{45:17-45:24}: error: invalid operands to binary expression ('int *' and '_Complex float') P = (P-42) + Gamma*4; // expected-error {{invalid operands to binary expression ('int *' and '_Complex float')}} ~~~~~~ ^ ~~~~~~~ exprs.c:61:7:{61:16-61:22}: error: invalid application of '__alignof' to bitfield R = __alignof(P->x); // expected-error {{invalid application of '__alignof' to bitfield}} expected-warning {{extension used}} ^ ~~~~~~ Note the range info after the column in the initial diagnostic. This is obviously really annoying if you're not a tool parsing the output of clang, which is why it is off by default. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66862 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/clang.cpp b/Driver/clang.cpp index a0195c629a..171e4ecb31 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -202,6 +202,10 @@ NoCaretDiagnostics("fno-caret-diagnostics", llvm::cl::desc("Do not include source line and caret with" " diagnostics")); +static llvm::cl::opt +PrintSourceRangeInfo("fprint-source-range-info", + llvm::cl::desc("Print source range spans in numeric form")); + //===----------------------------------------------------------------------===// // C++ Visualization. @@ -799,6 +803,8 @@ static std::string CreateTargetTriple() { // -A... - Play with #assertions // -undef - Undefine all predefined macros +// FIXME: -imacros + static llvm::cl::list D_macros("D", llvm::cl::value_desc("macro"), llvm::cl::Prefix, llvm::cl::desc("Predefine the specified macro")); @@ -810,7 +816,6 @@ static llvm::cl::list ImplicitIncludes("include", llvm::cl::value_desc("file"), llvm::cl::desc("Include file before parsing")); - // Append a #define line to Buf for Macro. Macro should be of the form XXX, // in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit // "#define XXX Y z W". To get a #define with no value, use "XXX=". @@ -910,7 +915,6 @@ static bool InitializePreprocessor(Preprocessor &PP, // FIXME: -nostdinc,-nostdinc++ // FIXME: -imultilib // -// FIXME: -imacros static llvm::cl::opt nostdinc("nostdinc", llvm::cl::desc("Disable standard #include directories")); @@ -1496,7 +1500,8 @@ int main(int argc, char **argv) { TextDiagClient = new TextDiagnosticPrinter(llvm::errs(), !NoShowColumn, !NoCaretDiagnostics, - !NoShowLocation); + !NoShowLocation, + PrintSourceRangeInfo); } else { // When checking diagnostics, just buffer them up. TextDiagClient = new TextDiagnosticBuffer(); diff --git a/include/clang/Frontend/TextDiagnosticPrinter.h b/include/clang/Frontend/TextDiagnosticPrinter.h index eeff279513..0f08a95283 100644 --- a/include/clang/Frontend/TextDiagnosticPrinter.h +++ b/include/clang/Frontend/TextDiagnosticPrinter.h @@ -33,11 +33,14 @@ class TextDiagnosticPrinter : public DiagnosticClient { bool ShowColumn; bool CaretDiagnostics; bool ShowLocation; + bool PrintRangeInfo; public: TextDiagnosticPrinter(llvm::raw_ostream &os, bool showColumn = true, - bool caretDiagnistics = true, bool showLocation = true) + bool caretDiagnistics = true, bool showLocation = true, + bool printRangeInfo = true) : LastCaretDiagnosticWasNote(false), OS(os), ShowColumn(showColumn), - CaretDiagnostics(caretDiagnistics), ShowLocation(showLocation) {} + CaretDiagnostics(caretDiagnistics), ShowLocation(showLocation), + PrintRangeInfo(printRangeInfo) {} void PrintIncludeStack(SourceLocation Loc, const SourceManager &SM); diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index 160b5cfae1..f1555dbff3 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -261,6 +261,38 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, if (ShowColumn) if (unsigned ColNo = PLoc.getColumn()) OS << ColNo << ':'; + + if (PrintRangeInfo && Info.getNumRanges()) { + FileID CaretFileID = + SM.getFileID(SM.getInstantiationLoc(Info.getLocation())); + bool PrintedRange = false; + + for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) { + SourceLocation B = Info.getRange(i).getBegin(); + SourceLocation E = Info.getRange(i).getEnd(); + std::pair BInfo=SM.getDecomposedInstantiationLoc(B); + + E = SM.getInstantiationLoc(E); + std::pair EInfo = SM.getDecomposedLoc(E); + + // If the start or end of the range is in another file, just discard + // it. + if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) + continue; + + // Add in the length of the token, so that we cover multi-char tokens. + unsigned TokSize = Lexer::MeasureTokenLength(E, SM); + + OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' + << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' + << SM.getLineNumber(EInfo.first, EInfo.second) << ':' + << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}'; + PrintedRange = true; + } + + if (PrintedRange) + OS << ':'; + } OS << ' '; } }