• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
checkerNameLT(const CheckerRegistry::CheckerInfo & a,const CheckerRegistry::CheckerInfo & b)25 static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
26                           const CheckerRegistry::CheckerInfo &b) {
27   return a.FullName < b.FullName;
28 }
29 
isInPackage(const CheckerRegistry::CheckerInfo & checker,StringRef packageName)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 
collectCheckers(const CheckerRegistry::CheckerInfoList & checkers,const llvm::StringMap<size_t> & packageSizes,CheckerOptInfo & opt,CheckerInfoSet & collected)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 
addChecker(InitializationFunction fn,StringRef name,StringRef desc)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 
initializeManager(CheckerManager & checkerMgr,SmallVectorImpl<CheckerOptInfo> & opts) const97 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 
validateCheckerOptions(const AnalyzerOptions & opts,DiagnosticsEngine & diags) const117 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 
printHelp(raw_ostream & out,size_t maxNameChars) const139 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