242 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- CheckerRegistry.h - Maintains all available checkers -----*- C++ -*-===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
 | |
| #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
 | |
| #include "llvm/ADT/Twine.h"
 | |
| #include <map>
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace ento;
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Methods of CmdLineOption, PackageInfo and CheckerInfo.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| LLVM_DUMP_METHOD void CmdLineOption::dump() const {
 | |
|   dumpToStream(llvm::errs());
 | |
| }
 | |
| 
 | |
| LLVM_DUMP_METHOD void
 | |
| CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const {
 | |
|   // The description can be just checked in Checkers.inc, the point here is to
 | |
|   // debug whether we succeeded in parsing it.
 | |
|   Out << OptionName << " (" << OptionType << ", "
 | |
|       << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \""
 | |
|       << DefaultValStr;
 | |
| }
 | |
| 
 | |
| static StringRef toString(StateFromCmdLine Kind) {
 | |
|   switch (Kind) {
 | |
|   case StateFromCmdLine::State_Disabled:
 | |
|     return "Disabled";
 | |
|   case StateFromCmdLine::State_Enabled:
 | |
|     return "Enabled";
 | |
|   case StateFromCmdLine::State_Unspecified:
 | |
|     return "Unspecified";
 | |
|   }
 | |
|   llvm_unreachable("Unhandled StateFromCmdLine enum");
 | |
| }
 | |
| 
 | |
| LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
 | |
| 
 | |
| LLVM_DUMP_METHOD void CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const {
 | |
|   // The description can be just checked in Checkers.inc, the point here is to
 | |
|   // debug whether we succeeded in parsing it. Same with documentation uri.
 | |
|   Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "")
 | |
|       << ")\n";
 | |
|   Out << "  Options:\n";
 | |
|   for (const CmdLineOption &Option : CmdLineOptions) {
 | |
|     Out << "    ";
 | |
|     Option.dumpToStream(Out);
 | |
|     Out << '\n';
 | |
|   }
 | |
|   Out << "  Dependencies:\n";
 | |
|   for (const CheckerInfo *Dependency : Dependencies) {
 | |
|     Out << "  " << Dependency->FullName << '\n';
 | |
|   }
 | |
|   Out << "  Weak dependencies:\n";
 | |
|   for (const CheckerInfo *Dependency : WeakDependencies) {
 | |
|     Out << "    " << Dependency->FullName << '\n';
 | |
|   }
 | |
| }
 | |
| 
 | |
| LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
 | |
| 
 | |
| LLVM_DUMP_METHOD void PackageInfo::dumpToStream(llvm::raw_ostream &Out) const {
 | |
|   Out << FullName << "\n";
 | |
|   Out << "  Options:\n";
 | |
|   for (const CmdLineOption &Option : CmdLineOptions) {
 | |
|     Out << "    ";
 | |
|     Option.dumpToStream(Out);
 | |
|     Out << '\n';
 | |
|   }
 | |
| }
 | |
| 
 | |
| static constexpr char PackageSeparator = '.';
 | |
| 
 | |
| static bool isInPackage(const CheckerInfo &Checker, StringRef PackageName) {
 | |
|   // Does the checker's full name have the package as a prefix?
 | |
|   if (!Checker.FullName.startswith(PackageName))
 | |
|     return false;
 | |
| 
 | |
|   // Is the package actually just the name of a specific checker?
 | |
|   if (Checker.FullName.size() == PackageName.size())
 | |
|     return true;
 | |
| 
 | |
|   // Is the checker in the package (or a subpackage)?
 | |
|   if (Checker.FullName[PackageName.size()] == PackageSeparator)
 | |
|     return true;
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| CheckerInfoListRange
 | |
| CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
 | |
|   auto It = checker_registry::binaryFind(Checkers, CmdLineArg);
 | |
| 
 | |
|   if (!isInPackage(*It, CmdLineArg))
 | |
|     return {Checkers.end(), Checkers.end()};
 | |
| 
 | |
|   // See how large the package is.
 | |
|   // If the package doesn't exist, assume the option refers to a single
 | |
|   // checker.
 | |
|   size_t Size = 1;
 | |
|   llvm::StringMap<size_t>::const_iterator PackageSize =
 | |
|       PackageSizes.find(CmdLineArg);
 | |
| 
 | |
|   if (PackageSize != PackageSizes.end())
 | |
|     Size = PackageSize->getValue();
 | |
| 
 | |
|   return {It, It + Size};
 | |
| }
 | |
| //===----------------------------------------------------------------------===//
 | |
| // Printing functions.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| void CheckerRegistryData::printCheckerWithDescList(
 | |
