Home | History | Annotate | Download | only in Core
      1 //===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
     11 #include "clang/Basic/Diagnostic.h"
     12 #include "clang/Frontend/FrontendDiagnostic.h"
     13 #include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
     14 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
     15 #include "llvm/ADT/SetVector.h"
     16 #include "llvm/Support/raw_ostream.h"
     17 
     18 using namespace clang;
     19 using namespace ento;
     20 
     21 static const char PackageSeparator = '.';
     22 typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet;
     23 
     24 
     25 static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
     26                           const CheckerRegistry::CheckerInfo &b) {
     27   return a.FullName < b.FullName;
     28 }
     29 
     30 static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
     31                         StringRef packageName) {
     32   // Does the checker's full name have the package as a prefix?
     33   if (!checker.FullName.startswith(packageName))
     34     return false;
     35 
     36   // Is the package actually just the name of a specific checker?
     37   if (checker.FullName.size() == packageName.size())
     38     return true;
     39 
     40   // Is the checker in the package (or a subpackage)?
     41   if (checker.FullName[packageName.size()] == PackageSeparator)
     42     return true;
     43 
     44   return false;
     45 }
     46 
     47 static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers,
     48                             const llvm::StringMap<size_t> &packageSizes,
     49                             CheckerOptInfo &opt, CheckerInfoSet &collected) {
     50   // Use a binary search to find the possible start of the package.
     51   CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.getName(), "");
     52   auto end = checkers.cend();
     53   CheckerRegistry::CheckerInfoList::const_iterator i =
     54     std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT);
     55 
     56   // If we didn't even find a possible package, give up.
     57   if (i == end)
     58     return;
     59 
     60   // If what we found doesn't actually start the package, give up.
     61   if (!isInPackage(*i, opt.getName()))
     62     return;
     63 
     64   // There is at least one checker in the package; claim the option.
     65   opt.claim();
     66 
     67   // See how large the package is.
     68   // If the package doesn't exist, assume the option refers to a single checker.
     69   size_t size = 1;
     70   llvm::StringMap<size_t>::const_iterator packageSize =
     71     packageSizes.find(opt.getName());
     72   if (packageSize != packageSizes.end())
     73     size = packageSize->getValue();
     74 
     75   // Step through all the checkers in the package.
     76   for (auto checkEnd = i+size; i != checkEnd; ++i) {
     77     if (opt.isEnabled())
     78       collected.insert(&*i);
     79     else
     80       collected.remove(&*i);
     81   }
     82 }
     83 
     84 void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name,
     85                                  StringRef desc) {
     86   Checkers.push_back(CheckerInfo(fn, name, desc));
     87 
     88   // Record the presence of the checker in its packages.
     89   StringRef packageName, leafName;
     90   std::tie(packageName, leafName) = name.rsplit(PackageSeparator);
     91   while (!leafName.empty()) {
     92     Packages[packageName] += 1;
     93     std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
     94   }
     95 }
     96 
     97 void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
     98                                   SmallVectorImpl<CheckerOptInfo> &opts) const {
     99   // Sort checkers for efficient collection.
    100   std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
    101 
    102   // Collect checkers enabled by the options.
    103   CheckerInfoSet enabledCheckers;
    104   for (SmallVectorImpl<CheckerOptInfo>::iterator
    105          i = opts.begin(), e = opts.end(); i != e; ++i) {
    106     collectCheckers(Checkers, Packages, *i, enabledCheckers);
    107   }
    108 
    109   // Initialize the CheckerManager with all enabled checkers.
    110   for (CheckerInfoSet::iterator
    111          i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) {
    112     checkerMgr.setCurrentCheckName(CheckName((*i)->FullName));
    113     (*i)->Initialize(checkerMgr);
    114   }
    115 }
    116 
    117 void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts,
    118                                              DiagnosticsEngine &diags) const {
    119   for (auto &config : opts.Config) {
    120     size_t pos = config.getKey().find(':');
    121     if (pos == StringRef::npos)
    122       continue;
    123 
    124     bool hasChecker = false;
    125     StringRef checkerName = config.getKey().substr(0, pos);
    126     for (auto &checker : Checkers) {
    127       if (checker.FullName.startswith(checkerName) &&
    128           (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
    129         hasChecker = true;
    130         break;
    131       }
    132     }
    133     if (!hasChecker) {
    134       diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
    135     }
    136   }
    137 }
    138 
    139 void CheckerRegistry::printHelp(raw_ostream &out,
    140                                 size_t maxNameChars) const {
    141   // FIXME: Alphabetical sort puts 'experimental' in the middle.
    142   // Would it be better to name it '~experimental' or something else
    143   // that's ASCIIbetically last?
    144   std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
    145 
    146   // FIXME: Print available packages.
    147 
    148   out << "CHECKERS:\n";
    149 
    150   // Find the maximum option length.
    151   size_t optionFieldWidth = 0;
    152   for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end();
    153        i != e; ++i) {
    154     // Limit the amount of padding we are willing to give up for alignment.
    155     //   Package.Name     Description  [Hidden]
    156     size_t nameLength = i->FullName.size();
    157     if (nameLength <= maxNameChars)
    158       optionFieldWidth = std::max(optionFieldWidth, nameLength);
    159   }
    160 
    161   const size_t initialPad = 2;
    162   for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end();
    163        i != e; ++i) {
    164     out.indent(initialPad) << i->FullName;
    165 
    166     int pad = optionFieldWidth - i->FullName.size();
    167 
    168     // Break on long option names.
    169     if (pad < 0) {
    170       out << '\n';
    171       pad = optionFieldWidth + initialPad;
    172     }
    173     out.indent(pad + 2) << i->Desc;
    174 
    175     out << '\n';
    176   }
    177 }
    178