[analyzer] Add a new frontend flag to display all checker options

Add the new frontend flag -analyzer-checker-option-help to display all
checker/package options.

Differential Revision: https://reviews.llvm.org/D57858

llvm-svn: 361552
This commit is contained in:
Kristof Umann 2019-05-23 20:47:28 +00:00
parent dab31924e9
commit e8df27d925
12 changed files with 168 additions and 58 deletions

View File

@ -143,6 +143,9 @@ def analyzer_list_enabled_checkers : Flag<["-"], "analyzer-list-enabled-checkers
def analyzer_config : Separate<["-"], "analyzer-config">,
HelpText<"Choose analyzer options to enable">;
def analyzer_checker_option_help : Flag<["-"], "analyzer-checker-option-help">,
HelpText<"Display the list of checker and package options">;
def analyzer_config_compatibility_mode : Separate<["-"], "analyzer-config-compatibility-mode">,
HelpText<"Don't emit errors on invalid analyzer-config inputs">;

View File

@ -166,6 +166,29 @@ public:
static std::vector<StringRef>
getRegisteredCheckers(bool IncludeExperimental = false);
/// Convenience function for printing options or checkers and their
/// description in a formatted manner. If \p MinLineWidth is set to 0, no line
/// breaks are introduced for the description.
///
/// Format, depending whether the option name's length is less then
/// \p OptionWidth:
///
/// <padding>EntryName<padding>Description
/// <---------padding--------->Description
/// <---------padding--------->Description
///
/// <padding>VeryVeryLongOptionName
/// <---------padding--------->Description
/// <---------padding--------->Description
/// ^~~~~~~~ InitialPad
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~ EntryWidth
/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~MinLineWidth
static void printFormattedEntry(
llvm::raw_ostream &Out,
std::pair<StringRef, StringRef> EntryDescPair,
size_t EntryWidth, size_t InitialPad, size_t MinLineWidth = 0);
/// Pair of checker name and enable/disable.
std::vector<std::pair<std::string, bool>> CheckersControlList;
@ -199,6 +222,7 @@ public:
unsigned ShowCheckerHelp : 1;
unsigned ShowCheckerHelpHidden : 1;
unsigned ShowEnabledCheckerList : 1;
unsigned ShowCheckerOptionList : 1;
unsigned ShowConfigOptionsList : 1;
unsigned ShouldEmitErrorsOnInvalidConfigValue : 1;
unsigned AnalyzeAll : 1;
@ -262,11 +286,11 @@ public:
AnalyzerOptions()
: DisableAllChecks(false), ShowCheckerHelp(false),
ShowCheckerHelpHidden(false), ShowEnabledCheckerList(false),
ShowConfigOptionsList(false), AnalyzeAll(false),
AnalyzerDisplayProgress(false), AnalyzeNestedBlocks(false),
eagerlyAssumeBinOpBifurcation(false), TrimGraph(false),
visualizeExplodedGraphWithGraphViz(false), UnoptimizedCFG(false),
PrintStats(false), NoRetryExhausted(false) {
ShowCheckerOptionList(false), ShowConfigOptionsList(false),
AnalyzeAll(false), AnalyzerDisplayProgress(false),
AnalyzeNestedBlocks(false), eagerlyAssumeBinOpBifurcation(false),
TrimGraph(false), visualizeExplodedGraphWithGraphViz(false),
UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false) {
llvm::sort(AnalyzerConfigCmdFlags);
}

View File

@ -272,6 +272,7 @@ public:
void printCheckerWithDescList(raw_ostream &Out,
size_t MaxNameChars = 30) const;
void printEnabledCheckerList(raw_ostream &Out) const;
void printCheckerOptionList(raw_ostream &Out) const;
private:
/// Collect all enabled checkers. The returned container preserves the order

View File

