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