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