@ -61,6 +61,10 @@ void printEnabledCheckerList(raw_ostream &OS, ArrayRef<std::string> plugins,
DiagnosticsEngine &diags,
const LangOptions &LangOpts);
void printAnalyzerConfigList(raw_ostream &OS);
void printCheckerConfigList(raw_ostream &OS, ArrayRef<std::string> plugins,
AnalyzerOptions &opts,
DiagnosticsEngine &diags,
const LangOptions &LangOpts);
} // end GR namespace

View File

@ -286,6 +286,7 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help);
Opts.ShowCheckerHelpHidden = Args.hasArg(OPT_analyzer_checker_help_hidden);
Opts.ShowCheckerOptionList = Args.hasArg(OPT_analyzer_checker_option_help);
Opts.ShowConfigOptionsList = Args.hasArg(OPT_analyzer_config_help);
Opts.ShowEnabledCheckerList = Args.hasArg(OPT_analyzer_list_enabled_checkers);
Opts.ShouldEmitErrorsOnInvalidConfigValue =

View File

@ -247,6 +247,16 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) {
return true;
}
// Honor -analyzer-checker-option-help.
if (Clang->getAnalyzerOpts()->ShowCheckerOptionList) {
ento::printCheckerConfigList(llvm::outs(),
Clang->getFrontendOpts().Plugins,
*Clang->getAnalyzerOpts(),
Clang->getDiagnostics(),
Clang->getLangOpts());
return true;
}
// Honor -analyzer-list-enabled-checkers.
if (AnOpts.ShowEnabledCheckerList) {
ento::printEnabledCheckerList(llvm::outs(),

View File

@ -19,6 +19,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
@ -48,6 +49,37 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
return Result;
}
void AnalyzerOptions::printFormattedEntry(
llvm::raw_ostream &Out,
std::pair<StringRef, StringRef> EntryDescPair,
size_t InitialPad, size_t EntryWidth, size_t MinLineWidth) {
llvm::formatted_raw_ostream FOut(Out);
const size_t PadForDesc = InitialPad + EntryWidth;
FOut.PadToColumn(InitialPad) << EntryDescPair.first;
// If the buffer's length is greater then PadForDesc, print a newline.
if (FOut.getColumn() > PadForDesc)
FOut << '\n';
FOut.PadToColumn(PadForDesc);
if (MinLineWidth == 0) {
FOut << EntryDescPair.second;
return;
}
for (char C : EntryDescPair.second) {
if (FOut.getColumn() > MinLineWidth && C == ' ') {
FOut << '\n';
FOut.PadToColumn(PadForDesc);
continue;
}
FOut << C;
}
}
ExplorationStrategyKind
AnalyzerOptions::getExplorationStrategy() const {
auto K =

View File

@ -18,7 +18,6 @@
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
@ -65,17 +64,20 @@ void ento::printEnabledCheckerList(raw_ostream &out,
.printEnabledCheckerList(out);
}
void ento::printCheckerConfigList(raw_ostream &OS,
ArrayRef<std::string> plugins,
AnalyzerOptions &opts,
DiagnosticsEngine &diags,
const LangOptions &LangOpts) {
CheckerRegistry(plugins, diags, opts, LangOpts)
.printCheckerOptionList(OS);
}
void ento::printAnalyzerConfigList(raw_ostream &out) {
out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n";
out << "USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config "
"<OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
out << " clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, "
"-analyzer-config OPTION2=VALUE, ...\n\n";
out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang"
"<OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang "
"OPTION1=VALUE, -Xclang -analyzer-config -Xclang "
"OPTION2=VALUE, ...\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";
using OptionAndDescriptionTy = std::pair<StringRef, std::string>;
@ -109,31 +111,10 @@ void ento::printAnalyzerConfigList(raw_ostream &out) {
return LHS.first < RHS.first;
});
constexpr size_t MinLineWidth = 70;
constexpr size_t PadForOpt = 2;
constexpr size_t OptionWidth = 30;
constexpr size_t PadForDesc = PadForOpt + OptionWidth;
static_assert(MinLineWidth > PadForDesc, "MinLineWidth must be greater!");
llvm::formatted_raw_ostream FOut(out);
for (const auto &Pair : PrintableOptions) {
FOut.PadToColumn(PadForOpt) << Pair.first;
// If the buffer's length is greater then PadForDesc, print a newline.
if (FOut.getColumn() > PadForDesc)
FOut << '\n';
FOut.PadToColumn(PadForDesc);
for (char C : Pair.second) {
if (FOut.getColumn() > MinLineWidth && C == ' ') {
FOut << '\n';
FOut.PadToColumn(PadForDesc);
continue;
}
FOut << C;
}
FOut << "\n\n";
AnalyzerOptions::printFormattedEntry(out, Pair, /*InitialPad*/ 2,
/*EntryWidth*/ 30,
/*MinLineWidth*/ 70);
out << "\n\n";
}
}

View File

@ -518,17 +518,8 @@ void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out,
if (!AnOpts.ShowCheckerHelpHidden && Checker.IsHidden)
continue;
Out.indent(InitialPad) << Checker.FullName;
int Pad = OptionFieldWidth - Checker.FullName.size();
// Break on long option names.
if (Pad < 0) {
Out << '\n';
Pad = OptionFieldWidth + InitialPad;
}
Out.indent(Pad + 2) << Checker.Desc;
AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Checker.Desc},
InitialPad, OptionFieldWidth);
Out << '\n';
}
}
@ -540,3 +531,41 @@ void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const {
for (const auto *i : EnabledCheckers)
Out << i->FullName << '\n';
}
void CheckerRegistry::printCheckerOptionList(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";
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});
}
}
for (const std::pair<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();
AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
/*InitialPad*/ 2,
/*EntryWidth*/ 50,
/*MinLineWidth*/ 90);
Out << "\n\n";
}
}

