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