Home | History | Annotate | Line # | Download | only in diagtool
      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