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