2007-06-07 17:34:54 +08:00
|
|
|
//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2007-06-07 17:34:54 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This diagnostic client prints out their diagnostic messages.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-03-02 14:16:29 +08:00
|
|
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
2012-10-24 06:26:28 +08:00
|
|
|
#include "clang/Basic/DiagnosticOptions.h"
|
2007-06-07 17:34:54 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2011-10-16 07:43:53 +08:00
|
|
|
#include "clang/Frontend/TextDiagnostic.h"
|
2007-06-07 17:34:54 +08:00
|
|
|
#include "clang/Lex/Lexer.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2008-11-19 14:56:25 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
Introduce code modification hints into the diagnostics system. When we
know how to recover from an error, we can attach a hint to the
diagnostic that states how to modify the code, which can be one of:
- Insert some new code (a text string) at a particular source
location
- Remove the code within a given range
- Replace the code within a given range with some new code (a text
string)
Right now, we use these hints to annotate diagnostic information. For
example, if one uses the '>>' in a template argument in C++98, as in
this code:
template<int I> class B { };
B<1000 >> 2> *b1;
we'll warn that the behavior will change in C++0x. The fix is to
insert parenthese, so we use code insertion annotations to illustrate
where the parentheses go:
test.cpp:10:10: warning: use of right-shift operator ('>>') in template
argument will require parentheses in C++0x
B<1000 >> 2> *b1;
^
( )
Use of these annotations is partially implemented for HTML
diagnostics, but it's not (yet) producing valid HTML, which may be
related to PR2386, so it has been #if 0'd out.
In this future, we could consider hooking this mechanism up to the
rewriter to actually try to fix these problems during compilation (or,
after a compilation whose only errors have fixes). For now, however, I
suggest that we use these code modification hints whenever we can, so
that we get better diagnostics now and will have better coverage when
we find better ways to use this information.
This also fixes PR3410 by placing the complaint about missing tokens
just after the previous token (rather than at the location of the next
token).
llvm-svn: 65570
2009-02-27 05:00:50 +08:00
|
|
|
#include <algorithm>
|
2007-06-07 17:34:54 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2011-07-23 18:55:15 +08:00
|
|
|
TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
|
2012-10-24 06:26:28 +08:00
|
|
|
DiagnosticOptions *diags,
|
2009-11-11 17:38:24 +08:00
|
|
|
bool _OwnsOutputStream)
|
2012-10-24 06:26:28 +08:00
|
|
|
: OS(os), DiagOpts(diags),
|
2009-11-11 17:38:24 +08:00
|
|
|
OwnsOutputStream(_OwnsOutputStream) {
|
|
|
|
}
|
|
|
|
|
|
|
|
TextDiagnosticPrinter::~TextDiagnosticPrinter() {
|
|
|
|
if (OwnsOutputStream)
|
|
|
|
delete &OS;
|
2009-11-04 14:24:30 +08:00
|
|
|
}
|
|
|
|
|
2011-10-16 10:57:39 +08:00
|
|
|
void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
|
|
|
|
const Preprocessor *PP) {
|
2012-05-10 13:03:45 +08:00
|
|
|
// Build the TextDiagnostic utility.
|
2012-10-24 06:26:28 +08:00
|
|
|
TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts));
|
2011-10-16 10:57:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextDiagnosticPrinter::EndSourceFile() {
|
2014-07-19 09:06:45 +08:00
|
|
|
TextDiag.reset();
|
2011-10-16 10:57:39 +08:00
|
|
|
}
|
|
|
|
|
2018-05-09 09:00:01 +08:00
|
|
|
/// Print any diagnostic option information to a raw_ostream.
|
2011-09-26 09:30:09 +08:00
|
|
|
///
|
|
|
|
/// This implements all of the logic for adding diagnostic options to a message
|
|
|
|
/// (via OS). Each relevant option is comma separated and all are enclosed in
|
|
|
|
/// the standard bracketing: " [...]".
|
|
|
|
static void printDiagnosticOptions(raw_ostream &OS,
|
2011-09-26 08:44:09 +08:00
|
|
|
DiagnosticsEngine::Level Level,
|
2011-09-26 09:18:08 +08:00
|
|
|
const Diagnostic &Info,
|
2011-09-26 08:44:09 +08:00
|
|
|
const DiagnosticOptions &DiagOpts) {
|
2011-09-26 09:21:58 +08:00
|
|
|
bool Started = false;
|
2011-09-26 08:44:09 +08:00
|
|
|
if (DiagOpts.ShowOptionNames) {
|
2011-09-26 09:21:58 +08:00
|
|
|
// Handle special cases for non-warnings early.
|
|
|
|
if (Info.getID() == diag::fatal_too_many_errors) {
|
|
|
|
OS << " [-ferror-limit=]";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-29 09:01:08 +08:00
|
|
|
// The code below is somewhat fragile because we are essentially trying to
|
|
|
|
// report to the user what happened by inferring what the diagnostic engine
|
|
|
|
// did. Eventually it might make more sense to have the diagnostic engine
|
|
|
|
// include some "why" information in the diagnostic.
|
|
|
|
|
|
|
|
// If this is a warning which has been mapped to an error by the user (as
|
|
|
|
// inferred by checking whether the default mapping is to an error) then
|
|
|
|
// flag it as such. Note that diagnostics could also have been mapped by a
|
|
|
|
// pragma, but we don't currently have a way to distinguish this.
|
2011-09-26 07:23:43 +08:00
|
|
|
if (Level == DiagnosticsEngine::Error &&
|
2011-09-29 09:01:08 +08:00
|
|
|
DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
|
|
|
|
!DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
|
|
|
|
OS << " [-Werror";
|
|
|
|
Started = true;
|
2011-09-26 09:21:58 +08:00
|
|
|
}
|
|
|
|
|
2011-07-23 18:55:15 +08:00
|
|
|
StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
|
2011-05-25 13:05:01 +08:00
|
|
|
if (!Opt.empty()) {
|
2014-04-17 00:54:24 +08:00
|
|
|
OS << (Started ? "," : " [")
|
2014-06-22 18:08:06 +08:00
|
|
|
<< (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
|
2014-07-09 09:37:24 +08:00
|
|
|
StringRef OptValue = Info.getDiags()->getFlagValue();
|
2014-04-22 07:16:03 +08:00
|
|
|
if (!OptValue.empty())
|
|
|
|
OS << "=" << OptValue;
|
2011-09-26 09:21:58 +08:00
|
|
|
Started = true;
|
Implement -fmessage-length=N, which word-wraps diagnostics to N columns.
Also, put a line of whitespace between the diagnostic and the source
code/caret line when the start of the actual source code text lines up
(or nearly lines up) with the most recent line of the diagnostic. For
example, here it's okay for the last line of the diagnostic to be
(vertically) next to the source line, because there is horizontal
whitespace to separate them:
decl-expr-ambiguity.cpp:12:16: error: function-style cast to a builtin
type can only take one argument
typeof(int)(a,5)<<a;
However, here is a case where we need the vertical separation (since
there is no horizontal separation):
message-length.c:10:46: warning: incompatible pointer types initializing 'void
(int, float, char, float)', expected 'int (*)(int, float, short,
float)'
int (*fp1)(int, float, short, float) = f;
This is part one of <rdar://problem/6711348>.
llvm-svn: 70578
2009-05-02 05:53:04 +08:00
|
|
|
}
|
2010-02-17 02:29:31 +08:00
|
|
|
}
|
2011-09-26 09:21:58 +08:00
|
|
|
|
2010-05-05 05:13:21 +08:00
|
|
|
// If the user wants to see category information, include it too.
|
2011-09-26 09:21:58 +08:00
|
|
|
if (DiagOpts.ShowCategories) {
|
|
|
|
unsigned DiagCategory =
|
|
|
|
DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
2010-05-05 05:13:21 +08:00
|
|
|
if (DiagCategory) {
|
2011-09-26 09:21:58 +08:00
|
|
|
OS << (Started ? "," : " [");
|
2011-09-26 10:14:13 +08:00
|
|
|
Started = true;
|
2011-09-26 08:44:09 +08:00
|
|
|
if (DiagOpts.ShowCategories == 1)
|
2011-09-26 10:14:13 +08:00
|
|
|
OS << DiagCategory;
|
2010-05-05 05:55:25 +08:00
|
|
|
else {
|
2011-09-26 08:44:09 +08:00
|
|
|
assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
|
|
|
|
OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
|
2010-05-05 05:55:25 +08:00
|
|
|
}
|
2010-05-05 05:13:21 +08:00
|
|
|
}
|
|
|
|
}
|
2011-09-26 10:14:13 +08:00
|
|
|
if (Started)
|
|
|
|
OS << ']';
|
2011-09-26 08:44:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
|
2011-09-26 09:18:08 +08:00
|
|
|
const Diagnostic &Info) {
|
2011-09-26 08:44:09 +08:00
|
|
|
// Default implementation (Warnings/errors count).
|
|
|
|
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
|
|
|
|
2011-09-26 19:25:30 +08:00
|
|
|
// Render the diagnostic message into a temporary buffer eagerly. We'll use
|
|
|
|
// this later as we print out the diagnostic to the terminal.
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<100> OutStr;
|
2011-09-26 19:25:30 +08:00
|
|
|
Info.FormatDiagnostic(OutStr);
|
|
|
|
|
|
|
|
llvm::raw_svector_ostream DiagMessageStream(OutStr);
|
|
|
|
printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
|
|
|
|
|
2012-07-23 16:59:39 +08:00
|
|
|
// Keeps track of the starting position of the location
|
2011-09-26 08:44:09 +08:00
|
|
|
// information (e.g., "foo.c:10:4:") that precedes the error
|
|
|
|
// message. We use this information to determine how long the
|
|
|
|
// file+line+column number prefix is.
|
|
|
|
uint64_t StartOfLocationInfo = OS.tell();
|
|
|
|
|
|
|
|
if (!Prefix.empty())
|
|
|
|
OS << Prefix << ": ";
|
|
|
|
|
2011-09-26 19:25:30 +08:00
|
|
|
// Use a dedicated, simpler path for diagnostics without a valid location.
|
2011-09-26 19:38:46 +08:00
|
|
|
// This is important as if the location is missing, we may be emitting
|
|
|
|
// diagnostics in a context that lacks language options, a source manager, or
|
|
|
|
// other infrastructure necessary when emitting more rich diagnostics.
|
2011-09-26 19:25:30 +08:00
|
|
|
if (!Info.getLocation().isValid()) {
|
2013-09-24 08:08:55 +08:00
|
|
|
TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors,
|
|
|
|
DiagOpts->CLFallbackMode);
|
2011-10-16 06:57:29 +08:00
|
|
|
TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
|
|
|
|
OS.tell() - StartOfLocationInfo,
|
|
|
|
DiagOpts->MessageLength,
|
|
|
|
DiagOpts->ShowColors);
|
2011-09-26 19:25:30 +08:00
|
|
|
OS.flush();
|
|
|
|
return;
|
2011-09-26 08:44:09 +08:00
|
|
|
}
|
|
|
|
|
2011-09-26 19:38:46 +08:00
|
|
|
// Assert that the rest of our infrastructure is setup properly.
|
|
|
|
assert(DiagOpts && "Unexpected diagnostic without options set");
|
|
|
|
assert(Info.hasSourceManager() &&
|
|
|
|
"Unexpected diagnostic with no source manager");
|
2012-05-10 13:03:45 +08:00
|
|
|
assert(TextDiag && "Unexpected diagnostic outside source file processing");
|
2011-10-16 10:57:39 +08:00
|
|
|
|
2017-06-27 17:50:38 +08:00
|
|
|
TextDiag->emitDiagnostic(
|
|
|
|
FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level,
|
|
|
|
DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints());
|
2009-09-08 07:07:56 +08:00
|
|
|
|
2008-11-19 14:56:25 +08:00
|
|
|
OS.flush();
|
2007-06-07 17:34:54 +08:00
|
|
|
}
|