1 //===- TreeView.cpp - diagtool tool for printing warning flags ------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "DiagTool.h" 10 #include "DiagnosticNames.h" 11 #include "clang/Basic/AllDiagnostics.h" 12 #include "clang/Basic/Diagnostic.h" 13 #include "clang/Basic/DiagnosticOptions.h" 14 #include "llvm/ADT/DenseSet.h" 15 #include "llvm/Support/Format.h" 16 #include "llvm/Support/Process.h" 17 18 DEF_DIAGTOOL("tree", "Show warning flags in a tree view", TreeView) 19 20 using namespace clang; 21 using namespace diagtool; 22 23 class TreePrinter { 24 using Colors = llvm::raw_ostream::Colors; 25 26 public: 27 llvm::raw_ostream &out; 28 bool Internal; 29 30 TreePrinter(llvm::raw_ostream &out) : out(out), Internal(false) {} 31 32 static bool isIgnored(unsigned DiagID) { 33 // FIXME: This feels like a hack. 34 static clang::DiagnosticsEngine Diags(new DiagnosticIDs, 35 new DiagnosticOptions); 36 return Diags.isIgnored(DiagID, SourceLocation()); 37 } 38 39 static bool unimplemented(const GroupRecord &Group) { 40 if (!Group.diagnostics().empty()) 41 return false; 42 43 return llvm::all_of(Group.subgroups(), unimplemented); 44 } 45 46 static bool enabledByDefault(const GroupRecord &Group) { 47 for (const DiagnosticRecord &DR : Group.diagnostics()) { 48 if (isIgnored(DR.DiagID)) 49 return false; 50 } 51 52 for (const GroupRecord &GR : Group.subgroups()) { 53 if (!enabledByDefault(GR)) 54 return false; 55 } 56 57 return true; 58 } 59 60 void printGroup(const GroupRecord &Group, unsigned Indent = 0) { 61 out.indent(Indent * 2); 62 63 if (unimplemented(Group)) 64 out << Colors::RED; 65 else if (enabledByDefault(Group)) 66 out << Colors::GREEN; 67 else 68 out << Colors::YELLOW; 69 70 out << "-W" << Group.getName() << "\n" << Colors::RESET; 71 72 ++Indent; 73 for (const GroupRecord &GR : Group.subgroups()) { 74 printGroup(GR, Indent); 75 } 76 77 if (Internal) { 78 for (const DiagnosticRecord &DR : Group.diagnostics()) { 79 if (!isIgnored(DR.DiagID)) 80 out << Colors::GREEN; 81 out.indent(Indent * 2); 82 out << DR.getName() << Colors::RESET << "\n"; 83 } 84 } 85 } 86 87 int showGroup(StringRef RootGroup) { 88 ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); 89 90 if (RootGroup.size() > UINT16_MAX) { 91 llvm::errs() << "No such diagnostic group exists\n"; 92 return 1; 93 } 94 95 const GroupRecord *Found = llvm::lower_bound(AllGroups, RootGroup); 96 if (Found == AllGroups.end() || Found->getName() != RootGroup) { 97 llvm::errs() << "No such diagnostic group exists\n"; 98 return 1; 99 } 100 101 printGroup(*Found); 102 103 return 0; 104 } 105 106 int showAll() { 107 ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); 108 llvm::DenseSet<unsigned> NonRootGroupIDs; 109 110 for (const GroupRecord &GR : AllGroups) { 111 for (auto SI = GR.subgroup_begin(), SE = GR.subgroup_end(); SI != SE; 112 ++SI) { 113 NonRootGroupIDs.insert((unsigned)SI.getID()); 114 } 115 } 116 117 assert(NonRootGroupIDs.size() < AllGroups.size()); 118 119 for (unsigned i = 0, e = AllGroups.size(); i != e; ++i) { 120 if (!NonRootGroupIDs.count(i)) 121 printGroup(AllGroups[i]); 122 } 123 124 return 0; 125 } 126 127 void showKey() { 128 out << '\n' << Colors::GREEN << "GREEN" << Colors::RESET 129 << " = enabled by default"; 130 out << '\n' 131 << Colors::YELLOW << "YELLOW" << Colors::RESET 132 << " = disabled by default"; 133 out << '\n' << Colors::RED << "RED" << Colors::RESET 134 << " = unimplemented (accepted for GCC compatibility)\n\n"; 135 } 136 }; 137 138 static void printUsage() { 139 llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n"; 140 } 141 142 int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { 143 // First check our one flag (--flags-only). 144 bool Internal = false; 145 if (argc > 0) { 146 StringRef FirstArg(*argv); 147 if (FirstArg.equals("--internal")) { 148 Internal = true; 149 --argc; 150 ++argv; 151 } 152 } 153 154 bool ShowAll = false; 155 StringRef RootGroup; 156 157 switch (argc) { 158 case 0: 159 ShowAll = true; 160 break; 161 case 1: 162 RootGroup = argv[0]; 163 if (RootGroup.startswith("-W")) 164 RootGroup = RootGroup.substr(2); 165 if (RootGroup == "everything") 166 ShowAll = true; 167 // FIXME: Handle other special warning flags, like -pedantic. 168 break; 169 default: 170 printUsage(); 171 return -1; 172 } 173 174 out.enable_colors(out.has_colors()); 175 176 TreePrinter TP(out); 177 TP.Internal = Internal; 178 TP.showKey(); 179 return ShowAll ? TP.showAll() : TP.showGroup(RootGroup); 180 } 181