llvm-capstone/clang/lib/Frontend/TextDiagnosticPrinter.cpp
Ted Kremenek 9877f689f2 Implement -Wpedantic and --no-pedantic to complement -Weverything.
This patch introduces some magic in tablegen to create a "Pedantic" diagnostic
group which automagically includes all warnings that are extensions.  This
allows a user to suppress specific warnings traditionally under -pedantic used
an ordinary warning flag.  This also allows users to use #pragma to silence
specific -pedantic warnings, or promote them to errors, within blocks of text
(just like any other warning).

-Wpedantic is NOT an alias for -pedantic.  Instead, it provides another way
to (a) activate -pedantic warnings and (b) disable them.  Where they differ
is that -pedantic changes the behavior of the preprocessor slightly, whereas
-Wpedantic does not (it just turns on the warnings).

The magic in the tablegen diagnostic emitter has to do with computing the minimal
set of diagnostic groups and diagnostics that should go into -Wpedantic, as those
diagnostics that already members of groups that themselves are (transitively) members
of -Wpedantic do not need to be included in the Pedantic group directly.  I went
back and forth on whether or not to magically generate this group, and the invariant
was that we always wanted extension warnings to be included in -Wpedantic "some how",
but the bookkeeping would be very onerous to manage by hand.

-no-pedantic (and --no-pedantic) is included for completeness, and matches many of the
same kind of flags the compiler already supports.  It does what it says: cancels out
-pedantic.  One discrepancy is that if one specifies --no-pedantic and -Weverything or
-Wpedantic the pedantic warnings are still enabled (essentially the -W flags win).  We
can debate the correct behavior here.

Along the way, this patch nukes some code in TextDiagnosticPrinter.cpp and CXStoredDiagnostic.cpp
that determine whether to include the "-pedantic" flag in the warning output.  This is
no longer needed, as all extensions now have a -W flag.

This patch also significantly reduces the number of warnings not under flags from 229
to 158 (all extension warnings).  That's a 31% reduction.

llvm-svn: 159875
2012-07-06 23:07:31 +00:00

163 lines
6.2 KiB
C++

//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This diagnostic client prints out their diagnostic messages.
//
//===----------------------------------------------------------------------===//
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/ADT/SmallString.h"
#include <algorithm>
using namespace clang;
TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os,
const DiagnosticOptions &diags,
bool _OwnsOutputStream)
: OS(os), DiagOpts(&diags),
OwnsOutputStream(_OwnsOutputStream) {
}
TextDiagnosticPrinter::~TextDiagnosticPrinter() {
if (OwnsOutputStream)
delete &OS;
}
void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
const Preprocessor *PP) {
// Build the TextDiagnostic utility.
TextDiag.reset(new TextDiagnostic(OS, LO, *DiagOpts));
}
void TextDiagnosticPrinter::EndSourceFile() {
TextDiag.reset(0);
}
/// \brief Print any diagnostic option information to a raw_ostream.
///
/// 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,
DiagnosticsEngine::Level Level,
const Diagnostic &Info,
const DiagnosticOptions &DiagOpts) {
bool Started = false;
if (DiagOpts.ShowOptionNames) {
// Handle special cases for non-warnings early.
if (Info.getID() == diag::fatal_too_many_errors) {
OS << " [-ferror-limit=]";
return;
}
// 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.
if (Level == DiagnosticsEngine::Error &&
DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
!DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
OS << " [-Werror";
Started = true;
}
StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
if (!Opt.empty()) {
OS << (Started ? "," : " [") << "-W" << Opt;
Started = true;
}
}
// If the user wants to see category information, include it too.
if (DiagOpts.ShowCategories) {
unsigned DiagCategory =
DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
if (DiagCategory) {
OS << (Started ? "," : " [");
Started = true;
if (DiagOpts.ShowCategories == 1)
OS << DiagCategory;
else {
assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
}
}
}
if (Started)
OS << ']';
}
void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
const Diagnostic &Info) {
// Default implementation (Warnings/errors count).
DiagnosticConsumer::HandleDiagnostic(Level, Info);
// Render the diagnostic message into a temporary buffer eagerly. We'll use
// this later as we print out the diagnostic to the terminal.
SmallString<100> OutStr;
Info.FormatDiagnostic(OutStr);
llvm::raw_svector_ostream DiagMessageStream(OutStr);
printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts);
// Keeps track of the the starting position of the location
// 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 << ": ";
// Use a dedicated, simpler path for diagnostics without a valid location.
// 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.
if (!Info.getLocation().isValid()) {
TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors);
TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(),
OS.tell() - StartOfLocationInfo,
DiagOpts->MessageLength,
DiagOpts->ShowColors);
OS.flush();
return;
}
// 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");
assert(TextDiag && "Unexpected diagnostic outside source file processing");
TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(),
Info.getRanges(),
llvm::makeArrayRef(Info.getFixItHints(),
Info.getNumFixItHints()),
&Info.getSourceManager());
OS.flush();
}
DiagnosticConsumer *
TextDiagnosticPrinter::clone(DiagnosticsEngine &Diags) const {
return new TextDiagnosticPrinter(OS, *DiagOpts, /*OwnsOutputStream=*/false);
}