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 
18 DEF_DIAGTOOL("show-enabled",
19              "Show which warnings are enabled for a given command line",
20              ShowEnabledWarnings)
21 
22 using namespace clang;
23 using namespace diagtool;
24 
25 namespace {
26   struct PrettyDiag {
27     StringRef Name;
28     StringRef Flag;
29     DiagnosticsEngine::Level Level;
30 
PrettyDiag__anon3e5480740111::PrettyDiag31     PrettyDiag(StringRef name, StringRef flag, DiagnosticsEngine::Level level)
32     : Name(name), Flag(flag), Level(level) {}
33 
operator <__anon3e5480740111::PrettyDiag34     bool operator<(const PrettyDiag &x) const { return Name < x.Name; }
35   };
36 }
37 
printUsage()38 static void printUsage() {
39   llvm::errs() << "Usage: diagtool show-enabled [<flags>] <single-input.c>\n";
40 }
41 
getCharForLevel(DiagnosticsEngine::Level Level)42 static char getCharForLevel(DiagnosticsEngine::Level Level) {
43   switch (Level) {
44   case DiagnosticsEngine::Ignored: return ' ';
45   case DiagnosticsEngine::Note:    return '-';
46   case DiagnosticsEngine::Remark:  return 'R';
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 
55 static IntrusiveRefCntPtr<DiagnosticsEngine>
createDiagnostics(unsigned int argc,char ** argv)56 createDiagnostics(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   SmallVector<const char *, 4> Args;
67   Args.push_back("diagtool");
68   Args.append(argv, argv + argc);
69   std::unique_ptr<CompilerInvocation> Invocation =
70       createInvocationFromCommandLine(Args, InterimDiags);
71   if (!Invocation)
72     return nullptr;
73 
74   // Build the diagnostics parser
75   IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags =
76     CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts());
77   if (!FinalDiags)
78     return nullptr;
79 
80   // Flush any errors created when initializing everything. This could happen
81   // for invalid command lines, which will probably give non-sensical results.
82   DiagsBuffer->FlushDiagnostics(*FinalDiags);
83 
84   return FinalDiags;
85 }
86 
run(unsigned int argc,char ** argv,raw_ostream & Out)87 int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
88   // First check our one flag (--levels).
89   bool ShouldShowLevels = true;
90   if (argc > 0) {
91     StringRef FirstArg(*argv);
92     if (FirstArg.equals("--no-levels")) {
93       ShouldShowLevels = false;
94       --argc;
95       ++argv;
96     } else if (FirstArg.equals("--levels")) {
97       ShouldShowLevels = true;
98       --argc;
99       ++argv;
100     }
101   }
102 
103   // Create the diagnostic engine.
104   IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv);
105   if (!Diags) {
106     printUsage();
107     return EXIT_FAILURE;
108   }
109 
110   // Now we have our diagnostics. Iterate through EVERY diagnostic and see
111   // which ones are turned on.
112   // FIXME: It would be very nice to print which flags are turning on which
113   // diagnostics, but this can be done with a diff.
114   std::vector<PrettyDiag> Active;
115 
116   for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) {
117     unsigned DiagID = DR.DiagID;
118 
119     if (DiagnosticIDs::isBuiltinNote(DiagID))
120       continue;
121 
122     if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID))
123       continue;
124 
125     DiagnosticsEngine::Level DiagLevel =
126       Diags->getDiagnosticLevel(DiagID, SourceLocation());
127     if (DiagLevel == DiagnosticsEngine::Ignored)
128       continue;
129 
130     StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID);
131     Active.push_back(PrettyDiag(DR.getName(), WarningOpt, DiagLevel));
132   }
133 
134   // Print them all out.
135   for (const PrettyDiag &PD : Active) {
136     if (ShouldShowLevels)
137       Out << getCharForLevel(PD.Level) << "  ";
138     Out << PD.Name;
139     if (!PD.Flag.empty())
140       Out << " [-W" << PD.Flag << "]";
141     Out << '\n';
142   }
143 
144   return EXIT_SUCCESS;
145 }
146