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
18DEF_DIAGTOOL("tree", "Show warning flags in a tree view", TreeView)
19
20using namespace clang;
21using namespace diagtool;
22
23class TreePrinter {
24 using Colors = llvm::raw_ostream::Colors;
25
26public:
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, Loc: SourceLocation());
37 }
38
39 static bool unimplemented(const GroupRecord &Group) {
40 if (!Group.diagnostics().empty())
41 return false;
42
43 return llvm::all_of(Range: Group.subgroups(), P: unimplemented);
44 }
45
46 static bool enabledByDefault(const GroupRecord &Group) {
47 for (const DiagnosticRecord &DR : Group.diagnostics()) {
48 if (isIgnored(DiagID: DR.DiagID))
49 return false;
50 }
51
52 for (const GroupRecord &GR : Group.subgroups()) {
53 if (!enabledByDefault(Group: GR))
54 return false;
55 }
56
57 return true;
58 }
59
60 void printGroup(const GroupRecord &Group, unsigned Indent = 0) {
61 out.indent(NumSpaces: 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(Group: GR, Indent);
75 }
76
77 if (Internal) {
78 for (const DiagnosticRecord &DR : Group.diagnostics()) {
79 if (!isIgnored(DiagID: DR.DiagID))
80 out << Colors::GREEN;
81 out.indent(NumSpaces: 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(Range&: AllGroups, Value&: 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(Group: *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(V: (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(V: i))
121 printGroup(Group: 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
138static void printUsage() {
139 llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n";
140}
141
142int 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 == "--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 RootGroup.consume_front(Prefix: "-W");
164 if (RootGroup == "everything")
165 ShowAll = true;
166 // FIXME: Handle other special warning flags, like -pedantic.
167 break;
168 default:
169 printUsage();
170 return -1;
171 }
172
173 out.enable_colors(enable: out.has_colors());
174
175 TreePrinter TP(out);
176 TP.Internal = Internal;
177 TP.showKey();
178 return ShowAll ? TP.showAll() : TP.showGroup(RootGroup);
179}
180