forked from OSchip/llvm-project
[analyzer] Emit an error for invalid -analyzer-config inputs
Differential Revision: https://reviews.llvm.org/D53280 llvm-svn: 348038
This commit is contained in:
parent
81a1a8e181
commit
d1a4b06c20
|
@ -299,6 +299,9 @@ def err_analyzer_config_no_value : Error<
|
|||
"analyzer-config option '%0' has a key but no value">;
|
||||
def err_analyzer_config_multiple_values : Error<
|
||||
"analyzer-config option '%0' should contain only one '='">;
|
||||
def err_analyzer_config_invalid_input : Error<
|
||||
"invalid input for analyzer-config option '%0', that expects %1 value">;
|
||||
def err_analyzer_config_unknown : Error<"unknown analyzer-config '%0'">;
|
||||
|
||||
def err_drv_invalid_hvx_length : Error<
|
||||
"-mhvx-length is not supported without a -mhvx/-mhvx= flag">;
|
||||
|
|
|
@ -138,6 +138,12 @@ def analyzer_list_enabled_checkers : Flag<["-"], "analyzer-list-enabled-checkers
|
|||
def analyzer_config : Separate<["-"], "analyzer-config">,
|
||||
HelpText<"Choose analyzer options to enable">;
|
||||
|
||||
def analyzer_config_compatibility_mode : Separate<["-"], "analyzer-config-compatibility-mode">,
|
||||
HelpText<"Don't emit errors on invalid analyzer-config inputs">;
|
||||
|
||||
def analyzer_config_compatibility_mode_EQ : Joined<["-"], "analyzer-config-compatibility-mode=">,
|
||||
Alias<analyzer_config_compatibility_mode>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Migrator Options
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -200,6 +200,7 @@ public:
|
|||
unsigned ShowCheckerHelp : 1;
|
||||
unsigned ShowEnabledCheckerList : 1;
|
||||
unsigned ShowConfigOptionsList : 1;
|
||||
unsigned ShouldEmitErrorsOnInvalidConfigValue : 1;
|
||||
unsigned AnalyzeAll : 1;
|
||||
unsigned AnalyzerDisplayProgress : 1;
|
||||
unsigned AnalyzeNestedBlocks : 1;
|
||||
|
@ -222,6 +223,7 @@ public:
|
|||
/// The mode of function selection used during inlining.
|
||||
AnalysisInliningMode InliningMode = NoRedundancy;
|
||||
|
||||
// Create a field for each -analyzer-config option.
|
||||
#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \
|
||||
SHALLOW_VAL, DEEP_VAL) \
|
||||
ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL)
|
||||
|
@ -233,13 +235,39 @@ public:
|
|||
#undef ANALYZER_OPTION
|
||||
#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
|
||||
|
||||
// Create an array of all -analyzer-config command line options. Sort it in
|
||||
// the constructor.
|
||||
std::vector<StringRef> AnalyzerConfigCmdFlags = {
|
||||
#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \
|
||||
SHALLOW_VAL, DEEP_VAL) \
|
||||
ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL)
|
||||
|
||||
#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \
|
||||
CMDFLAG,
|
||||
|
||||
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def"
|
||||
#undef ANALYZER_OPTION
|
||||
#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
|
||||
};
|
||||
|
||||
bool isUnknownAnalyzerConfig(StringRef Name) const {
|
||||
|
||||
assert(std::is_sorted(AnalyzerConfigCmdFlags.begin(),
|
||||
AnalyzerConfigCmdFlags.end()));
|
||||
|
||||
return !std::binary_search(AnalyzerConfigCmdFlags.begin(),
|
||||
AnalyzerConfigCmdFlags.end(), Name);
|
||||
}
|
||||
|
||||
AnalyzerOptions()
|
||||
: DisableAllChecks(false), ShowCheckerHelp(false),
|
||||
ShowEnabledCheckerList(false), ShowConfigOptionsList(false),
|
||||
AnalyzeAll(false), AnalyzerDisplayProgress(false),
|
||||
AnalyzeNestedBlocks(false), eagerlyAssumeBinOpBifurcation(false),
|
||||
TrimGraph(false), visualizeExplodedGraphWithGraphViz(false),
|
||||
UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false) {}
|
||||
UnoptimizedCFG(false), PrintStats(false), NoRetryExhausted(false) {
|
||||
llvm::sort(AnalyzerConfigCmdFlags);
|
||||
}
|
||||
|
||||
/// Interprets an option's string value as a boolean. The "true" string is
|
||||
/// interpreted as true and the "false" string is interpreted as false.
|
||||
|
|
|
@ -2368,6 +2368,9 @@ static void RenderAnalyzerOptions(const ArgList &Args, ArgStringList &CmdArgs,
|
|||
// Treat blocks as analysis entry points.
|
||||
CmdArgs.push_back("-analyzer-opt-analyze-nested-blocks");
|
||||
|
||||
// Enable compatilibily mode to avoid analyzer-config related errors.
|
||||
CmdArgs.push_back("-analyzer-config-compatibility-mode=true");
|
||||
|
||||
// Add default argument set.
|
||||
if (!Args.hasArg(options::OPT__analyzer_no_default_checks)) {
|
||||
CmdArgs.push_back("-analyzer-checker=core");
|
||||
|
|
|
@ -181,8 +181,10 @@ static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group,
|
|||
}
|
||||
}
|
||||
|
||||
// Parse the Static Analyzer configuration. If \p Diags is set to nullptr,
|
||||
// it won't verify the input.
|
||||
static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts,
|
||||
DiagnosticsEngine &Diags);
|
||||
DiagnosticsEngine *Diags);
|
||||
|
||||
static void getAllNoBuiltinFuncValues(ArgList &Args,
|
||||
std::vector<std::string> &Funcs) {
|
||||
|
@ -284,6 +286,12 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
|
|||
Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help);
|
||||
Opts.ShowConfigOptionsList = Args.hasArg(OPT_analyzer_config_help);
|
||||
Opts.ShowEnabledCheckerList = Args.hasArg(OPT_analyzer_list_enabled_checkers);
|
||||
Opts.ShouldEmitErrorsOnInvalidConfigValue =
|
||||
/* negated */!llvm::StringSwitch<bool>(
|
||||
Args.getLastArgValue(OPT_analyzer_config_compatibility_mode))
|
||||
.Case("true", true)
|
||||
.Case("false", false)
|
||||
.Default(false);
|
||||
Opts.DisableAllChecks = Args.hasArg(OPT_analyzer_disable_all_checks);
|
||||
|
||||
Opts.visualizeExplodedGraphWithGraphViz =
|
||||
|
@ -320,7 +328,7 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
|
|||
|
||||
// Go through the analyzer configuration options.
|
||||
for (const auto *A : Args.filtered(OPT_analyzer_config)) {
|
||||
A->claim();
|
||||
|
||||
// We can have a list of comma separated config names, e.g:
|
||||
// '-analyzer-config key1=val1,key2=val2'
|
||||
StringRef configList = A->getValue();
|
||||
|
@ -342,11 +350,24 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args,
|
|||
Success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Check checker options too, possibly in CheckerRegistry.
|
||||
// Leave unknown non-checker configs unclaimed.
|
||||
if (!key.contains(":") && Opts.isUnknownAnalyzerConfig(key)) {
|
||||
if (Opts.ShouldEmitErrorsOnInvalidConfigValue)
|
||||
Diags.Report(diag::err_analyzer_config_unknown) << key;
|
||||
continue;
|
||||
}
|
||||
|
||||
A->claim();
|
||||
Opts.Config[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
parseAnalyzerConfigs(Opts, Diags);
|
||||
if (Opts.ShouldEmitErrorsOnInvalidConfigValue)
|
||||
parseAnalyzerConfigs(Opts, &Diags);
|
||||
else
|
||||
parseAnalyzerConfigs(Opts, nullptr);
|
||||
|
||||
llvm::raw_string_ostream os(Opts.FullCompilerInvocation);
|
||||
for (unsigned i = 0; i < Args.getNumInputArgStrings(); ++i) {
|
||||
|
@ -365,56 +386,82 @@ static StringRef getStringOption(AnalyzerOptions::ConfigTable &Config,
|
|||
}
|
||||
|
||||
static void initOption(AnalyzerOptions::ConfigTable &Config,
|
||||
DiagnosticsEngine *Diags,
|
||||
StringRef &OptionField, StringRef Name,
|
||||
StringRef DefaultVal) {
|
||||
// String options may be known to invalid (e.g. if the expected string is a
|
||||
// file name, but the file does not exist), those will have to be checked in
|
||||
// parseConfigs.
|
||||
OptionField = getStringOption(Config, Name, DefaultVal);
|
||||
}
|
||||
|
||||
static void initOption(AnalyzerOptions::ConfigTable &Config,
|
||||
DiagnosticsEngine *Diags,
|
||||
bool &OptionField, StringRef Name, bool DefaultVal) {
|
||||
// FIXME: We should emit a warning here if the value is something other than
|
||||
// "true", "false", or the empty string (meaning the default value),
|
||||
// but the AnalyzerOptions doesn't have access to a diagnostic engine.
|
||||
OptionField = llvm::StringSwitch<bool>(getStringOption(Config, Name,
|
||||
(DefaultVal ? "true" : "false")))
|
||||
auto PossiblyInvalidVal = llvm::StringSwitch<Optional<bool>>(
|
||||
getStringOption(Config, Name, (DefaultVal ? "true" : "false")))
|
||||
.Case("true", true)
|
||||
.Case("false", false)
|
||||
.Default(DefaultVal);
|
||||
.Default(None);
|
||||
|
||||
if (!PossiblyInvalidVal) {
|
||||
if (Diags)
|
||||
Diags->Report(diag::err_analyzer_config_invalid_input)
|
||||
<< Name << "a boolean";
|
||||
else
|
||||
OptionField = DefaultVal;
|
||||
} else
|
||||
OptionField = PossiblyInvalidVal.getValue();
|
||||
}
|
||||
|
||||
static void initOption(AnalyzerOptions::ConfigTable &Config,
|
||||
DiagnosticsEngine *Diags,
|
||||
unsigned &OptionField, StringRef Name,
|
||||
unsigned DefaultVal) {
|
||||
|
||||
OptionField = DefaultVal;
|
||||
bool HasFailed = getStringOption(Config, Name, std::to_string(DefaultVal))
|
||||
.getAsInteger(10, OptionField);
|
||||
assert(!HasFailed && "analyzer-config option should be numeric");
|
||||
(void)HasFailed;
|
||||
if (Diags && HasFailed)
|
||||
Diags->Report(diag::err_analyzer_config_invalid_input)
|
||||
<< Name << "an unsigned";
|
||||
}
|
||||
|
||||
static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts,
|
||||
DiagnosticsEngine &Diags) {
|
||||
// TODO: Emit warnings for incorrect options.
|
||||
DiagnosticsEngine *Diags) {
|
||||
// TODO: There's no need to store the entire configtable, it'd be plenty
|
||||
// enough tostore checker options.
|
||||
|
||||
#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \
|
||||
initOption(AnOpts.Config, AnOpts.NAME, CMDFLAG, DEFAULT_VAL); \
|
||||
initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, DEFAULT_VAL);
|
||||
|
||||
#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \
|
||||
SHALLOW_VAL, DEEP_VAL) \
|
||||
switch (AnOpts.getUserMode()) { \
|
||||
case UMK_Shallow: \
|
||||
initOption(AnOpts.Config, AnOpts.NAME, CMDFLAG, SHALLOW_VAL); \
|
||||
initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, SHALLOW_VAL); \
|
||||
break; \
|
||||
case UMK_Deep: \
|
||||
initOption(AnOpts.Config, AnOpts.NAME, CMDFLAG, DEEP_VAL); \
|
||||
initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, DEEP_VAL); \
|
||||
break; \
|
||||
} \
|
||||
|
||||
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def"
|
||||
#undef ANALYZER_OPTION
|
||||
#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
|
||||
|
||||
// At this point, AnalyzerOptions is configured. Let's validate some options.
|
||||
|
||||
if (!Diags)
|
||||
return;
|
||||
|
||||
if (!AnOpts.CTUDir.empty() && !llvm::sys::fs::is_directory(AnOpts.CTUDir))
|
||||
Diags->Report(diag::err_analyzer_config_invalid_input)
|
||||
<< "ctu-dir" << "a filename";
|
||||
|
||||
if (!AnOpts.CTUDir.empty() && !llvm::sys::fs::is_directory(AnOpts.CTUDir))
|
||||
Diags->Report(diag::err_analyzer_config_invalid_input)
|
||||
<< "model-path" << "a filename";
|
||||
}
|
||||
|
||||
static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// RUN: not %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config notes-as-events=yesplease \
|
||||
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-BOOL-INPUT
|
||||
|
||||
// CHECK-BOOL-INPUT: (frontend): invalid input for analyzer-config option
|
||||
// CHECK-BOOL-INPUT-SAME: 'notes-as-events', that expects a boolean value
|
||||
|
||||
// RUN: %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config-compatibility-mode=true \
|
||||
// RUN: -analyzer-config notes-as-events=yesplease
|
||||
|
||||
|
||||
// RUN: not %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config max-inlinable-size=400km/h \
|
||||
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-UINT-INPUT
|
||||
|
||||
// CHECK-UINT-INPUT: (frontend): invalid input for analyzer-config option
|
||||
// CHECK-UINT-INPUT-SAME: 'max-inlinable-size', that expects an unsigned
|
||||
// CHECK-UINT-INPUT-SAME: value
|
||||
|
||||
// RUN: %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config-compatibility-mode=true \
|
||||
// RUN: -analyzer-config max-inlinable-size=400km/h
|
||||
|
||||
|
||||
// RUN: not %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config ctu-dir=0123012301230123 \
|
||||
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-FILENAME-INPUT
|
||||
|
||||
// CHECK-FILENAME-INPUT: (frontend): invalid input for analyzer-config option
|
||||
// CHECK-FILENAME-INPUT-SAME: 'ctu-dir', that expects a filename
|
||||
// CHECK-FILENAME-INPUT-SAME: value
|
||||
|
||||
// RUN: %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config-compatibility-mode=true \
|
||||
// RUN: -analyzer-config ctu-dir=0123012301230123
|
||||
|
||||
|
||||
// RUN: not %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config no-false-positives=true \
|
||||
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-UNKNOWN-CFG
|
||||
|
||||
// CHECK-UNKNOWN-CFG: (frontend): unknown analyzer-config 'no-false-positives'
|
||||
|
||||
// RUN: %clang_analyze_cc1 -verify %s \
|
||||
// RUN: -analyzer-checker=core \
|
||||
// RUN: -analyzer-config-compatibility-mode=true \
|
||||
// RUN: -analyzer-config no-false-positives=true
|
||||
|
||||
|
||||
// Test the driver properly using "analyzer-config-compatibility-mode=true",
|
||||
// no longer causing an error on input error.
|
||||
// RUN: %clang --analyze %s
|
||||
|
||||
// RUN: not %clang --analyze %s \
|
||||
// RUN: -Xclang -analyzer-config -Xclang no-false-positives=true \
|
||||
// RUN: -Xclang -analyzer-config-compatibility-mode=false \
|
||||
// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-NO-COMPAT
|
||||
|
||||
// CHECK-NO-COMPAT: error: unknown analyzer-config 'no-false-positives'
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
int main() {}
|
Loading…
Reference in New Issue