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::Warning: return 'W';
48  case DiagnosticsEngine::Error:   return 'E';
49  case DiagnosticsEngine::Fatal:   return 'F';
50  }
51
52  llvm_unreachable("Unknown diagnostic level");
53}
54
55static IntrusiveRefCntPtr<DiagnosticsEngine>
56createDiagnostics(unsigned int argc, char **argv) {
57  IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs());
58
59  // Buffer diagnostics from argument parsing so that we can output them using a
60  // well formed diagnostic object.
61  TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
62  IntrusiveRefCntPtr<DiagnosticsEngine> InterimDiags(
63    new DiagnosticsEngine(DiagIDs, new DiagnosticOptions(), DiagsBuffer));
64
65  // Try to build a CompilerInvocation.
66  OwningPtr<CompilerInvocation> Invocation(
67    createInvocationFromCommandLine(ArrayRef<const char *>(argv, argc),
68                                    InterimDiags));
69  if (!Invocation)
70    return NULL;
71
72  // Build the diagnostics parser
73  IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags =
74    CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts());
75  if (!FinalDiags)
76    return NULL;
77
78  // Flush any errors created when initializing everything. This could happen
79  // for invalid command lines, which will probably give non-sensical results.
80  DiagsBuffer->FlushDiagnostics(*FinalDiags);
81
82  return FinalDiags;
83}
84
85int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
86  // First check our one flag (--levels).
87  bool ShouldShowLevels = true;
88  if (argc > 0) {
89    StringRef FirstArg(*argv);
90    if (FirstArg.equals("--no-levels")) {
91      ShouldShowLevels = false;
92      --argc;
93      ++argv;
94    } else if (FirstArg.equals("--levels")) {
95      ShouldShowLevels = true;
96      --argc;
97      ++argv;
98    }
99  }
100
101  // Create the diagnostic engine.
102  IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv);
103  if (!Diags) {
104    printUsage();
105    return EXIT_FAILURE;
106  }
107
108  // Now we have our diagnostics. Iterate through EVERY diagnostic and see
109  // which ones are turned on.
110  // FIXME: It would be very nice to print which flags are turning on which
111  // diagnostics, but this can be done with a diff.
112  ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
113  std::vector<PrettyDiag> Active;
114
115  for (ArrayRef<DiagnosticRecord>::iterator I = AllDiagnostics.begin(),
116                                            E = AllDiagnostics.end();
117       I != E; ++I) {
118    unsigned DiagID = I->DiagID;
119
120    if (DiagnosticIDs::isBuiltinNote(DiagID))
121      continue;
122
123    if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID))
124      continue;
125
126    DiagnosticsEngine::Level DiagLevel =
127      Diags->getDiagnosticLevel(DiagID, SourceLocation());
128    if (DiagLevel == DiagnosticsEngine::Ignored)
129      continue;
130
131    StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID);
132    Active.push_back(PrettyDiag(I->getName(), WarningOpt, DiagLevel));
133  }
134
135  // Print them all out.
136  for (std::vector<PrettyDiag>::const_iterator I = Active.begin(),
137       E = Active.end(); I != E; ++I) {
138    if (ShouldShowLevels)
139      Out << getCharForLevel(I->Level) << "  ";
140    Out << I->Name;
141    if (!I->Flag.empty())
142      Out << " [-W" << I->Flag << "]";
143    Out << '\n';
144  }
145
146  return EXIT_SUCCESS;
147}
148