|     const AnalyzerOptions &AnOpts, raw_ostream &Out,
 | |
|     size_t MaxNameChars) const {
 | |
|   // FIXME: Print available packages.
 | |
| 
 | |
|   Out << "CHECKERS:\n";
 | |
| 
 | |
|   // Find the maximum option length.
 | |
|   size_t OptionFieldWidth = 0;
 | |
|   for (const auto &Checker : Checkers) {
 | |
|     // Limit the amount of padding we are willing to give up for alignment.
 | |
|     //   Package.Name     Description  [Hidden]
 | |
|     size_t NameLength = Checker.FullName.size();
 | |
|     if (NameLength <= MaxNameChars)
 | |
|       OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
 | |
|   }
 | |
| 
 | |
|   const size_t InitialPad = 2;
 | |
| 
 | |
|   auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
 | |
|                    StringRef Description) {
 | |
|     AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
 | |
|                                          InitialPad, OptionFieldWidth);
 | |
|     Out << '\n';
 | |
|   };
 | |
| 
 | |
|   for (const auto &Checker : Checkers) {
 | |
|     // The order of this if branches is significant, we wouldn't like to display
 | |
|     // developer checkers even in the alpha output. For example,
 | |
|     // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
 | |
|     // by default, and users (even when the user is a developer of an alpha
 | |
|     // checker) shouldn't normally tinker with whether they should be enabled.
 | |
| 
 | |
|     if (Checker.IsHidden) {
 | |
|       if (AnOpts.ShowCheckerHelpDeveloper)
 | |
|         Print(Out, Checker, Checker.Desc);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (Checker.FullName.startswith("alpha")) {
 | |
|       if (AnOpts.ShowCheckerHelpAlpha)
 | |
|         Print(Out, Checker,
 | |
|               ("(Enable only for development!) " + Checker.Desc).str());
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (AnOpts.ShowCheckerHelp)
 | |
|       Print(Out, Checker, Checker.Desc);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
 | |
|   for (const auto *i : EnabledCheckers)
 | |
|     Out << i->FullName << '\n';
 | |
| }
 | |
| 
 | |
| void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts,
 | |
|                                                  raw_ostream &Out) const {
 | |
|   Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
 | |
|   Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
 | |
|   Out << "       -analyzer-config OPTION1=VALUE, -analyzer-config "
 | |
|          "OPTION2=VALUE, ...\n\n";
 | |
|   Out << "OPTIONS:\n\n";
 | |
| 
 | |
|   // It's usually ill-advised to use multimap, but clang will terminate after
 | |
|   // this function.
 | |
|   std::multimap<StringRef, const CmdLineOption &> OptionMap;
 | |
| 
 | |
|   for (const CheckerInfo &Checker : Checkers) {
 | |
|     for (const CmdLineOption &Option : Checker.CmdLineOptions) {
 | |
|       OptionMap.insert({Checker.FullName, Option});
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (const PackageInfo &Package : Packages) {
 | |
|     for (const CmdLineOption &Option : Package.CmdLineOptions) {
 | |
|       OptionMap.insert({Package.FullName, Option});
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   auto Print = [](llvm::raw_ostream &Out, StringRef FullOption,
 | |
|                   StringRef Desc) {
 | |
|     AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
 | |
|                                          /*InitialPad*/ 2,
 | |
|                                          /*EntryWidth*/ 50,
 | |
|                                          /*MinLineWidth*/ 90);
 | |
|     Out << "\n\n";
 | |
|   };
 | |
|   for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
 | |
|        OptionMap) {
 | |
|     const CmdLineOption &Option = Entry.second;
 | |
|     std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
 | |
| 
 | |
|     std::string Desc =
 | |
|         ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
 | |
|          (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
 | |
|             .str();
 | |
| 
 | |
|     // The list of these if branches is significant, we wouldn't like to
 | |
|     // display hidden alpha checker options for
 | |
|     // -analyzer-checker-option-help-alpha.
 | |
| 
 | |
|     if (Option.IsHidden) {
 | |
|       if (AnOpts.ShowCheckerOptionDeveloperList)
 | |
|         Print(Out, FullOption, Desc);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (Option.DevelopmentStatus == "alpha" ||
 | |
|         Entry.first.startswith("alpha")) {
 | |
|       if (AnOpts.ShowCheckerOptionAlphaList)
 | |
|         Print(Out, FullOption,
 | |
|               llvm::Twine("(Enable only for development!) " + Desc).str());
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (AnOpts.ShowCheckerOptionList)
 | |
|       Print(Out, FullOption, Desc);
 | |
|   }
 | |
| }
 |