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
18using namespace clang;
19using namespace ento;
20
21static const char PackageSeparator = '.';
22typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet;
23
24
25static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
26                          const CheckerRegistry::CheckerInfo &b) {
27  return a.FullName < b.FullName;
28}
29
30static 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
47static 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  CheckerRegistry::CheckerInfoList::const_iterator e = checkers.end();
53  CheckerRegistry::CheckerInfoList::const_iterator i =
54    std::lower_bound(checkers.begin(), e, packageInfo, checkerNameLT);
55
56  // If we didn't even find a possible package, give up.
57  if (i == e)
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 (e = i+size; i != e; ++i) {
77    if (opt.isEnabled())
78      collected.insert(&*i);
79    else
80      collected.remove(&*i);
81  }
82}
83
84void 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
97void 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
117void 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
139void 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