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/Driver/DriverDiagnostic.h"
13 #include "clang/Frontend/FrontendDiagnostic.h"
14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15 #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
16 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SetVector.h"
19 #include "llvm/ADT/StringMap.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/DynamicLibrary.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include <algorithm>
25 
26 using namespace clang;
27 using namespace ento;
28 using llvm::sys::DynamicLibrary;
29 
30 using RegisterCheckersFn = void (*)(CheckerRegistry &);
31 
32 static bool isCompatibleAPIVersion(const char *VersionString) {
33   // If the version string is null, its not an analyzer plugin.
34   if (!VersionString)
35     return false;
36 
37   // For now, none of the static analyzer API is considered stable.
38   // Versions must match exactly.
39   return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
40 }
41 
42 namespace {
43 template <class T> struct FullNameLT {
44   bool operator()(const T &Lhs, const T &Rhs) {
45     return Lhs.FullName < Rhs.FullName;
46   }
47 };
48 
49 using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>;
50 using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>;
51 } // end of anonymous namespace
52 
53 template <class CheckerOrPackageInfoList>
54 static
55     typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value,
56                               typename CheckerOrPackageInfoList::const_iterator,
57                               typename CheckerOrPackageInfoList::iterator>::type
58     binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) {
59 
60   using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type;
61   using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>;
62 
63   assert(std::is_sorted(Collection.begin(), Collection.end(),
64                         CheckerOrPackageFullNameLT{}) &&
65          "In order to efficiently gather checkers/packages, this function "
66          "expects them to be already sorted!");
67 
68   return llvm::lower_bound(Collection, CheckerOrPackage(FullName),
69                            CheckerOrPackageFullNameLT{});
70 }
71 
72 static constexpr char PackageSeparator = '.';
73 
74 static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker,
75                         StringRef PackageName) {
76   // Does the checker's full name have the package as a prefix?
77   if (!Checker.FullName.startswith(PackageName))
78     return false;
79 
80   // Is the package actually just the name of a specific checker?
81   if (Checker.FullName.size() == PackageName.size())
82     return true;
83 
84   // Is the checker in the package (or a subpackage)?
85   if (Checker.FullName[PackageName.size()] == PackageSeparator)
86     return true;
87 
88   return false;
89 }
90 
91 CheckerRegistry::CheckerInfoListRange
92 CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
93   auto It = binaryFind(Checkers, CmdLineArg);
94 
95   if (!isInPackage(*It, CmdLineArg))
96     return {Checkers.end(), Checkers.end()};
97 
98   // See how large the package is.
99   // If the package doesn't exist, assume the option refers to a single
100   // checker.
101   size_t Size = 1;
102   llvm::StringMap<size_t>::const_iterator PackageSize =
103       PackageSizes.find(CmdLineArg);
104 
105   if (PackageSize != PackageSizes.end())
106     Size = PackageSize->getValue();
107 
108   return {It, It + Size};
109 }
110 
111 CheckerRegistry::CheckerRegistry(
112     ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags,
113     AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
114     ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
115     : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) {
116 
117   // Register builtin checkers.
118 #define GET_CHECKERS
119 #define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN)                 \
120   addChecker(register##CLASS, shouldRegister##CLASS, FULLNAME, HELPTEXT,       \
121              DOC_URI, IS_HIDDEN);
122 
123 #define GET_PACKAGES
124 #define PACKAGE(FULLNAME) addPackage(FULLNAME);
125 
126 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
127 #undef CHECKER
128 #undef GET_CHECKERS
129 #undef PACKAGE
130 #undef GET_PACKAGES
131 
132   // Register checkers from plugins.
133   for (const std::string &Plugin : Plugins) {
134     // Get access to the plugin.
135     std::string ErrorMsg;
136     DynamicLibrary Lib =
137         DynamicLibrary::getPermanentLibrary(Plugin.c_str(), &ErrorMsg);
138     if (!Lib.isValid()) {
139       Diags.Report(diag::err_fe_unable_to_load_plugin) << Plugin << ErrorMsg;
140       continue;
141     }
142 
143     // See if its compatible with this build of clang.
144     const char *PluginAPIVersion = static_cast<const char *>(
145         Lib.getAddressOfSymbol("clang_analyzerAPIVersionString"));
146 
147     if (!isCompatibleAPIVersion(PluginAPIVersion)) {
148       Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
149           << llvm::sys::path::filename(Plugin);
150       Diags.Report(diag::note_incompatible_analyzer_plugin_api)
151           << CLANG_ANALYZER_API_VERSION_STRING << PluginAPIVersion;
152       continue;
153     }
154 
155     // Register its checkers.
156     RegisterCheckersFn RegisterPluginCheckers =
157         reinterpret_cast<RegisterCheckersFn>(
158             Lib.getAddressOfSymbol("clang_registerCheckers"));
159     if (RegisterPluginCheckers)
160       RegisterPluginCheckers(*this);
161   }
162 
163   // Register statically linked checkers, that aren't generated from the tblgen
164   // file, but rather passed their registry function as a parameter in
165   // checkerRegistrationFns.
166 
167   for (const auto &Fn : CheckerRegistrationFns)
168     Fn(*this);
169 
170   // Sort checkers for efficient collection.
171   // FIXME: Alphabetical sort puts 'experimental' in the middle.
172   // Would it be better to name it '~experimental' or something else
173   // that's ASCIIbetically last?
174   llvm::sort(Packages, PackageNameLT{});
175   llvm::sort(Checkers, CheckerNameLT{});
176 
177 #define GET_CHECKER_DEPENDENCIES
178 
179 #define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)                               \
180   addDependency(FULLNAME, DEPENDENCY);
181 
182 #define GET_CHECKER_OPTIONS
183 #define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN)  \
184   addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN);
185 
186 #define GET_PACKAGE_OPTIONS
187 #define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN)  \
188   addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN);
189 
190 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
191 #undef CHECKER_DEPENDENCY
192 #undef GET_CHECKER_DEPENDENCIES
193 #undef CHECKER_OPTION
194 #undef GET_CHECKER_OPTIONS
195 #undef PACKAGE_OPTION
196 #undef GET_PACKAGE_OPTIONS
197 
198   resolveDependencies();
199   resolveCheckerAndPackageOptions();
200 
201   // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
202   // command line.
203   for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {
204     CheckerInfoListRange CheckerForCmdLineArg =
205         getMutableCheckersForCmdLineArg(Opt.first);
206 
207     if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
208       Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first;
209       Diags.Report(diag::note_suggest_disabling_all_checkers);
210     }
211 
212     for (CheckerInfo &checker : CheckerForCmdLineArg) {
213       checker.State = Opt.second ? StateFromCmdLine::State_Enabled
214                                  : StateFromCmdLine::State_Disabled;
215     }
216   }
217 }
218 
219 /// Collects dependencies in \p ret, returns false on failure.
220 static bool
221 collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
222                         const LangOptions &LO,
223                         CheckerRegistry::CheckerInfoSet &Ret);
224 
225 /// Collects dependenies in \p enabledCheckers. Return None on failure.
226 LLVM_NODISCARD
227 static llvm::Optional<CheckerRegistry::CheckerInfoSet>
228 collectDependencies(const CheckerRegistry::CheckerInfo &checker,
229                     const LangOptions &LO) {
230 
231   CheckerRegistry::CheckerInfoSet Ret;
232   // Add dependencies to the enabled checkers only if all of them can be
233   // enabled.
234   if (!collectDependenciesImpl(checker.Dependencies, LO, Ret))
235     return None;
236 
237   return Ret;
238 }
239 
240 static bool
241 collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
242                         const LangOptions &LO,
243                         CheckerRegistry::CheckerInfoSet &Ret) {
244 
245   for (const CheckerRegistry::CheckerInfo *Dependency : Deps) {
246 
247     if (Dependency->isDisabled(LO))
248       return false;
249 
250     // Collect dependencies recursively.
251     if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret))
252       return false;
253 
254     Ret.insert(Dependency);
255   }
256 
257   return true;
258 }
259 
260 CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
261 
262   CheckerInfoSet EnabledCheckers;
263 
264   for (const CheckerInfo &Checker : Checkers) {
265     if (!Checker.isEnabled(LangOpts))
266       continue;
267 
268     // Recursively enable its dependencies.
269     llvm::Optional<CheckerInfoSet> Deps =
270         collectDependencies(Checker, LangOpts);
271 
272     if (!Deps) {
273       // If we failed to enable any of the dependencies, don't enable this
274       // checker.
275       continue;
276     }
277 
278     // Note that set_union also preserves the order of insertion.
279     EnabledCheckers.set_union(*Deps);
280 
281     // Enable the checker.
282     EnabledCheckers.insert(&Checker);
283   }
284 
285   return EnabledCheckers;
286 }
287 
288 void CheckerRegistry::resolveDependencies() {
289   for (const std::pair<StringRef, StringRef> &Entry : Dependencies) {
290     auto CheckerIt = binaryFind(Checkers, Entry.first);
291     assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first &&
292            "Failed to find the checker while attempting to set up its "
293            "dependencies!");
294 
295     auto DependencyIt = binaryFind(Checkers, Entry.second);
296     assert(DependencyIt != Checkers.end() &&
297            DependencyIt->FullName == Entry.second &&
298            "Failed to find the dependency of a checker!");
299 
300     CheckerIt->Dependencies.emplace_back(&*DependencyIt);
301   }
302 
303   Dependencies.clear();
304 }
305 
306 void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
307   Dependencies.emplace_back(FullName, Dependency);
308 }
309 
310 /// Insert the checker/package option to AnalyzerOptions' config table, and
311 /// validate it, if the user supplied it on the command line.
312 static void insertAndValidate(StringRef FullName,
313                               const CheckerRegistry::CmdLineOption &Option,
314                               AnalyzerOptions &AnOpts,
315                               DiagnosticsEngine &Diags) {
316 
317   std::string FullOption = (FullName + ":" + Option.OptionName).str();
318 
319   auto It =
320       AnOpts.Config.insert({FullOption, std::string(Option.DefaultValStr)});
321 
322   // Insertation was successful -- CmdLineOption's constructor will validate
323   // whether values received from plugins or TableGen files are correct.
324   if (It.second)
325     return;
326 
327   // Insertion failed, the user supplied this package/checker option on the
328   // command line. If the supplied value is invalid, we'll restore the option
329   // to it's default value, and if we're in non-compatibility mode, we'll also
330   // emit an error.
331 
332   StringRef SuppliedValue = It.first->getValue();
333 
334   if (Option.OptionType == "bool") {
335     if (SuppliedValue != "true" && SuppliedValue != "false") {
336       if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) {
337         Diags.Report(diag::err_analyzer_checker_option_invalid_input)
338             << FullOption << "a boolean value";
339       }
340 
341       It.first->setValue(std::string(Option.DefaultValStr));
342     }
343     return;
344   }
345 
346   if (Option.OptionType == "int") {
347     int Tmp;
348     bool HasFailed = SuppliedValue.getAsInteger(0, Tmp);
349     if (HasFailed) {
350       if (AnOpts.ShouldEmitErrorsOnInvalidConfigValue) {
351         Diags.Report(diag::err_analyzer_checker_option_invalid_input)
352             << FullOption << "an integer value";
353       }
354 
355       It.first->setValue(std::string(Option.DefaultValStr));
356     }
357     return;
358   }
359 }
360 
361 template <class T>
362 static void
363 insertOptionToCollection(StringRef FullName, T &Collection,
364                          const CheckerRegistry::CmdLineOption &Option,
365                          AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) {
366   auto It = binaryFind(Collection, FullName);
367   assert(It != Collection.end() &&
368          "Failed to find the checker while attempting to add a command line "
369          "option to it!");
370 
371   insertAndValidate(FullName, Option, AnOpts, Diags);
372 
373   It->CmdLineOptions.emplace_back(Option);
374 }
375 
376 void CheckerRegistry::resolveCheckerAndPackageOptions() {
377   for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
378        CheckerOptions) {
379     insertOptionToCollection(CheckerOptEntry.first, Checkers,
380                              CheckerOptEntry.second, AnOpts, Diags);
381   }
382   CheckerOptions.clear();
383 
384   for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
385        PackageOptions) {
386     insertOptionToCollection(PackageOptEntry.first, Packages,
387                              PackageOptEntry.second, AnOpts, Diags);
388   }
389   PackageOptions.clear();
390 }
391 
392 void CheckerRegistry::addPackage(StringRef FullName) {
393   Packages.emplace_back(PackageInfo(FullName));
394 }
395 
396 void CheckerRegistry::addPackageOption(StringRef OptionType,
397                                        StringRef PackageFullName,
398                                        StringRef OptionName,
399                                        StringRef DefaultValStr,
400                                        StringRef Description,
401                                        StringRef DevelopmentStatus,
402                                        bool IsHidden) {
403   PackageOptions.emplace_back(
404       PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
405                                      Description, DevelopmentStatus, IsHidden});
406 }
407 
408 void CheckerRegistry::addChecker(InitializationFunction Rfn,
409                                  ShouldRegisterFunction Sfn, StringRef Name,
410                                  StringRef Desc, StringRef DocsUri,
411                                  bool IsHidden) {
412   Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
413 
414   // Record the presence of the checker in its packages.
415   StringRef PackageName, LeafName;
416   std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
417   while (!LeafName.empty()) {
418     PackageSizes[PackageName] += 1;
419     std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
420   }
421 }
422 
423 void CheckerRegistry::addCheckerOption(StringRef OptionType,
424                                        StringRef CheckerFullName,
425                                        StringRef OptionName,
426                                        StringRef DefaultValStr,
427                                        StringRef Description,
428                                        StringRef DevelopmentStatus,
429                                        bool IsHidden) {
430   CheckerOptions.emplace_back(
431       CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
432                                      Description, DevelopmentStatus, IsHidden});
433 }
434 
435 void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
436   // Collect checkers enabled by the options.
437   CheckerInfoSet enabledCheckers = getEnabledCheckers();
438 
439   // Initialize the CheckerManager with all enabled checkers.
440   for (const auto *Checker : enabledCheckers) {
441     CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
442     Checker->Initialize(CheckerMgr);
443   }
444 }
445 
446 static void
447 isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList,
448                     StringRef SuppliedChecker, StringRef SuppliedOption,
449                     const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) {
450 
451   if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue)
452     return;
453 
454   using CmdLineOption = CheckerRegistry::CmdLineOption;
455 
456   auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) {
457     return Opt.OptionName == SuppliedOption;
458   };
459 
460   auto OptionIt = llvm::find_if(OptionList, SameOptName);
461 
462   if (OptionIt == OptionList.end()) {
463     Diags.Report(diag::err_analyzer_checker_option_unknown)
464         << SuppliedChecker << SuppliedOption;
465     return;
466   }
467 }
468 
469 void CheckerRegistry::validateCheckerOptions() const {
470   for (const auto &Config : AnOpts.Config) {
471 
472     StringRef SuppliedCheckerOrPackage;
473     StringRef SuppliedOption;
474     std::tie(SuppliedCheckerOrPackage, SuppliedOption) =
475         Config.getKey().split(':');
476 
477     if (SuppliedOption.empty())
478       continue;
479 
480     // AnalyzerOptions' config table contains the user input, so an entry could
481     // look like this:
482     //
483     //   cor:NoFalsePositives=true
484     //
485     // Since lower_bound would look for the first element *not less* than "cor",
486     // it would return with an iterator to the first checker in the core, so we
487     // we really have to use find here, which uses operator==.
488     auto CheckerIt =
489         llvm::find(Checkers, CheckerInfo(SuppliedCheckerOrPackage));
490     if (CheckerIt != Checkers.end()) {
491       isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,
492                           SuppliedOption, AnOpts, Diags);
493       continue;
494     }
495 
496     auto PackageIt =
497         llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage));
498     if (PackageIt != Packages.end()) {
499       isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,
500                           SuppliedOption, AnOpts, Diags);
501       continue;
502     }
503 
504     Diags.Report(diag::err_unknown_analyzer_checker_or_package)
505         << SuppliedCheckerOrPackage;
506   }
507 }
508 
509 void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out,
510                                                size_t MaxNameChars) const {
511   // FIXME: Print available packages.
512 
513   Out << "CHECKERS:\n";
514 
515   // Find the maximum option length.
516   size_t OptionFieldWidth = 0;
517   for (const auto &Checker : Checkers) {
518     // Limit the amount of padding we are willing to give up for alignment.
519     //   Package.Name     Description  [Hidden]
520     size_t NameLength = Checker.FullName.size();
521     if (NameLength <= MaxNameChars)
522       OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
523   }
524 
525   const size_t InitialPad = 2;
526 
527   auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
528                    StringRef Description) {
529     AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
530                                          InitialPad, OptionFieldWidth);
531     Out << '\n';
532   };
533 
534   for (const auto &Checker : Checkers) {
535     // The order of this if branches is significant, we wouldn't like to display
536     // developer checkers even in the alpha output. For example,
537     // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
538     // by default, and users (even when the user is a developer of an alpha
539     // checker) shouldn't normally tinker with whether they should be enabled.
540 
541     if (Checker.IsHidden) {
542       if (AnOpts.ShowCheckerHelpDeveloper)
543         Print(Out, Checker, Checker.Desc);
544       continue;
545     }
546 
547     if (Checker.FullName.startswith("alpha")) {
548       if (AnOpts.ShowCheckerHelpAlpha)
549         Print(Out, Checker,
550               ("(Enable only for development!) " + Checker.Desc).str());
551       continue;
552     }
553 
554     if (AnOpts.ShowCheckerHelp)
555         Print(Out, Checker, Checker.Desc);
556   }
557 }
558 
559 void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const {
560   // Collect checkers enabled by the options.
561   CheckerInfoSet EnabledCheckers = getEnabledCheckers();
562 
563   for (const auto *i : EnabledCheckers)
564     Out << i->FullName << '\n';
565 }
566 
567 void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const {
568   Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
569   Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
570   Out << "       -analyzer-config OPTION1=VALUE, -analyzer-config "
571          "OPTION2=VALUE, ...\n\n";
572   Out << "OPTIONS:\n\n";
573 
574   std::multimap<StringRef, const CmdLineOption &> OptionMap;
575 
576   for (const CheckerInfo &Checker : Checkers) {
577     for (const CmdLineOption &Option : Checker.CmdLineOptions) {
578       OptionMap.insert({Checker.FullName, Option});
579     }
580   }
581 
582   for (const PackageInfo &Package : Packages) {
583     for (const CmdLineOption &Option : Package.CmdLineOptions) {
584       OptionMap.insert({Package.FullName, Option});
585     }
586   }
587 
588   auto Print = [] (llvm::raw_ostream &Out, StringRef FullOption, StringRef Desc) {
589     AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
590                                          /*InitialPad*/ 2,
591                                          /*EntryWidth*/ 50,
592                                          /*MinLineWidth*/ 90);
593     Out << "\n\n";
594   };
595   for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
596        OptionMap) {
597     const CmdLineOption &Option = Entry.second;
598     std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
599 
600     std::string Desc =
601         ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
602          (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
603             .str();
604 
605     // The list of these if branches is significant, we wouldn't like to
606     // display hidden alpha checker options for
607     // -analyzer-checker-option-help-alpha.
608 
609     if (Option.IsHidden) {
610       if (AnOpts.ShowCheckerOptionDeveloperList)
611         Print(Out, FullOption, Desc);
612       continue;
613     }
614 
615     if (Option.DevelopmentStatus == "alpha" ||
616         Entry.first.startswith("alpha")) {
617       if (AnOpts.ShowCheckerOptionAlphaList)
618         Print(Out, FullOption,
619               llvm::Twine("(Enable only for development!) " + Desc).str());
620       continue;
621     }
622 
623     if (AnOpts.ShowCheckerOptionList)
624       Print(Out, FullOption, Desc);
625   }
626 }
627