150 lines · cpp
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-exception6//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 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 DiagnosticOptions DiagOpts;60 61 // Buffer diagnostics from argument parsing so that we can output them using a62 // 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("diagtool");68 Args.append(argv, argv + argc);69 CreateInvocationOptions CIOpts;70 CIOpts.Diags = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(71 DiagnosticIDs::create(), DiagOpts, DiagsBuffer);72 std::unique_ptr<CompilerInvocation> Invocation =73 createInvocation(Args, CIOpts);74 if (!Invocation)75 return nullptr;76 77 // Build the diagnostics parser78 IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags =79 CompilerInstance::createDiagnostics(*llvm::vfs::getRealFileSystem(),80 Invocation->getDiagnosticOpts());81 if (!FinalDiags)82 return nullptr;83 84 // Flush any errors created when initializing everything. This could happen85 // for invalid command lines, which will probably give non-sensical results.86 DiagsBuffer->FlushDiagnostics(*FinalDiags);87 88 return FinalDiags;89}90 91int 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 see115 // which ones are turned on.116 // FIXME: It would be very nice to print which flags are turning on which117 // 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, SourceLocation());131 if (DiagLevel == DiagnosticsEngine::Ignored)132 continue;133 134 StringRef WarningOpt = DiagnosticIDs{}.getWarningOptionForDiag(DiagID);135 Active.push_back(PrettyDiag(DR.getName(), WarningOpt, DiagLevel));136 }137 138 // Print them all out.139 for (const PrettyDiag &PD : Active) {140 if (ShouldShowLevels)141 Out << getCharForLevel(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