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