Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -416,6 +416,7 @@ } static bool shouldSkipFunction(const Decl *D, + AnalyzerOptionsRef Opts, const SetOfConstDecls &Visited, const SetOfConstDecls &VisitedAsTopLevel) { if (VisitedAsTopLevel.count(D)) @@ -438,8 +439,22 @@ return false; } - // Otherwise, if we visited the function before, do not reanalyze it. - return Visited.count(D); + // Otherwise, if we visited the function before, do not reanalyze it + // unless we don't want to analyze all functions. + if (Opts->getBooleanOption("skip-inlined-functions", true)) + return Visited.count(D); + + // If "skip-inlined-functions" option was set to false, + // we want to reanalyze every function that can be visible externally + // (but we exclude C static functions and private methods). + if (D->getAccess() == AS_private) + return true; + + const FunctionDecl *FD = dyn_cast(D); + if (FD && !FD->hasExternalFormalLinkage()) + return true; + + return false; } ExprEngine::InliningModes @@ -489,7 +504,7 @@ // Skip the functions which have been processed already or previously // inlined. - if (shouldSkipFunction(D, Visited, VisitedAsTopLevel)) + if (shouldSkipFunction(D, Opts, Visited, VisitedAsTopLevel)) continue; // Analyze the function. Index: test/Analysis/analyzer-config.c =================================================================== --- test/Analysis/analyzer-config.c +++ test/Analysis/analyzer-config.c @@ -25,7 +25,8 @@ // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: skip-inlined-functions = true // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 15 +// CHECK-NEXT: num-entries = 16 Index: test/Analysis/analyzer-config.cpp =================================================================== --- test/Analysis/analyzer-config.cpp +++ test/Analysis/analyzer-config.cpp @@ -36,6 +36,7 @@ // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: skip-inlined-functions = true // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 20 +// CHECK-NEXT: num-entries = 21 Index: test/Analysis/inlining/analyze-all-visible.cpp =================================================================== --- /dev/null +++ test/Analysis/inlining/analyze-all-visible.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.ExprInspection -analyzer-config skip-inlined-functions=false -analyzer-display-progress %s 2>&1 | FileCheck %s + +namespace c_like { + +// static functions are not analyzed again if they are inlined +static int static_func() { + return 0; +} + +// We analyze callee() again despite the fact it was inlined +// because it is externally visible +int callee(int arg) { + if (arg == 1) + return 1; // expected-warning{{REACHABLE}} + return 0; // expected-warning{{REACHABLE}} +} + +void caller() { + callee(1); + static_func(); +} + +} + +namespace private_methods { + +class Test { +public: + void test() { + callee(1); + } +private: + // We don't analyze private methods if they were inlined + int callee(int arg) { + if (arg == 1) + return 1; // expected-warning{{REACHABLE}} + return 0; // no-warning + } +}; + +} + +// CHECK: analyze-all-visible.cpp c_like::static_func() +// CHECK-NEXT: analyze-all-visible.cpp c_like::callee(int) +// CHECK-NEXT: analyze-all-visible.cpp c_like::caller() +// CHECK-NEXT: analyze-all-visible.cpp private_methods::Test::test() +// CHECK-NEXT: analyze-all-visible.cpp private_methods::Test::callee(int) +// CHECK-NEXT: analyze-all-visible.cpp private_methods::Test::test() +// CHECK-NEXT: analyze-all-visible.cpp c_like::caller() +// CHECK-NEXT: analyze-all-visible.cpp c_like::callee(int)