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