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 static bool hasColors(const llvm::raw_ostream &out) { 24 if (&out != &llvm::errs() && &out != &llvm::outs()) 25 return false; 26 return llvm::errs().is_displayed() && llvm::outs().is_displayed(); 27 } 28 29 class TreePrinter { 30 public: 31 llvm::raw_ostream &out; 32 const bool ShowColors; 33 bool Internal; 34 35 TreePrinter(llvm::raw_ostream &out) 36 : out(out), ShowColors(hasColors(out)), Internal(false) {} 37 38 void setColor(llvm::raw_ostream::Colors Color) { 39 if (ShowColors) 40 out << llvm::sys::Process::OutputColor(static_cast<char>(Color), false, 41 false); 42 } 43 44 void resetColor() { 45 if (ShowColors) 46 out << llvm::sys::Process::ResetColor(); 47 } 48 49 static bool isIgnored(unsigned DiagID) { 50 // FIXME: This feels like a hack. 51 static clang::DiagnosticsEngine Diags(new DiagnosticIDs, 52 new DiagnosticOptions); 53 return Diags.isIgnored(DiagID, SourceLocation()); 54 } 55 56 static bool enabledByDefault(const GroupRecord &Group) { 57 for (const DiagnosticRecord &DR : Group.diagnostics()) { 58 if (isIgnored(DR.DiagID)) 59 return false; 60 } 61 62 for (const GroupRecord &GR : Group.subgroups()) { 63 if (!enabledByDefault(GR)) 64 return false; 65 } 66 67 return true; 68 } 69 70 void printGroup(const GroupRecord &Group, unsigned Indent = 0) { 71 out.indent(Indent * 2); 72 73 if (enabledByDefault(Group)) 74 setColor(llvm::raw_ostream::GREEN); 75 else 76 setColor(llvm::raw_ostream::YELLOW); 77 78 out << "-W" << Group.getName() << "\n"; 79 resetColor(); 80 81 ++Indent; 82 for (const GroupRecord &GR : Group.subgroups()) { 83 printGroup(GR, Indent); 84 } 85 86 if (Internal) { 87 for (const DiagnosticRecord &DR : Group.diagnostics()) { 88 if (ShowColors && !isIgnored(DR.DiagID)) 89 setColor(llvm::raw_ostream::GREEN); 90 out.indent(Indent * 2); 91 out << DR.getName(); 92 resetColor(); 93 out << "\n"; 94 } 95 } 96 } 97 98 int showGroup(StringRef RootGroup) { 99 ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); 100 101 if (RootGroup.size() > UINT16_MAX) { 102 llvm::errs() << "No such diagnostic group exists\n"; 103 return 1; 104 } 105 106 const GroupRecord *Found = llvm::lower_bound(AllGroups, RootGroup); 107 if (Found == AllGroups.end() || Found->getName() != RootGroup) { 108 llvm::errs() << "No such diagnostic group exists\n"; 109 return 1; 110 } 111 112 printGroup(*Found); 113 114 return 0; 115 } 116 117 int showAll() { 118 ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); 119 llvm::DenseSet<unsigned> NonRootGroupIDs; 120 121 for (const GroupRecord &GR : AllGroups) { 122 for (auto SI = GR.subgroup_begin(), SE = GR.subgroup_end(); SI != SE; 123 ++SI) { 124 NonRootGroupIDs.insert((unsigned)SI.getID()); 125 } 126 } 127 128 assert(NonRootGroupIDs.size() < AllGroups.size()); 129 130 for (unsigned i = 0, e = AllGroups.size(); i != e; ++i) { 131 if (!NonRootGroupIDs.count(i)) 132 printGroup(AllGroups[i]); 133 } 134 135 return 0; 136 } 137 138 void showKey() { 139 if (ShowColors) { 140 out << '\n'; 141 setColor(llvm::raw_ostream::GREEN); 142 out << "GREEN"; 143 resetColor(); 144 out << " = enabled by default\n\n"; 145 } 146 } 147 }; 148 149 static void printUsage() { 150 llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n"; 151 } 152 153 int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { 154 // First check our one flag (--flags-only). 155 bool Internal = false; 156 if (argc > 0) { 157 StringRef FirstArg(*argv); 158 if (FirstArg.equals("--internal")) { 159 Internal = true; 160 --argc; 161 ++argv; 162 } 163 } 164 165 bool ShowAll = false; 166 StringRef RootGroup; 167 168 switch (argc) { 169 case 0: 170 ShowAll = true; 171 break; 172 case 1: 173 RootGroup = argv[0]; 174 if (RootGroup.startswith("-W")) 175 RootGroup = RootGroup.substr(2); 176 if (RootGroup == "everything") 177 ShowAll = true; 178 // FIXME: Handle other special warning flags, like -pedantic. 179 break; 180 default: 181 printUsage(); 182 return -1; 183 } 184 185 TreePrinter TP(out); 186 TP.Internal = Internal; 187 TP.showKey(); 188 return ShowAll ? TP.showAll() : TP.showGroup(RootGroup); 189 } 190