xref: /llvm-project/clang/tools/diagtool/ShowEnabledWarnings.cpp (revision 0865ecc5150b9a55ba1f9e30b6d463a66ac362a6)
1 //===- ShowEnabledWarnings - diagtool tool for printing enabled 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/LLVM.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Frontend/TextDiagnosticBuffer.h"
14 #include "clang/Frontend/TextDiagnosticPrinter.h"
15 #include "clang/Frontend/Utils.h"
16 #include "llvm/Support/TargetSelect.h"
17 #include "llvm/Support/VirtualFileSystem.h"
18 
19 DEF_DIAGTOOL("show-enabled",
20              "Show which warnings are enabled for a given command line",
21              ShowEnabledWarnings)
22 
23 using namespace clang;
24 using namespace diagtool;
25 
26 namespace {
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 
39 static void printUsage() {
40   llvm::errs() << "Usage: diagtool show-enabled [<flags>] <single-input.c>\n";
41 }
42 
43 static 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 
56 static IntrusiveRefCntPtr<DiagnosticsEngine>
57 createDiagnostics(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 
64   // Try to build a CompilerInvocation.
65   SmallVector<const char *, 4> Args;
66   Args.push_back("diagtool");
67   Args.append(argv, argv + argc);
68   CreateInvocationOptions CIOpts;
69   CIOpts.Diags =
70       new DiagnosticsEngine(DiagIDs, new DiagnosticOptions(), DiagsBuffer);
71   std::unique_ptr<CompilerInvocation> Invocation =
72       createInvocation(Args, CIOpts);
73   if (!Invocation)
74     return nullptr;
75 
76   // Build the diagnostics parser
77   IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags =
78       CompilerInstance::createDiagnostics(*llvm::vfs::getRealFileSystem(),
79                                           &Invocation->getDiagnosticOpts());
80   if (!FinalDiags)
81     return nullptr;
82 
83   // Flush any errors created when initializing everything. This could happen
84   // for invalid command lines, which will probably give non-sensical results.
85   DiagsBuffer->FlushDiagnostics(*FinalDiags);
86 
87   return FinalDiags;
88 }
89 
90 int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
91   // First check our one flag (--levels).
92   bool ShouldShowLevels = true;
93   if (argc > 0) {
94     StringRef FirstArg(*argv);
95     if (FirstArg == "--no-levels") {
96       ShouldShowLevels = false;
97       --argc;
98       ++argv;
99     } else if (FirstArg == "--levels") {
100       ShouldShowLevels = true;
101       --argc;
102       ++argv;
103     }
104   }
105 
106   // Create the diagnostic engine.
107   IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv);
108   if (!Diags) {
109     printUsage();
110     return EXIT_FAILURE;
111   }
112 
113   // Now we have our diagnostics. Iterate through EVERY diagnostic and see
114   // which ones are turned on.
115   // FIXME: It would be very nice to print which flags are turning on which
116   // diagnostics, but this can be done with a diff.
117   std::vector<PrettyDiag> Active;
118 
119   for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) {
120     unsigned DiagID = DR.DiagID;
121 
122     if (DiagnosticIDs{}.isNote(DiagID))
123       continue;
124 
125     if (!DiagnosticIDs{}.isWarningOrExtension(DiagID))
126       continue;
127 
128     DiagnosticsEngine::Level DiagLevel =
129       Diags->getDiagnosticLevel(DiagID, SourceLocation());
130     if (DiagLevel == DiagnosticsEngine::Ignored)
131       continue;
132 
133     StringRef WarningOpt = DiagnosticIDs{}.getWarningOptionForDiag(DiagID);
134     Active.push_back(PrettyDiag(DR.getName(), WarningOpt, DiagLevel));
135   }
136 
137   // Print them all out.
138   for (const PrettyDiag &PD : Active) {
139     if (ShouldShowLevels)
140       Out << getCharForLevel(PD.Level) << "  ";
141     Out << PD.Name;
142     if (!PD.Flag.empty())
143       Out << " [-W" << PD.Flag << "]";
144     Out << '\n';
145   }
146 
147   return EXIT_SUCCESS;
148 }
149