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, SourceLocation());
37  }
38
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
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
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
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
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
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
139static void printUsage() {
140  llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n";
141}
142
143int 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