View File

@ -0,0 +1,19 @@
// RUN: %clang_cc1 -analyzer-checker-option-help 2>&1 | FileCheck %s
// CHECK: OVERVIEW: Clang Static Analyzer Checker and Package Option List
//
// CHECK: USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>
//
// CHECK: -analyzer-config OPTION1=VALUE, -analyzer-config
// CHECK-SAME: OPTION2=VALUE, ...
//
// CHECK: OPTIONS:
//
// CHECK: alpha.clone.CloneChecker:MinimumCloneComplexity
// CHECK-SAME: (int) Ensures that every clone has at least
// CHECK: the given complexity. Complexity is here
// CHECK: defined as the total amount of children
// CHECK: of a statement. This constraint assumes
// CHECK: the first statement in the group is representative
// CHECK: for all other statements in the group in
// CHECK: terms of complexity. (default: 50)

View File

@ -1,14 +1,11 @@
// RUN: %clang_cc1 -analyzer-config-help 2>&1 | FileCheck %s
// CHECK: OVERVIEW: Clang Static Analyzer -analyzer-config Option List
//
// CHECK: USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>
//
// CHECK: clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, -analyzer-config OPTION2=VALUE, ...
//
// CHECK: clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang<OPTION1=VALUE,OPTION2=VALUE,...>
//
// CHECK: clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang OPTION1=VALUE, -Xclang -analyzer-config -Xclang OPTION2=VALUE, ...
// CHECK: USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>
//
// CHECK: -analyzer-config OPTION1=VALUE, -analyzer-config
// CHECK-SAME: OPTION2=VALUE, ...
//
// CHECK: OPTIONS:
//

View File

@ -104,3 +104,12 @@ void caller() {
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-CORRECTED-BOOL-VALUE
// CHECK-CORRECTED-BOOL-VALUE: example.MyChecker:ExampleOption = false
// RUN: %clang_analyze_cc1 %s \
// RUN: -load %llvmshlibdir/CheckerOptionHandlingAnalyzerPlugin%pluginext\
// RUN: -analyzer-checker=example.MyChecker \
// RUN: -analyzer-checker-option-help \
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-CHECKER-OPTION-HELP
// CHECK-CHECKER-OPTION-HELP: example.MyChecker:ExampleOption (bool) This is an
// CHECK-CHECKER-OPTION-HELP-SAME: example checker opt. (default: false)