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