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