From: Jordan Rose Date: Sun, 24 Jun 2012 00:07:45 +0000 (+0000) Subject: [diagtool] Add a new "tree" command to shows warnings activated by a flag. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e7427636767501903cfa51ccecafa7a4795a23c2;p=clang [diagtool] Add a new "tree" command to shows warnings activated by a flag. % diagtool tree -Wunused-value -Wunused-value -Wunused-comparison warn_unused_comparison -Wunused-result warn_unused_result warn_unused_call warn_unused_container_subscript_expr warn_unused_expr warn_unused_property_expr warn_unused_voidptr git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159093 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/Misc/warning-flags-tree.c b/test/Misc/warning-flags-tree.c new file mode 100644 index 0000000000..a64e942f20 --- /dev/null +++ b/test/Misc/warning-flags-tree.c @@ -0,0 +1,56 @@ +// RUN: diagtool tree | FileCheck -strict-whitespace %s +// RUN: diagtool tree -Weverything | FileCheck -strict-whitespace %s +// RUN: diagtool tree everything | FileCheck -strict-whitespace %s +// +// These three ways of running diagtool tree are the same: +// they produce a tree for every top-level diagnostic flag. +// Just check a few to make sure we're actually showing more than one group. +// +// CHECK: -W +// CHECK: -Wextra +// CHECK: -Wmissing-field-initializers +// CHECK: warn_missing_field_initializers +// CHECK: -Wall +// CHECK: -Wmost + +// These flags are currently unimplemented; test that we output them anyway. +// CHECK: -Wstrict-aliasing +// CHECK-NEXT: -Wstrict-aliasing=0 +// CHECK-NEXT: -Wstrict-aliasing=1 +// CHECK-NEXT: -Wstrict-aliasing=2 +// CHECK: -Wstrict-overflow +// CHECK-NEXT: -Wstrict-overflow=0 +// CHECK-NEXT: -Wstrict-overflow=1 +// CHECK-NEXT: -Wstrict-overflow=2 +// CHECK-NEXT: -Wstrict-overflow=3 +// CHECK-NEXT: -Wstrict-overflow=4 +// CHECK-NEXT: -Wstrict-overflow=5 + + +// RUN: not diagtool tree -Wthis-is-not-a-valid-flag + + +// RUN: diagtool tree -Wgnu | FileCheck -strict-whitespace -check-prefix CHECK-GNU %s +// CHECK-GNU: -Wgnu +// CHECK-GNU: -Wgnu-designator +// CHECK-GNU: ext_gnu_array_range +// CHECK-GNU: ext_gnu_missing_equal_designator +// CHECK-GNU: ext_gnu_old_style_field_designator +// CHECK-GNU: -Wvla +// CHECK-GNU: ext_vla +// CHECK-GNU: ext_array_init_copy +// CHECK-GNU: ext_empty_struct_union +// CHECK-GNU: ext_expr_not_ice +// There are more GNU extensions but we don't need to check them all. + +// RUN: diagtool tree --flags-only -Wgnu | FileCheck -check-prefix CHECK-FLAGS-ONLY %s +// CHECK-FLAGS-ONLY: -Wgnu +// CHECK-FLAGS-ONLY: -Wgnu-designator +// CHECK-FLAGS-ONLY-NOT: ext_gnu_array_range +// CHECK-FLAGS-ONLY-NOT: ext_gnu_missing_equal_designator +// CHECK-FLAGS-ONLY-NOT: ext_gnu_old_style_field_designator +// CHECK-FLAGS-ONLY: -Wvla +// CHECK-FLAGS-ONLY-NOT: ext_vla +// CHECK-FLAGS-ONLY-NOT: ext_array_init_copy +// CHECK-FLAGS-ONLY-NOT: ext_empty_struct_union +// CHECK-FLAGS-ONLY-NOT: ext_expr_not_ice diff --git a/tools/diagtool/CMakeLists.txt b/tools/diagtool/CMakeLists.txt index 6e2ae2d8cf..77f5f381be 100644 --- a/tools/diagtool/CMakeLists.txt +++ b/tools/diagtool/CMakeLists.txt @@ -8,6 +8,7 @@ add_clang_executable(diagtool DiagnosticNames.cpp ListWarnings.cpp ShowEnabledWarnings.cpp + TreeView.cpp ) add_dependencies(diagtool diff --git a/tools/diagtool/DiagnosticNames.cpp b/tools/diagtool/DiagnosticNames.cpp index dd86c287ac..9995e3b20f 100644 --- a/tools/diagtool/DiagnosticNames.cpp +++ b/tools/diagtool/DiagnosticNames.cpp @@ -9,17 +9,69 @@ #include "DiagnosticNames.h" #include "clang/Basic/AllDiagnostics.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; +using namespace diagtool; -const diagtool::DiagnosticRecord diagtool::BuiltinDiagnostics[] = { +static const DiagnosticRecord BuiltinDiagnosticsByName[] = { #define DIAG_NAME_INDEX(ENUM) { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, #include "clang/Basic/DiagnosticIndexName.inc" #undef DIAG_NAME_INDEX - { 0, 0, 0 } }; -const size_t diagtool::BuiltinDiagnosticsCount = - sizeof(diagtool::BuiltinDiagnostics) / - sizeof(diagtool::BuiltinDiagnostics[0]) - 1; +llvm::ArrayRef diagtool::getBuiltinDiagnosticsByName() { + return BuiltinDiagnosticsByName; +} + +// FIXME: Is it worth having two tables, especially when this one can get +// out of sync easily? +static const DiagnosticRecord BuiltinDiagnosticsByID[] = { +#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP, \ + SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER, \ + CATEGORY) \ + { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#undef DIAG +}; + +static bool orderByID(const DiagnosticRecord &Left, + const DiagnosticRecord &Right) { + return Left.DiagID < Right.DiagID; +} + +const DiagnosticRecord &diagtool::getDiagnosticForID(short DiagID) { + DiagnosticRecord Key = {0, DiagID, 0}; + + const DiagnosticRecord *Result = + std::lower_bound(BuiltinDiagnosticsByID, + llvm::array_endof(BuiltinDiagnosticsByID), + Key, orderByID); + assert(Result && "diagnostic not found; table may be out of date"); + return *Result; +} + + +#define GET_DIAG_ARRAYS +#include "clang/Basic/DiagnosticGroups.inc" +#undef GET_DIAG_ARRAYS + +// Second the table of options, sorted by name for fast binary lookup. +static const GroupRecord OptionTable[] = { +#define GET_DIAG_TABLE +#include "clang/Basic/DiagnosticGroups.inc" +#undef GET_DIAG_TABLE +}; + +llvm::ArrayRef diagtool::getDiagnosticGroups() { + return OptionTable; +} diff --git a/tools/diagtool/DiagnosticNames.h b/tools/diagtool/DiagnosticNames.h index ac1934aac1..9a731587e5 100644 --- a/tools/diagtool/DiagnosticNames.h +++ b/tools/diagtool/DiagnosticNames.h @@ -7,22 +7,122 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" namespace diagtool { + struct DiagnosticRecord { const char *NameStr; - unsigned short DiagID; + short DiagID; uint8_t NameLen; llvm::StringRef getName() const { return llvm::StringRef(NameStr, NameLen); } + + bool operator<(const DiagnosticRecord &Other) const { + return getName() < Other.getName(); + } }; - extern const DiagnosticRecord BuiltinDiagnostics[]; - extern const size_t BuiltinDiagnosticsCount; + /// \brief Get every diagnostic in the system, sorted by name. + llvm::ArrayRef getBuiltinDiagnosticsByName(); + + /// \brief Get a diagnostic by its ID. + const DiagnosticRecord &getDiagnosticForID(short DiagID); + + + struct GroupRecord { + // Be safe with the size of 'NameLen' because we don't statically check if + // the size will fit in the field; the struct size won't decrease with a + // shorter type anyway. + size_t NameLen; + const char *NameStr; + const short *Members; + const short *SubGroups; + + llvm::StringRef getName() const { + return llvm::StringRef(NameStr, NameLen); + } + + template + class group_iterator { + const short *CurrentID; + + friend struct GroupRecord; + group_iterator(const short *Start) : CurrentID(Start) { + if (CurrentID && *CurrentID == -1) + CurrentID = 0; + } + + public: + typedef RecordType value_type; + typedef const value_type & reference; + typedef const value_type * pointer; + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + + inline reference operator*() const; + inline pointer operator->() const { + return &operator*(); + } + + inline short getID() const { + return *CurrentID; + } + + group_iterator &operator++() { + ++CurrentID; + if (*CurrentID == -1) + CurrentID = 0; + return *this; + } + + bool operator==(group_iterator &Other) const { + return CurrentID == Other.CurrentID; + } + + bool operator!=(group_iterator &Other) const { + return CurrentID != Other.CurrentID; + } + }; + + typedef group_iterator subgroup_iterator; + subgroup_iterator subgroup_begin() const { + return SubGroups; + } + subgroup_iterator subgroup_end() const { + return 0; + } + + typedef group_iterator diagnostics_iterator; + diagnostics_iterator diagnostics_begin() const { + return Members; + } + diagnostics_iterator diagnostics_end() const { + return 0; + } + + bool operator<(const GroupRecord &Other) const { + return getName() < Other.getName(); + } + }; + + /// \brief Get every diagnostic group in the system, sorted by name. + llvm::ArrayRef getDiagnosticGroups(); + + template<> + inline GroupRecord::subgroup_iterator::reference + GroupRecord::subgroup_iterator::operator*() const { + return getDiagnosticGroups()[*CurrentID]; + } + template<> + inline GroupRecord::diagnostics_iterator::reference + GroupRecord::diagnostics_iterator::operator*() const { + return getDiagnosticForID(*CurrentID); + } } // end namespace diagtool diff --git a/tools/diagtool/ListWarnings.cpp b/tools/diagtool/ListWarnings.cpp index 6c59338eef..d59ffaa2a0 100644 --- a/tools/diagtool/ListWarnings.cpp +++ b/tools/diagtool/ListWarnings.cpp @@ -25,6 +25,7 @@ DEF_DIAGTOOL("list-warnings", ListWarnings) using namespace clang; +using namespace diagtool; namespace { struct Entry { @@ -52,9 +53,11 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { std::vector Flagged, Unflagged; llvm::StringMap > flagHistogram; - for (const diagtool::DiagnosticRecord *di = diagtool::BuiltinDiagnostics, - *de = di + diagtool::BuiltinDiagnosticsCount; di != de; ++di) { - + ArrayRef AllDiagnostics = getBuiltinDiagnosticsByName(); + + for (ArrayRef::iterator di = AllDiagnostics.begin(), + de = AllDiagnostics.end(); + di != de; ++di) { unsigned diagID = di->DiagID; if (DiagnosticIDs::isBuiltinNote(diagID)) @@ -74,9 +77,6 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { } } - std::sort(Flagged.begin(), Flagged.end()); - std::sort(Unflagged.begin(), Unflagged.end()); - out << "Warnings with flags (" << Flagged.size() << "):\n"; printEntries(Flagged, out); diff --git a/tools/diagtool/ShowEnabledWarnings.cpp b/tools/diagtool/ShowEnabledWarnings.cpp index 2bc5124e61..7162451b4e 100644 --- a/tools/diagtool/ShowEnabledWarnings.cpp +++ b/tools/diagtool/ShowEnabledWarnings.cpp @@ -21,6 +21,7 @@ DEF_DIAGTOOL("show-enabled", ShowEnabledWarnings) using namespace clang; +using namespace diagtool; namespace { struct PrettyDiag { @@ -109,10 +110,12 @@ int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) { // which ones are turned on. // FIXME: It would be very nice to print which flags are turning on which // diagnostics, but this can be done with a diff. + ArrayRef AllDiagnostics = getBuiltinDiagnosticsByName(); std::vector Active; - for (const diagtool::DiagnosticRecord *I = diagtool::BuiltinDiagnostics, - *E = I + diagtool::BuiltinDiagnosticsCount; I != E; ++I) { + for (ArrayRef::iterator I = AllDiagnostics.begin(), + E = AllDiagnostics.end(); + I != E; ++I) { unsigned DiagID = I->DiagID; if (DiagnosticIDs::isBuiltinNote(DiagID)) @@ -130,8 +133,6 @@ int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) { Active.push_back(PrettyDiag(I->getName(), WarningOpt, DiagLevel)); } - std::sort(Active.begin(), Active.end()); - // Print them all out. for (std::vector::const_iterator I = Active.begin(), E = Active.end(); I != E; ++I) { diff --git a/tools/diagtool/TreeView.cpp b/tools/diagtool/TreeView.cpp new file mode 100644 index 0000000000..9db2c26dd9 --- /dev/null +++ b/tools/diagtool/TreeView.cpp @@ -0,0 +1,135 @@ +//===- TreeView.cpp - diagtool tool for printing warning flags ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This diagnostic tool +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "DiagnosticNames.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/Support/Format.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/DenseSet.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/Basic/AllDiagnostics.h" + +DEF_DIAGTOOL("tree", + "Show warning flags in a tree view", + TreeView) + +using namespace clang; +using namespace diagtool; + +static void printUsage() { + llvm::errs() << "Usage: diagtool tree [--flags-only] []\n"; +} + +static void printGroup(llvm::raw_ostream &out, const GroupRecord &Group, + bool FlagsOnly, unsigned Indent = 0) { + out.indent(Indent * 2); + out << "-W" << Group.getName() << "\n"; + + ++Indent; + for (GroupRecord::subgroup_iterator I = Group.subgroup_begin(), + E = Group.subgroup_end(); + I != E; ++I) { + printGroup(out, *I, FlagsOnly, Indent); + } + + if (!FlagsOnly) { + for (GroupRecord::diagnostics_iterator I = Group.diagnostics_begin(), + E = Group.diagnostics_end(); + I != E; ++I) { + out.indent(Indent * 2); + out << I->getName() << "\n"; + } + } +} + +static int showGroup(llvm::raw_ostream &out, StringRef RootGroup, + bool FlagsOnly) { + ArrayRef AllGroups = getDiagnosticGroups(); + + GroupRecord Key = { RootGroup.size(), RootGroup.data(), 0, 0 }; + const GroupRecord *Found = + std::lower_bound(AllGroups.begin(), AllGroups.end(), Key); + + if (Found == AllGroups.end() || Found->getName() != RootGroup) { + llvm::errs() << "No such diagnostic group exists\n"; + return 1; + } + + printGroup(out, *Found, FlagsOnly); + + return 0; +} + +static int showAll(llvm::raw_ostream &out, bool FlagsOnly) { + ArrayRef AllGroups = getDiagnosticGroups(); + llvm::DenseSet NonRootGroupIDs; + + for (ArrayRef::iterator I = AllGroups.begin(), + E = AllGroups.end(); + I != E; ++I) { + for (GroupRecord::subgroup_iterator SI = I->subgroup_begin(), + SE = I->subgroup_end(); + SI != SE; ++SI) { + NonRootGroupIDs.insert((unsigned)SI.getID()); + } + } + + assert(NonRootGroupIDs.size() < AllGroups.size()); + + for (unsigned i = 0, e = AllGroups.size(); i != e; ++i) { + if (!NonRootGroupIDs.count(i)) + printGroup(out, AllGroups[i], FlagsOnly); + } + + return 0; +} + +int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { + // First check our one flag (--flags-only). + bool FlagsOnly = false; + if (argc > 0) { + StringRef FirstArg(*argv); + if (FirstArg.equals("--flags-only")) { + FlagsOnly = true; + --argc; + ++argv; + } + } + + bool ShowAll = false; + StringRef RootGroup; + + switch (argc) { + case 0: + ShowAll = true; + break; + case 1: + RootGroup = argv[0]; + if (RootGroup.startswith("-W")) + RootGroup = RootGroup.substr(2); + if (RootGroup == "everything") + ShowAll = true; + // FIXME: Handle other special warning flags, like -pedantic. + break; + default: + printUsage(); + return -1; + } + + if (ShowAll) + return showAll(out, FlagsOnly); + + return showGroup(out, RootGroup, FlagsOnly); +} +