1//===- ShowEnabledWarnings - diagtool tool for printing enabled 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/Basic/LLVM.h"
13#include "clang/Frontend/CompilerInstance.h"
14#include "clang/Frontend/TextDiagnosticBuffer.h"
15#include "clang/Frontend/TextDiagnosticPrinter.h"
16#include "clang/Frontend/Utils.h"
17#include "llvm/Support/TargetSelect.h"
18
19DEF_DIAGTOOL("show-enabled",
20             "Show which warnings are enabled for a given command line",
21             ShowEnabledWarnings)
22
23using namespace clang;
24using namespace diagtool;
25
26namespace {
27  struct PrettyDiag {
28    StringRef Name;
29    StringRef Flag;
30    DiagnosticsEngine::Level Level;
31
32    PrettyDiag(StringRef name, StringRef flag, DiagnosticsEngine::Level level)
33    : Name(name), Flag(flag), Level(level) {}
34
35    bool operator<(const PrettyDiag &x) const { return Name < x.Name; }
36  };
37}
38
39static void printUsage() {
40  llvm::errs() << "Usage: diagtool show-enabled [<flags>] <single-input.c>\n";
41}
42
43static char getCharForLevel(DiagnosticsEngine::Level Level) {
44  switch (Level) {
45  case DiagnosticsEngine::Ignored: return ' ';
46  case DiagnosticsEngine::Note:    return '-';
47  case DiagnosticsEngine::Remark:  return 'R';
48  case DiagnosticsEngine::Warning: return 'W';
49  case DiagnosticsEngine::Error:   return 'E';
50  case DiagnosticsEngine::Fatal:   return 'F';
51  }
52
53  llvm_unreachable("Unknown diagnostic level");
54}
55
56static IntrusiveRefCntPtr<DiagnosticsEngine>
57createDiagnostics(unsigned int argc, char **argv) {
58  IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs());
59
60  // Buffer diagnostics from argument parsing so that we can output them using a
61  // well formed diagnostic object.
62  TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
63  IntrusiveRefCntPtr<DiagnosticsEngine> InterimDiags(
64    new DiagnosticsEngine(DiagIDs, new DiagnosticOptions(), DiagsBuffer));
65
66  // Try to build a CompilerInvocation.
67  std::unique_ptr<CompilerInvocation> Invocation(
68      createInvocationFromCommandLine(ArrayRef<const char *>(argv, argc),
69                                      InterimDiags));
70  if (!Invocation)
71    return nullptr;
72
73  // Build the diagnostics parser
74  IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags =
75    CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts());
76  if (!FinalDiags)
77    return nullptr;
78
79  // Flush any errors created when initializing everything. This could happen
80  // for invalid command lines, which will probably give non-sensical results.
81  DiagsBuffer->FlushDiagnostics(*FinalDiags);
82
83  return FinalDiags;
84}
85
86int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
87  // First check our one flag (--levels).
88  bool ShouldShowLevels = true;
89  if (argc > 0) {
90    StringRef FirstArg(*argv);
91    if (FirstArg.equals("--no-levels")) {
92      ShouldShowLevels = false;
93      --argc;
94      ++argv;
95    } else if (FirstArg.equals("--levels")) {
96      ShouldShowLevels = true;
97      --argc;
98      ++argv;
99    }
100  }
101
102  // Create the diagnostic engine.
103  IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv);
104  if (!Diags) {
105    printUsage();
106    return EXIT_FAILURE;
107  }
108
109  // Now we have our diagnostics. Iterate through EVERY diagnostic and see
110  // which ones are turned on.
111  // FIXME: It would be very nice to print which flags are turning on which
112  // diagnostics, but this can be done with a diff.
113  ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
114  std::vector<PrettyDiag> Active;
115
116  for (ArrayRef<DiagnosticRecord>::iterator I = AllDiagnostics.begin(),
117                                            E = AllDiagnostics.end();
118       I != E; ++I) {
119    unsigned DiagID = I->DiagID;
120
121    if (DiagnosticIDs::isBuiltinNote(DiagID))
122      continue;
123
124    if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID))
125      continue;
126
127    DiagnosticsEngine::Level DiagLevel =
128      Diags->getDiagnosticLevel(DiagID, SourceLocation());
129    if (DiagLevel == DiagnosticsEngine::Ignored)
130      continue;
131
132    StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID);
133    Active.push_back(PrettyDiag(I->getName(), WarningOpt, DiagLevel));
134  }
135
136  // Print them all out.
137  for (std::vector<PrettyDiag>::const_iterator I = Active.begin(),
138       E = Active.end(); I != E; ++I) {
139    if (ShouldShowLevels)
140      Out << getCharForLevel(I->Level) << "  ";
141    Out << I->Name;
142    if (!I->Flag.empty())
143      Out << " [-W" << I->Flag << "]";
144    Out << '\n';
145  }
146
147  return EXIT_SUCCESS;
148}
149