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 | DiagnosticOptions DiagOpts; |
60 | |
61 | // Buffer diagnostics from argument parsing so that we can output them using a |
62 | // well formed diagnostic object. |
63 | TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; |
64 | |
65 | // Try to build a CompilerInvocation. |
66 | SmallVector<const char *, 4> Args; |
67 | Args.push_back(Elt: "diagtool" ); |
68 | Args.append(in_start: argv, in_end: argv + argc); |
69 | CreateInvocationOptions CIOpts; |
70 | CIOpts.Diags = new DiagnosticsEngine(DiagIDs, DiagOpts, DiagsBuffer); |
71 | std::unique_ptr<CompilerInvocation> Invocation = |
72 | createInvocation(Args, Opts: CIOpts); |
73 | if (!Invocation) |
74 | return nullptr; |
75 | |
76 | // Build the diagnostics parser |
77 | IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags = |
78 | CompilerInstance::createDiagnostics(VFS&: *llvm::vfs::getRealFileSystem(), |
79 | Opts&: 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(Diags&: *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, Loc: SourceLocation()); |
130 | if (DiagLevel == DiagnosticsEngine::Ignored) |
131 | continue; |
132 | |
133 | StringRef WarningOpt = DiagnosticIDs{}.getWarningOptionForDiag(DiagID); |
134 | Active.push_back(x: PrettyDiag(DR.getName(), WarningOpt, DiagLevel)); |
135 | } |
136 | |
137 | // Print them all out. |
138 | for (const PrettyDiag &PD : Active) { |
139 | if (ShouldShowLevels) |
140 | Out << getCharForLevel(Level: 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 | |