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
TreePrinter(llvm::raw_ostream & out)30 TreePrinter(llvm::raw_ostream &out) : out(out), Internal(false) {}
31
isIgnored(unsigned DiagID)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
unimplemented(const GroupRecord & Group)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
enabledByDefault(const GroupRecord & Group)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
printGroup(const GroupRecord & Group,unsigned Indent=0)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
showGroup(StringRef RootGroup)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
showAll()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
showKey()127 void showKey() {
128 out << '\n' << Colors::GREEN << "GREEN" << Colors::RESET
129 << " = enabled by default";
130 out << '\n' << Colors::RED << "RED" << Colors::RESET
131 << " = unimplemented (accepted for GCC compatibility)\n\n";
132 }
133 };
134
printUsage()135 static void printUsage() {
136 llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n";
137 }
138
run(unsigned int argc,char ** argv,llvm::raw_ostream & out)139 int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
140 // First check our one flag (--flags-only).
141 bool Internal = false;
142 if (argc > 0) {
143 StringRef FirstArg(*argv);
144 if (FirstArg.equals("--internal")) {
145 Internal = true;
146 --argc;
147 ++argv;
148 }
149 }
150
151 bool ShowAll = false;
152 StringRef RootGroup;
153
154 switch (argc) {
155 case 0:
156 ShowAll = true;
157 break;
158 case 1:
159 RootGroup = argv[0];
160 if (RootGroup.startswith("-W"))
161 RootGroup = RootGroup.substr(2);
162 if (RootGroup == "everything")
163 ShowAll = true;
164 // FIXME: Handle other special warning flags, like -pedantic.
165 break;
166 default:
167 printUsage();
168 return -1;
169 }
170
171 out.enable_colors(out.has_colors());
172
173 TreePrinter TP(out);
174 TP.Internal = Internal;
175 TP.showKey();
176 return ShowAll ? TP.showAll() : TP.showGroup(RootGroup);
177 }
178