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