Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -139,6 +139,9 @@ 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 options">; + def analyzer_config_compatibility_mode : Separate<["-"], "analyzer-config-compatibility-mode">, HelpText<"Don't emit errors on invalid analyzer-config inputs">; Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -198,6 +198,7 @@ unsigned DisableAllChecks : 1; unsigned ShowCheckerHelp : 1; + unsigned ShowCheckerOptionList : 1; unsigned ShowEnabledCheckerList : 1; unsigned ShowConfigOptionsList : 1; unsigned ShouldEmitErrorsOnInvalidConfigValue : 1; Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -248,6 +248,7 @@ 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 Index: include/clang/StaticAnalyzer/Frontend/FrontendActions.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/FrontendActions.h +++ include/clang/StaticAnalyzer/Frontend/FrontendActions.h @@ -61,6 +61,10 @@ DiagnosticsEngine &diags, const LangOptions &LangOpts); void printAnalyzerConfigList(raw_ostream &OS); +void printCheckerConfigList(raw_ostream &OS, ArrayRef plugins, + AnalyzerOptions &opts, + DiagnosticsEngine &diags, + const LangOptions &LangOpts); } // end GR namespace Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -285,6 +285,7 @@ } Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help); + 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 = Index: lib/FrontendTool/ExecuteCompilerInvocation.cpp =================================================================== --- lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -245,6 +245,16 @@ 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 (Clang->getAnalyzerOpts()->ShowEnabledCheckerList) { ento::printEnabledCheckerList(llvm::outs(), Index: lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -65,6 +65,15 @@ .printEnabledCheckerList(out); } +void ento::printCheckerConfigList(raw_ostream &OS, + ArrayRef 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 " Index: lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -427,3 +427,57 @@ for (const auto *i : enabledCheckers) out << i->FullName << '\n'; } + +void CheckerRegistry::printCheckerOptionList(raw_ostream &out) const { + out << "OVERVIEW: Clang Static Analyzer Checker Option List\n\n"; + out << "USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config " + "\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" + "\n\n"; + out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang " + "OPTION1=VALUE, -Xclang -analyzer-config -Xclang " + "OPTION2=VALUE, ...\n\n"; + out << "OPTIONS:\n\n"; + + constexpr size_t MinLineWidth = 90; + constexpr size_t PadForOpt = 2; + constexpr size_t OptionWidth = 50; + constexpr size_t PadForDesc = PadForOpt + OptionWidth; + static_assert(MinLineWidth > PadForDesc, "MinLineWidth must be greater!"); + + llvm::formatted_raw_ostream FOut(out); + + for (const CheckerInfo &Checker : Checkers) { + for (const CmdLineOption &Option : Checker.CmdLineOptions) { + FOut.PadToColumn(PadForOpt) << Checker.FullName << ':' + << Option.OptionName; + + // If the buffer's length is greater then PadForDesc, print a newline. + if (FOut.getColumn() > PadForDesc) + FOut << '\n'; + + FOut.PadToColumn(PadForDesc) << "(" << Option.OptionType << ") "; + + for (char C : Option.Description) { + if (FOut.getColumn() > MinLineWidth && C == ' ') { + FOut << '\n'; + FOut.PadToColumn(PadForDesc); + continue; + } + FOut << C; + } + + if (!Option.Description.empty()) + FOut << ' '; + if (FOut.getColumn() > MinLineWidth) { + FOut << '\n'; + FOut.PadToColumn(PadForDesc); + } + FOut << "(default: " + << (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + << ")\n\n"; + } + } +} Index: test/Analysis/analyzer-checker-option-help.c =================================================================== --- /dev/null +++ test/Analysis/analyzer-checker-option-help.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -analyzer-checker-option-help 2>&1 | FileCheck %s + +// CHECK: OVERVIEW: Clang Static Analyzer Checker Option List +// +// CHECK: USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config +// +// CHECK: clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, +// CHECK-SAME: -analyzer-config OPTION2=VALUE, ... +// +// CHECK: clang [CLANG_OPTIONS] -Xclang -analyzer-config +// CHECK-SAME: -Xclang +// +// CHECK: clang [CLANG_OPTIONS] -Xclang -analyzer-config +// CHECK-SAME: -Xclang OPTION1=VALUE, -Xclang -analyzer-config +// CHECK-SAME: -Xclang 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)