1 //===- CheckerRegistry.cpp - Maintains all available checkers -------------===// 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 "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" 10 #include "clang/Basic/Diagnostic.h" 11 #include "clang/Basic/LLVM.h" 12 #include "clang/Frontend/FrontendDiagnostic.h" 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 15 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" 16 #include "llvm/ADT/STLExtras.h" 17 #include "llvm/ADT/SetVector.h" 18 #include "llvm/ADT/StringMap.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/Support/DynamicLibrary.h" 21 #include "llvm/Support/Path.h" 22 #include "llvm/Support/raw_ostream.h" 23 #include <algorithm> 24 25 using namespace clang; 26 using namespace ento; 27 using llvm::sys::DynamicLibrary; 28 29 using RegisterCheckersFn = void (*)(CheckerRegistry &); 30 31 static bool isCompatibleAPIVersion(const char *versionString) { 32 // If the version string is null, it's not an analyzer plugin. 33 if (!versionString) 34 return false; 35 36 // For now, none of the static analyzer API is considered stable. 37 // Versions must match exactly. 38 return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; 39 } 40 41 static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, 42 const CheckerRegistry::CheckerInfo &b) { 43 return a.FullName < b.FullName; 44 } 45 46 static constexpr char PackageSeparator = '.'; 47 48 static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, 49 StringRef packageName) { 50 // Does the checker's full name have the package as a prefix? 51 if (!checker.FullName.startswith(packageName)) 52 return false; 53 54 // Is the package actually just the name of a specific checker? 55 if (checker.FullName.size() == packageName.size()) 56 return true; 57 58 // Is the checker in the package (or a subpackage)? 59 if (checker.FullName[packageName.size()] == PackageSeparator) 60 return true; 61 62 return false; 63 } 64 65 CheckerRegistry::CheckerInfoListRange 66 CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) { 67 68 assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && 69 "In order to efficiently gather checkers, this function expects them " 70 "to be already sorted!"); 71 72 // Use a binary search to find the possible start of the package. 73 CheckerRegistry::CheckerInfo 74 packageInfo(nullptr, nullptr, CmdLineArg, "", ""); 75 auto it = std::lower_bound(Checkers.begin(), Checkers.end(), 76 packageInfo, checkerNameLT); 77 78 if (!isInPackage(*it, CmdLineArg)) 79 return { Checkers.end(), Checkers.end() }; 80 81 // See how large the package is. 82 // If the package doesn't exist, assume the option refers to a single 83 // checker. 84 size_t size = 1; 85 llvm::StringMap<size_t>::const_iterator packageSize = 86 Packages.find(CmdLineArg); 87 88 if (packageSize != Packages.end()) 89 size = packageSize->getValue(); 90 91 return { it, it + size }; 92 } 93 94 CheckerRegistry::CheckerRegistry( 95 ArrayRef<std::string> plugins, DiagnosticsEngine &diags, 96 AnalyzerOptions &AnOpts, const LangOptions &LangOpts, 97 ArrayRef<std::function<void(CheckerRegistry &)>> 98 checkerRegistrationFns) 99 : Diags(diags), AnOpts(AnOpts), LangOpts(LangOpts) { 100 101 // Register builtin checkers. 102 #define GET_CHECKERS 103 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ 104 addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT, \ 105 DOC_URI); 106 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 107 #undef CHECKER 108 #undef GET_CHECKERS 109 110 // Register checkers from plugins. 111 for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end(); 112 i != e; ++i) { 113 // Get access to the plugin. 114 std::string err; 115 DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err); 116 if (!lib.isValid()) { 117 diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err; 118 continue; 119 } 120 121 // See if it's compatible with this build of clang. 122 const char *pluginAPIVersion = 123 (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString"); 124 if (!isCompatibleAPIVersion(pluginAPIVersion)) { 125 Diags.Report(diag::warn_incompatible_analyzer_plugin_api) 126 << llvm::sys::path::filename(*i); 127 Diags.Report(diag::note_incompatible_analyzer_plugin_api) 128 << CLANG_ANALYZER_API_VERSION_STRING 129 << pluginAPIVersion; 130 continue; 131 } 132 133 // Register its checkers. 134 RegisterCheckersFn registerPluginCheckers = 135 (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol( 136 "clang_registerCheckers"); 137 if (registerPluginCheckers) 138 registerPluginCheckers(*this); 139 } 140 141 // Register statically linked checkers, that aren't generated from the tblgen 142 // file, but rather passed their registry function as a parameter in 143 // checkerRegistrationFns. 144 145 for (const auto &Fn : checkerRegistrationFns) 146 Fn(*this); 147 148 // Sort checkers for efficient collection. 149 // FIXME: Alphabetical sort puts 'experimental' in the middle. 150 // Would it be better to name it '~experimental' or something else 151 // that's ASCIIbetically last? 152 llvm::sort(Checkers, checkerNameLT); 153 154 #define GET_CHECKER_DEPENDENCIES 155 156 #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ 157 addDependency(FULLNAME, DEPENDENCY); 158 159 #include "clang/StaticAnalyzer/Checkers/Checkers.inc" 160 #undef CHECKER_DEPENDENCY 161 #undef GET_CHECKER_DEPENDENCIES 162 163 // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the 164 // command line. 165 for (const std::pair<std::string, bool> &opt : AnOpts.CheckersControlList) { 166 CheckerInfoListRange checkersForCmdLineArg = 167 getMutableCheckersForCmdLineArg(opt.first); 168 169 if (checkersForCmdLineArg.begin() == checkersForCmdLineArg.end()) { 170 Diags.Report(diag::err_unknown_analyzer_checker) << opt.first; 171 Diags.Report(diag::note_suggest_disabling_all_checkers); 172 } 173 174 for (CheckerInfo &checker : checkersForCmdLineArg) { 175 checker.State = opt.second ? StateFromCmdLine::State_Enabled : 176 StateFromCmdLine::State_Disabled; 177 } 178 } 179 } 180 181 /// Collects dependencies in \p ret, returns false on failure. 182 static bool collectDependenciesImpl( 183 const CheckerRegistry::ConstCheckerInfoList &deps, 184 const LangOptions &LO, 185 CheckerRegistry::CheckerInfoSet &ret); 186 187 /// Collects dependenies in \p enabledCheckers. Return None on failure. 188 LLVM_NODISCARD 189 static llvm::Optional<CheckerRegistry::CheckerInfoSet> collectDependencies( 190 const CheckerRegistry::CheckerInfo &checker, const LangOptions &LO) { 191 192 CheckerRegistry::CheckerInfoSet ret; 193 // Add dependencies to the enabled checkers only if all of them can be 194 // enabled. 195 if (!collectDependenciesImpl(checker.Dependencies, LO, ret)) 196 return None; 197 198 return ret; 199 } 200 201 static bool collectDependenciesImpl( 202 const CheckerRegistry::ConstCheckerInfoList &deps, 203 const LangOptions &LO, 204 CheckerRegistry::CheckerInfoSet &ret) { 205 206 for (const CheckerRegistry::CheckerInfo *dependency : deps) { 207 208 if (dependency->isDisabled(LO)) 209 return false; 210 211 // Collect dependencies recursively. 212 if (!collectDependenciesImpl(dependency->Dependencies, LO, ret)) 213 return false; 214 215 ret.insert(dependency); 216 } 217 218 return true; 219 } 220 221 CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const { 222 223 CheckerInfoSet enabledCheckers; 224 225 for (const CheckerInfo &checker : Checkers) { 226 if (!checker.isEnabled(LangOpts)) 227 continue; 228 229 // Recursively enable it's dependencies. 230 llvm::Optional<CheckerInfoSet> deps = 231 collectDependencies(checker, LangOpts); 232 233 if (!deps) { 234 // If we failed to enable any of the dependencies, don't enable this 235 // checker. 236 continue; 237 } 238 239 // Note that set_union also preserves the order of insertion. 240 enabledCheckers.set_union(*deps); 241 242 // Enable the checker. 243 enabledCheckers.insert(&checker); 244 } 245 246 return enabledCheckers; 247 } 248 249 void CheckerRegistry::addChecker(InitializationFunction Rfn, 250 ShouldRegisterFunction Sfn, StringRef Name, 251 StringRef Desc, StringRef DocsUri) { 252 Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri); 253 254 // Record the presence of the checker in its packages. 255 StringRef packageName, leafName; 256 std::tie(packageName, leafName) = Name.rsplit(PackageSeparator); 257 while (!leafName.empty()) { 258 Packages[packageName] += 1; 259 std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); 260 } 261 } 262 263 void CheckerRegistry::initializeManager(CheckerManager &checkerMgr) const { 264 // Collect checkers enabled by the options. 265 CheckerInfoSet enabledCheckers = getEnabledCheckers(); 266 267 // Initialize the CheckerManager with all enabled checkers. 268 for (const auto *i : enabledCheckers) { 269 checkerMgr.setCurrentCheckName(CheckName(i->FullName)); 270 i->Initialize(checkerMgr); 271 } 272 } 273 274 void CheckerRegistry::validateCheckerOptions() const { 275 for (const auto &config : AnOpts.Config) { 276 size_t pos = config.getKey().find(':'); 277 if (pos == StringRef::npos) 278 continue; 279 280 bool hasChecker = false; 281 StringRef checkerName = config.getKey().substr(0, pos); 282 for (const auto &checker : Checkers) { 283 if (checker.FullName.startswith(checkerName) && 284 (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { 285 hasChecker = true; 286 break; 287 } 288 } 289 if (!hasChecker) 290 Diags.Report(diag::err_unknown_analyzer_checker) << checkerName; 291 } 292 } 293 294 void CheckerRegistry::printHelp(raw_ostream &out, 295 size_t maxNameChars) const { 296 // FIXME: Print available packages. 297 298 out << "CHECKERS:\n"; 299 300 // Find the maximum option length. 301 size_t optionFieldWidth = 0; 302 for (const auto &i : Checkers) { 303 // Limit the amount of padding we are willing to give up for alignment. 304 // Package.Name Description [Hidden] 305 size_t nameLength = i.FullName.size(); 306 if (nameLength <= maxNameChars) 307 optionFieldWidth = std::max(optionFieldWidth, nameLength); 308 } 309 310 const size_t initialPad = 2; 311 for (const auto &i : Checkers) { 312 out.indent(initialPad) << i.FullName; 313 314 int pad = optionFieldWidth - i.FullName.size(); 315 316 // Break on long option names. 317 if (pad < 0) { 318 out << '\n'; 319 pad = optionFieldWidth + initialPad; 320 } 321 out.indent(pad + 2) << i.Desc; 322 323 out << '\n'; 324 } 325 } 326 327 void CheckerRegistry::printList(raw_ostream &out) const { 328 // Collect checkers enabled by the options. 329 CheckerInfoSet enabledCheckers = getEnabledCheckers(); 330 331 for (const auto *i : enabledCheckers) 332 out << i->FullName << '\n'; 333 } 334