Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -151,6 +151,11 @@ def analyzer_checker_option_help : Flag<["-"], "analyzer-checker-option-help">, HelpText<"Display the list of checker and package options">; +def analyzer_checker_option_help_alpha : Flag<["-"], "analyzer-checker-option-help-alpha">, + HelpText<"Display the list of in development checker and package options. " + "These are NOT considered safe, they are unstable and will emit " + "incorrect reports. Enable ONLY FOR DEVELOPMENT purposes">; + def analyzer_checker_option_help_developer : Flag<["-"], "analyzer-checker-option-help-developer">, HelpText<"Display the list of checker and package options meant for " "development purposes only">; Index: include/clang/StaticAnalyzer/Checkers/CheckerBase.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/CheckerBase.td +++ include/clang/StaticAnalyzer/Checkers/CheckerBase.td @@ -21,10 +21,28 @@ def String : CmdLineOptionTypeEnum<1>; def Boolean : CmdLineOptionTypeEnum<2>; -class Type { - bits<2> Type = val.Type; +/// Describes the state of the entry. We wouldn't like to display, for example, +/// developer only entries for a list meant for end users. +class DevelopmentStageEnum val> { + bits<2> Val = val; } +/// Alpha entries are under development, might be incomplet, inkorrekt and +/// unstable. +def InAlpha : DevelopmentStageEnum<0>; + +/// Beta entries are considered stable, but need some finishing touches, +/// might have a not ideal false positive/false negative ratio, but are +/// usable in production. +/// Beta checkers checkers at minimum must be able to analyze the LLVM project +/// without crashes, and have a reasonable FP/TP ratio. +def InBeta : DevelopmentStageEnum<1>; + +/// Released entries are stable, produce minimal, if any false positives, +/// and emits reports that explain the occurance of the bug understandably and +/// thoroughly. +def Released : DevelopmentStageEnum<2>; + /// Marks the entry hidden. Hidden entries won't be displayed in /// -analyzer-checker-option-help. class HiddenEnum { @@ -35,11 +53,13 @@ /// Describes an option for a checker or a package. class CmdLineOption { + string defaultVal, DevelopmentStageEnum stage, + HiddenEnum isHidden = DontHide> { bits<2> Type = type.Type; string CmdFlag = cmdFlag; string Desc = desc; string DefaultVal = defaultVal; + bits<2> DevelopmentStage = stage.Val; bit Hidden = isHidden.Val; } Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -58,7 +58,8 @@ "concerned with your custom nullability annotations more " "than with following nullability specifications of system " "header functions.", - "false"> + "false", + Released> ]>; def Cplusplus : Package<"cplusplus">; @@ -404,7 +405,8 @@ "If set to true, the checker assumes that all the " "allocating and deallocating functions are annotated with " "ownership_holds, ownership_takes and ownership_returns.", - "false"> + "false", + InAlpha> ]>, Dependencies<[CStringModeling]>, Documentation, @@ -509,7 +511,8 @@ "user has intentionally asked us to completely eliminate " "use-after-move in his code. Values: \"KnownsOnly\", " "\"KnownsAndLocals\", \"All\".", - "KnownsAndLocals"> + "KnownsAndLocals", + Released> ]>, Documentation; @@ -525,7 +528,8 @@ "If set to false, the checker won't emit warnings " "for objects that don't have at least one initialized " "field.", - "false">, + "false", + Released>, CmdLineOption, CmdLineOption, + "false", + InBeta>, CmdLineOption, + "\"\"", + Released>, CmdLineOption + "false", + InAlpha> ]>, Documentation; @@ -561,7 +569,8 @@ CmdLineOption + "false", + Released> ]>, Documentation; @@ -664,7 +673,8 @@ "AllowedPad", "Reports are only generated if the excessive padding exceeds " "'AllowedPad' in bytes.", - "24"> + "24", + Released> ]>, Documentation; @@ -781,11 +791,13 @@ CmdLineOption, + "0x04", + Released>, CmdLineOption + "0x01", + Released> ]>, Documentation; @@ -832,7 +844,8 @@ "Enables detection of more conversion patterns (which are " "most likely more harmless, and therefore are more likely to " "produce false positives).", - "false"> + "false", + Released> ]>, Documentation; @@ -933,6 +946,7 @@ "retain-release rules for Objective-C NSObject instances " "and CoreFoundation objects.", "true", + InAlpha, Hide>, CmdLineOption + "false", + Released> ]>, Dependencies<[RetainCountBase]>, Documentation; @@ -1062,6 +1077,7 @@ "SymRegion (Non-Aggressive), basically leaving only string " "literals as NonLocalized.", "false", + InAlpha, Hide> ]>, Documentation; @@ -1127,81 +1143,97 @@ "PreStmtCastExpr", "", "false", + Released, Hide>, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption, CmdLineOption ]>, Documentation; @@ -1285,16 +1317,19 @@ "of a statement. This constraint assumes the first statement " "in the group is representative for all other statements in " "the group in terms of complexity.", - "50">, + "50", + Released>, CmdLineOption, + "true", + Released>, CmdLineOption + "\"\"", + Released> ]>, Documentation; Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -222,9 +222,12 @@ unsigned ShowCheckerHelp : 1; unsigned ShowCheckerHelpAlpha : 1; unsigned ShowCheckerHelpDeveloper : 1; - unsigned ShowEnabledCheckerList : 1; + unsigned ShowCheckerOptionList : 1; + unsigned ShowCheckerOptionAlphaList : 1; unsigned ShowCheckerOptionDeveloperList : 1; + + unsigned ShowEnabledCheckerList : 1; unsigned ShowConfigOptionsList : 1; unsigned ShouldEmitErrorsOnInvalidConfigValue : 1; unsigned AnalyzeAll : 1; @@ -288,8 +291,8 @@ AnalyzerOptions() : DisableAllChecks(false), ShowCheckerHelp(false), ShowCheckerHelpAlpha(false), ShowCheckerHelpDeveloper(false), - ShowEnabledCheckerList(false), - ShowCheckerOptionList(false), ShowCheckerOptionDeveloperList(false), + ShowCheckerOptionList(false), ShowCheckerOptionAlphaList(false), + ShowCheckerOptionDeveloperList(false), ShowEnabledCheckerList(false), ShowConfigOptionsList(false), AnalyzeAll(false), AnalyzerDisplayProgress(false), AnalyzeNestedBlocks(false), eagerlyAssumeBinOpBifurcation(false), TrimGraph(false), Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -98,13 +98,15 @@ StringRef OptionName; StringRef DefaultValStr; StringRef Description; + StringRef DevelopmentStatus; bool IsHidden; CmdLineOption(StringRef OptionType, StringRef OptionName, - StringRef DefaultValStr, StringRef Description, bool IsHidden) + StringRef DefaultValStr, StringRef Description, + StringRef DevelopmentStatus, bool IsHidden) : OptionType(OptionType), OptionName(OptionName), DefaultValStr(DefaultValStr), Description(Description), - IsHidden(IsHidden) { + DevelopmentStatus(DevelopmentStatus), IsHidden(IsHidden) { assert((OptionType == "bool" || OptionType == "string" || OptionType == "int") && @@ -120,6 +122,10 @@ "Invalid value for integer command line option! Maybe incorrect " "parameters to the addCheckerOption or addPackageOption method?"); (void)Tmp; + + assert((DevelopmentStatus == "alpha" || DevelopmentStatus == "beta" || + DevelopmentStatus == "released") && + "Invalid development status!"); } }; @@ -241,7 +247,8 @@ /// non-compatibility mode. void addCheckerOption(StringRef OptionType, StringRef CheckerFullName, StringRef OptionName, StringRef DefaultValStr, - StringRef Description, bool IsHidden = false); + StringRef Description, StringRef DevelopmentStatus, + bool IsHidden = false); /// Adds a package to the registry. void addPackage(StringRef FullName); @@ -257,7 +264,8 @@ /// non-compatibility mode. void addPackageOption(StringRef OptionType, StringRef PackageFullName, StringRef OptionName, StringRef DefaultValStr, - StringRef Description, bool IsHidden = false); + StringRef Description, StringRef DevelopmentStatus, + bool IsHidden = false); // FIXME: This *really* should be added to the frontend flag descriptions. /// Initializes a CheckerManager by calling the initialization functions for Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -288,9 +288,13 @@ Opts.ShowCheckerHelpAlpha = Args.hasArg(OPT_analyzer_checker_help_alpha); Opts.ShowCheckerHelpDeveloper = Args.hasArg(OPT_analyzer_checker_help_developer); + Opts.ShowCheckerOptionList = Args.hasArg(OPT_analyzer_checker_option_help); + Opts.ShowCheckerOptionAlphaList = + Args.hasArg(OPT_analyzer_checker_option_help_alpha); Opts.ShowCheckerOptionDeveloperList = Args.hasArg(OPT_analyzer_checker_option_help_developer); + 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 @@ -249,7 +249,8 @@ } // Honor -analyzer-checker-option-help. - if (AnOpts.ShowCheckerOptionList || AnOpts.ShowCheckerOptionDeveloperList) { + if (AnOpts.ShowCheckerOptionList || AnOpts.ShowCheckerOptionAlphaList || + AnOpts.ShowCheckerOptionDeveloperList) { ento::printCheckerConfigList(llvm::outs(), Clang->getFrontendOpts().Plugins, *Clang->getAnalyzerOpts(), Index: lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -180,12 +180,12 @@ addDependency(FULLNAME, DEPENDENCY); #define GET_CHECKER_OPTIONS -#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, IS_HIDDEN) \ - addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, IS_HIDDEN); +#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \ + addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); #define GET_PACKAGE_OPTIONS -#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, IS_HIDDEN) \ - addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, IS_HIDDEN); +#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \ + addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN); #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER_DEPENDENCY @@ -396,10 +396,12 @@ StringRef PackageFullName, StringRef OptionName, StringRef DefaultValStr, - StringRef Description, bool IsHidden) { + StringRef Description, + StringRef DevelopmentStatus, + bool IsHidden) { PackageOptions.emplace_back( PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, - Description, IsHidden}); + Description, DevelopmentStatus, IsHidden}); } void CheckerRegistry::addChecker(InitializationFunction Rfn, @@ -421,10 +423,12 @@ StringRef CheckerFullName, StringRef OptionName, StringRef DefaultValStr, - StringRef Description, bool IsHidden) { + StringRef Description, + StringRef DevelopmentStatus, + bool IsHidden) { CheckerOptions.emplace_back( CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr, - Description, IsHidden}); + Description, DevelopmentStatus, IsHidden}); } void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const { @@ -580,10 +584,14 @@ } } + 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 &Entry : OptionMap) { - if (!AnOpts.ShowCheckerOptionDeveloperList && Entry.second.IsHidden) - continue; - const CmdLineOption &Option = Entry.second; std::string FullOption = (Entry.first + ":" + Option.OptionName).str(); @@ -592,10 +600,29 @@ (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")") .str(); - AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc}, - /*InitialPad*/ 2, - /*EntryWidth*/ 50, - /*MinLineWidth*/ 90); - Out << "\n\n"; + // 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) { + if (Option.DevelopmentStatus == "beta") + Print(Out, FullOption, llvm::Twine("(Experimental) " + Desc).str()); + else + Print(Out, FullOption, Desc); + } } } Index: test/Analysis/analyzer-checker-option-help.c =================================================================== --- test/Analysis/analyzer-checker-option-help.c +++ test/Analysis/analyzer-checker-option-help.c @@ -1,25 +1,87 @@ -// RUN: %clang_cc1 -analyzer-checker-option-help 2>&1 | FileCheck %s +// RUN: %clang_cc1 -analyzer-checker-option-help \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-STABLE + +// RUN: %clang_cc1 -analyzer-checker-option-help-alpha \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-ALPHA + +// RUN: %clang_cc1 -analyzer-checker-option-help-developer \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-DEVELOPER // RUN: %clang_cc1 -analyzer-checker-option-help-developer \ -// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-HIDDEN +// RUN: -analyzer-checker-option-help-alpha \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-DEVELOPER-ALPHA + +// RUN: %clang_cc1 -analyzer-checker-option-help \ +// RUN: -analyzer-checker-option-help-alpha \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-STABLE-ALPHA -// CHECK: OVERVIEW: Clang Static Analyzer Checker and Package Option List +// RUN: %clang_cc1 -analyzer-checker-option-help \ +// RUN: -analyzer-checker-option-help-developer \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-STABLE-DEVELOPER + +// RUN: %clang_cc1 -analyzer-checker-option-help \ +// RUN: -analyzer-checker-option-help-alpha \ +// RUN: -analyzer-checker-option-help-developer \ +// RUN: 2>&1 | FileCheck %s -check-prefix=CHECK-STABLE-ALPHA-DEVELOPER + +// CHECK-STABLE: OVERVIEW: Clang Static Analyzer Checker and Package Option List // -// CHECK: USAGE: -analyzer-config +// CHECK-STABLE: USAGE: -analyzer-config // -// CHECK: -analyzer-config OPTION1=VALUE, -analyzer-config -// CHECK-SAME: OPTION2=VALUE, ... +// CHECK-STABLE: -analyzer-config OPTION1=VALUE, -analyzer-config +// CHECK-STABLE-SAME: OPTION2=VALUE, ... // -// CHECK: OPTIONS: +// CHECK-STABLE: 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) - -// CHECK-NOT: optin.cplusplus.UninitializedObject:NotesAsWarnings -// CHECK-HIDDEN: optin.cplusplus.UninitializedObject:NotesAsWarnings +// CHECK-STABLE: cplusplus.Move:WarnOn +// CHECK-STABLE-SAME: (string) In non-aggressive mode, only warn +// CHECK-STABLLE: on use-after-move of local variables (or +// CHECK-STABLLE: local rvalue references) and of STL objects. +// CHECK-STABLLE: The former is possible because local variables +// CHECK-STABLLE: (or local rvalue references) are not tempting +// CHECK-STABLLE: their user to re-use the storage. The latter +// CHECK-STABLLE: is possible because STL objects are known +// CHECK-STABLLE: to end up in a valid but unspecified state +// CHECK-STABLLE: after the move and their state-reset methods +// CHECK-STABLLE: are also known, which allows us to predict +// CHECK-STABLLE: precisely when use-after-move is invalid. +// CHECK-STABLLE: Some STL objects are known to conform to +// CHECK-STABLLE: additional contracts after move, so they +// CHECK-STABLLE: are not tracked. However, smart pointers +// CHECK-STABLLE: specifically are tracked because we can +// CHECK-STABLLE: perform extra checking over them. In aggressive +// CHECK-STABLLE: mode, warn on any use-after-move because +// CHECK-STABLLE: the user has intentionally asked us to completely +// CHECK-STABLLE: eliminate use-after-move in his code. Values: +// CHECK-STABLLE: "KnownsOnly", "KnownsAndLocals", "All". +// CHECK-STABLLE: (default: KnownsAndLocals) + +// CHECK-STABLE-NOT: debug.AnalysisOrder:* +// CHECK-DEVELOPER: debug.AnalysisOrder:* +// CHECK-ALPHA-NOT: debug.AnalysisOrder:* + +// CHECK-STABLE-NOT: optin.cplusplus.UninitializedObject:IgnoreGuardedFields +// CHECK-DEVELOPER-NOT: optin.cplusplus.UninitializedObject:IgnoreGuardedFields +// CHECK-ALPHA: optin.cplusplus.UninitializedObject:IgnoreGuardedFields + +// CHECK-STABLE: optin.performance.Padding:AllowedPad +// CHECK-DEVELOPER-NOT: optin.performance.Padding:AllowedPad +// CHECK-ALPHA-NOT: optin.performance.Padding:AllowedPad + + +// CHECK-STABLE-ALPHA-NOT: debug.AnalysisOrder:* +// CHECK-DEVELOPER-ALPHA: debug.AnalysisOrder:* +// CHECK-STABLE-DEVELOPER: debug.AnalysisOrder:* + +// CHECK-STABLE-ALPHA: optin.cplusplus.UninitializedObject:IgnoreGuardedFields +// CHECK-DEVELOPER-ALPHA: optin.cplusplus.UninitializedObject:IgnoreGuardedFields +// CHECK-STABLE-DEVELOPER-NOT: optin.cplusplus.UninitializedObject:IgnoreGuardedFields + +// CHECK-STABLE-ALPHA: optin.performance.Padding:AllowedPad +// CHECK-DEVELOPER-ALPHA-NOT: optin.performance.Padding:AllowedPad +// CHECK-STABLE-DEVELOPER: optin.performance.Padding:AllowedPad + + +// CHECK-STABLE-ALPHA-DEVELOPER: debug.AnalysisOrder:* +// CHECK-STABLE-ALPHA-DEVELOPER: optin.cplusplus.UninitializedObject:IgnoreGuardedFields +// CHECK-STABLE-ALPHA-DEVELOPER: optin.performance.Padding:AllowedPad Index: test/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp =================================================================== --- test/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp +++ test/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp @@ -36,7 +36,8 @@ /*CheckerFullName*/ "example.MyChecker", /*OptionName*/ "ExampleOption", /*DefaultValStr*/ "false", - /*Description*/ "This is an example checker opt."); + /*Description*/ "This is an example checker opt.", + /*DevelopmentStage*/ "released"); } extern "C" const char clang_analyzerAPIVersionString[] = Index: utils/TableGen/ClangSACheckersEmitter.cpp =================================================================== --- utils/TableGen/ClangSACheckersEmitter.cpp +++ utils/TableGen/ClangSACheckersEmitter.cpp @@ -110,6 +110,24 @@ return ""; } +static std::string getDevelopmentStage(const Record &R) { + if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) { + switch(getValueFromBitsInit(BI, R)) { + case 0: + return "alpha"; + case 1: + return "beta"; + case 2: + return "released"; + } + } + + PrintFatalError(R.getLoc(), + "unable to parse command line option type for " + + getCheckerFullName(&R)); + return ""; +} + static bool isHidden(const Record *R) { if (R->getValueAsBit("Hidden")) return true; @@ -149,6 +167,8 @@ OS.write_escaped(getStringValue(R, "Desc")) << "\", "; OS << '\"'; OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", "; + OS << '\"'; + OS << getDevelopmentStage(R) << "\", "; if (!R.getValueAsBit("Hidden")) OS << "false";