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