Index: clang-tools-extra/clang-tidy/ClangTidy.cpp =================================================================== --- clang-tools-extra/clang-tidy/ClangTidy.cpp +++ clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -334,8 +334,8 @@ typedef std::vector> CheckersList; -static CheckersList getCheckersControlList(ClangTidyContext &Context, - bool IncludeExperimental) { +static CheckersList getAnalyzerCheckers(ClangTidyContext &Context, + bool IncludeExperimental) { CheckersList List; const auto &RegisteredCheckers = @@ -419,9 +419,9 @@ #if CLANG_ENABLE_STATIC_ANALYZER AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts(); - AnalyzerOptions->CheckersControlList = - getCheckersControlList(Context, Context.canEnableAnalyzerAlphaCheckers()); - if (!AnalyzerOptions->CheckersControlList.empty()) { + AnalyzerOptions->Checkers = + getAnalyzerCheckers(Context, Context.canEnableAnalyzerAlphaCheckers()); + if (!AnalyzerOptions->Checkers.empty()) { setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions); AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel; AnalyzerOptions->AnalysisDiagOpt = PD_NONE; @@ -447,8 +447,8 @@ } #if CLANG_ENABLE_STATIC_ANALYZER - for (const auto &AnalyzerCheck : getCheckersControlList( - Context, Context.canEnableAnalyzerAlphaCheckers())) + for (const auto &AnalyzerCheck : + getAnalyzerCheckers(Context, Context.canEnableAnalyzerAlphaCheckers())) CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); #endif // CLANG_ENABLE_STATIC_ANALYZER Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -188,9 +188,11 @@ std::pair EntryDescPair, size_t EntryWidth, size_t InitialPad, size_t MinLineWidth = 0); - /// Pair of checker name and enable/disable. - std::vector> CheckersControlList; + std::vector> Checkers; + + /// Vector of checker names which will not emit warnings. + std::vector SilencedCheckers; /// A key-value table of use-specified configuration values. // TODO: This shouldn't be public. Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def =================================================================== --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -380,12 +380,6 @@ "Value: \"constructors\", \"destructors\", \"methods\".", "destructors") -ANALYZER_OPTION_DEPENDS_ON_USER_MODE( - StringRef, IPAMode, "ipa", - "Controls the mode of inter-procedural analysis. Value: \"none\", " - "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", - /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") - ANALYZER_OPTION( StringRef, ExplorationStrategy, "exploration_strategy", "Value: \"dfs\", \"bfs\", \"unexplored_first\", " @@ -393,5 +387,15 @@ "\"bfs_block_dfs_contents\".", "unexplored_first_queue") +ANALYZER_OPTION( + StringRef, RawSilencedCheckers, "silence-checkers", + "A semicolon separated list of checker and package names to silence.", "") + +ANALYZER_OPTION_DEPENDS_ON_USER_MODE( + StringRef, IPAMode, "ipa", + "Controls the mode of inter-procedural analysis. Value: \"none\", " + "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".", + /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate") + #undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE #undef ANALYZER_OPTION Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -324,18 +324,18 @@ getLastArgIntValue(Args, OPT_analyzer_inline_max_stack_depth, Opts.InlineMaxStackDepth, Diags); - Opts.CheckersControlList.clear(); + Opts.Checkers.clear(); for (const Arg *A : Args.filtered(OPT_analyzer_checker, OPT_analyzer_disable_checker)) { A->claim(); - bool enable = (A->getOption().getID() == OPT_analyzer_checker); + bool IsEnabled = A->getOption().getID() == OPT_analyzer_checker; // We can have a list of comma separated checker names, e.g: // '-analyzer-checker=cocoa,unix' - StringRef checkerList = A->getValue(); - SmallVector checkers; - checkerList.split(checkers, ","); - for (auto checker : checkers) - Opts.CheckersControlList.emplace_back(checker, enable); + StringRef CheckerList = A->getValue(); + SmallVector Checkers; + CheckerList.split(Checkers, ","); + for (const StringRef Checker : Checkers) + Opts.Checkers.emplace_back(Checker, IsEnabled); } // Go through the analyzer configuration options. @@ -479,6 +479,13 @@ !llvm::sys::fs::is_directory(AnOpts.ModelPath)) Diags->Report(diag::err_analyzer_config_invalid_input) << "model-path" << "a filename"; + + if (!AnOpts.RawSilencedCheckers.empty()) { + SmallVector Checkers; + AnOpts.RawSilencedCheckers.split(Checkers, ";"); + for (const StringRef Checker : Checkers) + AnOpts.SilencedCheckers.emplace_back(Checker); + } } static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) { Index: clang/lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1917,12 +1917,19 @@ PathDiagnosticBuilder &PDB, const ExplodedNode *ErrorNode, const VisitorsDiagnosticsTy &VisitorsDiagnostics) { + BugReport *R = PDB.getBugReport(); + AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions(); + StringRef ErrorTag = ErrorNode->getLocation().getTag()->getTagDescription(); + + // See whether we need to silence the checker. + for (const std::string &CheckerName : Opts.SilencedCheckers) { + if (ErrorTag.startswith(CheckerName)) + return nullptr; + } bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None); bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive); SourceManager &SM = PDB.getSourceManager(); - BugReport *R = PDB.getBugReport(); - AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions(); StackDiagVector CallStack; InterestingExprs IE; LocationContextMap LCM; @@ -2686,9 +2693,12 @@ const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; for (PathDiagnosticConsumer *PC : consumers) { PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC); - std::unique_ptr PD = generatePathDiagnosticForConsumer( - PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second); - (*Out)[PC] = std::move(PD); + if (std::unique_ptr PD = + generatePathDiagnosticForConsumer(PC->getGenerationScheme(), PDB, + ErrorNode, + *ReportInfo.second)) { + (*Out)[PC] = std::move(PD); + } } } Index: clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -200,7 +200,7 @@ // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the // command line. - for (const std::pair &Opt : AnOpts.CheckersControlList) { + for (const std::pair &Opt : AnOpts.Checkers) { CheckerInfoListRange CheckerForCmdLineArg = getMutableCheckersForCmdLineArg(Opt.first); Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ clang/test/Analysis/analyzer-config.c @@ -82,6 +82,7 @@ // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: serialize-stats = false +// CHECK-NEXT: silence-checkers = "" // CHECK-NEXT: stable-report-filename = false // CHECK-NEXT: suppress-c++-stdlib = true // CHECK-NEXT: suppress-inlined-defensive-checks = true @@ -92,4 +93,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 89 +// CHECK-NEXT: num-entries = 90 Index: clang/test/Analysis/silence-checker-core-all.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/silence-checker-core-all.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config silence-checkers=core \ +// RUN: -verify %s +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config silence-checkers="core.DivideZero;core.NullDeref" \ +// RUN: -verify %s + +void test_disable_core_div_by_zero() { + (void)(1 / 0); + // expected-warning@-1 {{division by zero is undefined}} + // no-warning: 'Division by zero' +} + +void test_disable_null_deref(int *p) { + if (p) + return; + + int x = p[0]; + // no-warning: Array access (from variable 'p') results in a null pointer dereference +} Index: clang/test/Analysis/silence-checker-core-div-by-zero.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/silence-checker-core-div-by-zero.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config silence-checkers=core.DivideZero \ +// RUN: -verify %s + +void test_disable_core_div_by_zero() { + (void)(1 / 0); + // expected-warning@-1 {{division by zero is undefined}} + // no-warning: 'Division by zero' +} + +void test_disable_null_deref(int *p) { + if (p) + return; + + int x = p[0]; + // expected-warning@-1 {{Array access (from variable 'p') results in a null pointer dereference}} +} Index: clang/tools/scan-build/bin/scan-build =================================================================== --- clang/tools/scan-build/bin/scan-build +++ clang/tools/scan-build/bin/scan-build @@ -57,6 +57,7 @@ KeepEmpty => 0, # Don't remove output directory even with 0 results. EnableCheckers => {}, DisableCheckers => {}, + SilenceCheckers => {}, Excludes => [], UseCC => undef, # C compiler to use for compilation. UseCXX => undef, # C++ compiler to use for compilation. @@ -1742,9 +1743,15 @@ if ($arg eq "-disable-checker") { shift @$Args; my $Checker = shift @$Args; - # Store $NumArgs to preserve the order the checkers were disabled. - $Options{DisableCheckers}{$Checker} = $NumArgs; - delete $Options{EnableCheckers}{$Checker}; + # Store $NumArgs to preserve the order the checkers are disabled/silenced. + # See whether it is a core checker to disable. That means we do not want + # to emit a report from that checker so we have to silence it. + if (index($Checker, "core") == 0) { + $Options{SilenceCheckers}{$Checker} = $NumArgs; + } else { + $Options{DisableCheckers}{$Checker} = $NumArgs; + delete $Options{EnableCheckers}{$Checker}; + } next; } @@ -1882,6 +1889,11 @@ # Push checkers in order they were disabled. push @AnalysesToRun, "-analyzer-disable-checker", $_; } +foreach (sort { $Options{SilenceCheckers}{$a} <=> $Options{SilenceCheckers}{$b} } + keys %{$Options{SilenceCheckers}}) { + # Push checkers in order they were silenced. + push @AnalysesToRun, "-analyzer-config silence-checker", $_; +} if ($Options{AnalyzeHeaders}) { push @AnalysesToRun, "-analyzer-opt-analyze-headers"; } if ($Options{AnalyzerStats}) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; } if ($Options{MaxLoop} > 0) { push @AnalysesToRun, "-analyzer-max-loop $Options{MaxLoop}"; } Index: clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp =================================================================== --- clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp +++ clang/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp @@ -46,8 +46,7 @@ std::unique_ptr AnalysisConsumer = CreateAnalysisConsumer(Compiler); AnalysisConsumer->AddDiagnosticConsumer(new DiagConsumer(DiagsOutput)); - Compiler.getAnalyzerOpts()->CheckersControlList = { - {"custom.CustomChecker", true}}; + Compiler.getAnalyzerOpts()->Checkers = {{"custom.CustomChecker", true}}; AnalysisConsumer->AddCheckerRegistrationFn([](CheckerRegistry &Registry) { Registry.addChecker("custom.CustomChecker", "Description", ""); });