diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -138,6 +138,8 @@ UMK_Deep = 2 }; +enum class CTUPhase1InliningKind { None, Small, All }; + /// Stores options for the analyzer from the command line. /// /// Some options are frontend flags (e.g.: -analyzer-output), but some are @@ -379,6 +381,7 @@ UserModeKind getUserMode() const; ExplorationStrategyKind getExplorationStrategy() const; + CTUPhase1InliningKind getCTUPhase1Inlining() const; /// Returns the inter-procedural analysis mode. IPAKind getIPAMode() const; diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -425,6 +425,13 @@ "is too low, it is meaningful to provide a minimum value that serves as an " "upper bound instead.", 1000) +ANALYZER_OPTION( + StringRef, CTUPhase1InliningMode, "ctu-phase1-inlining", + "Controls which functions will be inlined during the first phase of the ctu " + "analysis." + "Value: \"none\", \"small\", \"all\".", + "small") + ANALYZER_OPTION( unsigned, RegionStoreSmallStructLimit, "region-store-small-struct-limit", "The largest number of fields a struct can have and still be considered " diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp --- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -81,6 +81,17 @@ return K.getValue(); } +CTUPhase1InliningKind AnalyzerOptions::getCTUPhase1Inlining() const { + auto K = llvm::StringSwitch>( + CTUPhase1InliningMode) + .Case("none", CTUPhase1InliningKind::None) + .Case("small", CTUPhase1InliningKind::Small) + .Case("all", CTUPhase1InliningKind::All) + .Default(None); + assert(K.hasValue() && "CTU inlining mode is invalid."); + return K.getValue(); +} + IPAKind AnalyzerOptions::getIPAMode() const { auto K = llvm::StringSwitch>(IPAMode) .Case("none", IPAK_None) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -435,6 +435,14 @@ ProgramStateRef ConservativeEvalState = nullptr; WorkList *CTUWList = Engine.getCTUWorkList(); if (Call.isForeign() && CTUWList) { + const auto IK = AMgr.options.getCTUPhase1Inlining(); + const bool DoInline = IK == CTUPhase1InliningKind::All || + (IK == CTUPhase1InliningKind::Small && + isSmall(AMgr.getAnalysisDeclContext(D))); + if (DoInline) { + inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); + return true; + } const bool BState = State->contains(D); if (!BState) { // This is the first time we see this foreign function. // Enqueue it to be analyzed in the second (ctu) phase. diff --git a/clang/test/Analysis/Inputs/ctu-onego-small-other.cpp b/clang/test/Analysis/Inputs/ctu-onego-small-other.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/ctu-onego-small-other.cpp @@ -0,0 +1,3 @@ +int bar() { + return 0; +} diff --git a/clang/test/Analysis/Inputs/ctu-onego-small-other.cpp.externalDefMap.ast-dump.txt b/clang/test/Analysis/Inputs/ctu-onego-small-other.cpp.externalDefMap.ast-dump.txt new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/ctu-onego-small-other.cpp.externalDefMap.ast-dump.txt @@ -0,0 +1 @@ +9:c:@F@bar# ctu-onego-small-other.cpp.ast diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -51,6 +51,7 @@ // CHECK-NEXT: ctu-invocation-list = invocations.yaml // CHECK-NEXT: ctu-max-nodes-min = 1000 // CHECK-NEXT: ctu-max-nodes-mul = 100 +// CHECK-NEXT: ctu-phase1-inlining = small // CHECK-NEXT: deadcode.DeadStores:ShowFixIts = false // CHECK-NEXT: deadcode.DeadStores:WarnForDeadNestedAssignments = true // CHECK-NEXT: debug.AnalysisOrder:* = false diff --git a/clang/test/Analysis/ctu-main.c b/clang/test/Analysis/ctu-main.c --- a/clang/test/Analysis/ctu-main.c +++ b/clang/test/Analysis/ctu-main.c @@ -3,12 +3,24 @@ // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu \ // RUN: -emit-pch -o %t/ctudir2/ctu-other.c.ast %S/Inputs/ctu-other.c // RUN: cp %S/Inputs/ctu-other.c.externalDefMap.ast-dump.txt %t/ctudir2/externalDefMap.txt + +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -std=c89 -analyze \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir2 \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ +// RUN: -verify=newctu %s + +// Simulate the behavior of the previous CTU implementation by inlining all +// functions during the first phase. This way, the second phase is a noop. // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -std=c89 -analyze \ // RUN: -analyzer-checker=core,debug.ExprInspection \ // RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir2 \ -// RUN: -verify %s +// RUN: -analyzer-config ctu-phase1-inlining=all \ +// RUN: -verify=oldctu %s void clang_analyzer_eval(int); @@ -17,7 +29,8 @@ int unknown(int); void test_unknown() { int res = unknown(6); - clang_analyzer_eval(res == 6); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(res == 6); // newctu-warning{{UNKNOWN}} + // oldctu-warning@-1{{UNKNOWN}} } // Test typedef and global variable in function. @@ -28,8 +41,9 @@ extern FooBar fb; int f(int); void testGlobalVariable() { - clang_analyzer_eval(f(5) == 1); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(f(5) == 1); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} } // Test enums. @@ -38,9 +52,11 @@ y, z }; void testEnum(void) { - clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} - clang_analyzer_eval(enumCheck() == 42); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(x == 0); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(enumCheck() == 42); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} } // Test that asm import does not fail. @@ -53,19 +69,22 @@ struct S; int g(struct S *); void testMacro(void) { - g(0); // expected-warning@Inputs/ctu-other.c:29 {{Access to field 'a' results in a dereference of a null pointer (loaded from variable 'ctx')}} + g(0); // newctu-warning@Inputs/ctu-other.c:29 {{Access to field 'a' results in a dereference of a null pointer (loaded from variable 'ctx')}} + // oldctu-warning@Inputs/ctu-other.c:29 {{Access to field 'a' results in a dereference of a null pointer (loaded from variable 'ctx')}} } // The external function prototype is incomplete. // warning:implicit functions are prohibited by c99 void testImplicit(void) { int res = identImplicit(6); // external implicit functions are not inlined - clang_analyzer_eval(res == 6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(res == 6); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} // Call something with uninitialized from the same function in which the implicit was called. // This is necessary to reproduce a special bug in NoStoreFuncVisitor. int uninitialized; - h(uninitialized); // expected-warning{{1st function call argument is an uninitialized value}} + h(uninitialized); // newctu-warning{{1st function call argument is an uninitialized value}} + // oldctu-warning@-1{{1st function call argument is an uninitialized value}} } // Tests the import of functions that have a struct parameter @@ -80,7 +99,8 @@ d.a = 1; d.b = 0; // Not imported, thus remains unknown both in stu and ctu. - clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(structInProto(&d) == 0); // newctu-warning{{UNKNOWN}} + // oldctu-warning@-1{{UNKNOWN}} } int switchWithoutCases(int); diff --git a/clang/test/Analysis/ctu-main.cpp b/clang/test/Analysis/ctu-main.cpp --- a/clang/test/Analysis/ctu-main.cpp +++ b/clang/test/Analysis/ctu-main.cpp @@ -5,12 +5,25 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ // RUN: -emit-pch -o %t/ctudir/ctu-chain.cpp.ast %S/Inputs/ctu-chain.cpp // RUN: cp %S/Inputs/ctu-other.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ +// RUN: -verify=newctu %s + +// Simulate the behavior of the previous CTU implementation by inlining all +// functions during the first phase. This way, the second phase is a noop. // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ // RUN: -analyzer-checker=core,debug.ExprInspection \ // RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir \ -// RUN: -verify %s +// RUN: -analyzer-config ctu-phase1-inlining=all \ +// RUN: -verify=oldctu %s + // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ // RUN: -analyzer-checker=core,debug.ExprInspection \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ @@ -114,13 +127,17 @@ void test_virtual_functions(mycls* obj) { // The dynamic type is known. - clang_analyzer_eval(mycls().fvcl(1) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(derived().fvcl(1) == 9); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls().fvcl(1) == 8); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(derived().fvcl(1) == 9); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} // We cannot decide about the dynamic type. - clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} ctu, stu + clang_analyzer_eval(obj->fvcl(1) == 8); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} ctu, stu + // oldctu-warning@-2{{TRUE}} + // oldctu-warning@-3{{UNKNOWN}} } class TestAnonUnionUSR { @@ -141,62 +158,92 @@ extern int testImportOfDelegateConstructor(int); int main() { - clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - - clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - - clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - - clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - // expected-warning@Inputs/ctu-other.cpp:93{{REACHABLE}} - MACRODIAG(); // expected-warning{{REACHABLE}} + clang_analyzer_eval(f(3) == 2); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(f(4) == 3); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(f(5) == 3); // newctu-warning{{FALSE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{FALSE}} + clang_analyzer_eval(g(4) == 6); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(h(2) == 8); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + + clang_analyzer_eval(myns::fns(2) == 9); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(mycls().fcl(1) == 6); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(mycls::fscl(1) == 7); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + + clang_analyzer_eval(chns::chf1(4) == 12); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + clang_analyzer_eval(fun_using_anon_struct(8) == 8); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + + clang_analyzer_eval(other_macro_diag(1) == 1); // newctu-warning{{TRUE}} ctu + // newctu-warning@-1{{UNKNOWN}} stu + // oldctu-warning@-2{{TRUE}} + // newctu-warning@Inputs/ctu-other.cpp:93{{REACHABLE}} + // oldctu-warning@Inputs/ctu-other.cpp:93{{REACHABLE}} + MACRODIAG(); // newctu-warning{{REACHABLE}} + // oldctu-warning@-1{{REACHABLE}} // FIXME we should report an UNKNOWN as well for all external variables! - clang_analyzer_eval(extInt == 2); // expected-warning{{TRUE}} - clang_analyzer_eval(intns::extInt == 3); // expected-warning{{TRUE}} - clang_analyzer_eval(extS.a == 4); // expected-warning{{TRUE}} - clang_analyzer_eval(extNonConstS.a == 4); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(extInt == 2); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(intns::extInt == 3); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(extS.a == 4); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(extNonConstS.a == 4); // newctu-warning{{UNKNOWN}} + // oldctu-warning@-1{{UNKNOWN}} // Do not import non-trivial classes' initializers. - clang_analyzer_eval(extNTS.a == 4); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(extHere == 6); // expected-warning{{TRUE}} - clang_analyzer_eval(A::a == 3); // expected-warning{{TRUE}} - clang_analyzer_eval(extSC.a == 8); // expected-warning{{TRUE}} - clang_analyzer_eval(ST::sc.a == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(extNTS.a == 4); // newctu-warning{{UNKNOWN}} + // oldctu-warning@-1{{UNKNOWN}} + clang_analyzer_eval(extHere == 6); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(A::a == 3); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(extSC.a == 8); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(ST::sc.a == 2); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} // clang_analyzer_eval(extSCN.scn.a == 9); // TODO - clang_analyzer_eval(extSubSCN.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(extSubSCN.a == 1); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} // clang_analyzer_eval(extSCC.a == 7); // TODO - clang_analyzer_eval(extU.a == 4); // expected-warning{{TRUE}} - clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // expected-warning{{TRUE}} + clang_analyzer_eval(extU.a == 4); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} + clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // newctu-warning{{TRUE}} + // oldctu-warning@-1{{TRUE}} clang_analyzer_eval(testImportOfIncompleteDefaultParmDuringImport(9) == 9); - // expected-warning@-1{{TRUE}} ctu - // expected-warning@-2{{UNKNOWN}} stu + // newctu-warning@-1{{TRUE}} ctu + // newctu-warning@-2{{UNKNOWN}} stu + // oldctu-warning@-3{{TRUE}} clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); - // expected-warning@-1{{TRUE}} ctu - // expected-warning@-2{{UNKNOWN}} stu + // newctu-warning@-1{{TRUE}} ctu + // newctu-warning@-2{{UNKNOWN}} stu + // oldctu-warning@-3{{TRUE}} } diff --git a/clang/test/Analysis/ctu-on-demand-parsing.c b/clang/test/Analysis/ctu-on-demand-parsing.c --- a/clang/test/Analysis/ctu-on-demand-parsing.c +++ b/clang/test/Analysis/ctu-on-demand-parsing.c @@ -13,12 +13,16 @@ // // RUN: cd "%t" && %clang_cc1 -fsyntax-only -std=c89 -analyze \ // RUN: -analyzer-checker=core,debug.ExprInspection \ -// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=. \ // RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ +// RUN: -analyzer-config ctu-phase1-inlining=all \ // RUN: -verify ctu-on-demand-parsing.c // +// FIXME On-demand ctu should be tested in the very same file that we have for +// the PCH version, but with a different a different verify prefix (e.g. +// -verfiy=on-demanc-ctu) +// // FIXME: Path handling should work on all platforms. // REQUIRES: system-linux @@ -33,7 +37,6 @@ int f(int); void testGlobalVariable() { clang_analyzer_eval(f(5) == 1); // expected-warning{{TRUE}} - // expected-warning@-1{{UNKNOWN}} stu } // Test enums. @@ -44,7 +47,6 @@ void testEnum() { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(enumCheck() == 42); // expected-warning{{TRUE}} - // expected-warning@-1{{UNKNOWN}} stu } // Test that asm import does not fail. @@ -64,7 +66,6 @@ void testImplicit() { int res = identImplicit(6); // external implicit functions are not inlined clang_analyzer_eval(res == 6); // expected-warning{{TRUE}} - // expected-warning@-1{{UNKNOWN}} stu // Call something with uninitialized from the same function in which the // implicit was called. This is necessary to reproduce a special bug in // NoStoreFuncVisitor. @@ -83,6 +84,5 @@ struct DataType d; d.a = 1; d.b = 0; - // Not imported, thus remains unknown both in stu and ctu. - clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{TRUE}} expected-warning{{FALSE}} } diff --git a/clang/test/Analysis/ctu-on-demand-parsing.cpp b/clang/test/Analysis/ctu-on-demand-parsing.cpp --- a/clang/test/Analysis/ctu-on-demand-parsing.cpp +++ b/clang/test/Analysis/ctu-on-demand-parsing.cpp @@ -15,10 +15,10 @@ // // RUN: cd "%t" && %clang_analyze_cc1 \ // RUN: -analyzer-checker=core,debug.ExprInspection \ -// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=. \ // RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ +// RUN: -analyzer-config ctu-phase1-inlining=all \ // RUN: -verify ctu-on-demand-parsing.cpp // RUN: cd "%t" && %clang_analyze_cc1 \ // RUN: -analyzer-checker=core,debug.ExprInspection \ @@ -29,6 +29,10 @@ // // CHECK: CTU loaded AST file: {{.*}}ctu-other.cpp // CHECK: CTU loaded AST file: {{.*}}ctu-chain.cpp + +// FIXME On-demand ctu should be tested in the very same file that we have for +// the PCH version, but with a different a different verify prefix (e.g. +// -verfiy=on-demanc-ctu) // // FIXME: Path handling should work on all platforms. // REQUIRES: system-linux @@ -83,46 +87,30 @@ void test_virtual_functions(mycls *obj) { // The dynamic type is known. clang_analyzer_eval(mycls().fvcl(1) == 8); // expected-warning{{TRUE}} - // expected-warning@-1{{UNKNOWN}} stu clang_analyzer_eval(derived().fvcl(1) == 9); // expected-warning{{TRUE}} - // expected-warning@-1{{UNKNOWN}} stu // We cannot decide about the dynamic type. - clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} ctu, stu + clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{FALSE}} expected-warning{{TRUE}} + clang_analyzer_eval(obj->fvcl(1) == 9); // expected-warning{{FALSE}} expected-warning{{TRUE}} } int main() { - clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - - clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - - clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu - clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} ctu - // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} + clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} + clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} + + clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} + clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} + clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} + clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} + clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} + clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} + + clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} + clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} - // expected-warning@-1{{UNKNOWN}} stu // expected-warning@Inputs/ctu-other.cpp:93{{REACHABLE}} MACRODIAG(); // expected-warning{{REACHABLE}} } diff --git a/clang/test/Analysis/ctu-onego-indirect.cpp b/clang/test/Analysis/ctu-onego-indirect.cpp --- a/clang/test/Analysis/ctu-onego-indirect.cpp +++ b/clang/test/Analysis/ctu-onego-indirect.cpp @@ -18,6 +18,7 @@ // RUN: -analyzer-config ctu-dir=%t/ctudir \ // RUN: -analyzer-display-progress \ // RUN: -analyzer-inlining-mode=all \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ // RUN: -analyzer-config ctu-max-nodes-mul=100 \ // RUN: -analyzer-config ctu-max-nodes-min=1000 2>&1 %s | FileCheck %s // CHECK: ANALYZE (Path, Inline_Regular):{{.*}}adirectbaruser(int) @@ -30,6 +31,7 @@ // RUN: -analyzer-config ctu-dir=%t/ctudir \ // RUN: -analyzer-display-progress \ // RUN: -analyzer-inlining-mode=all \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ // RUN: -verify %s \ // RUN: -analyzer-config ctu-max-nodes-mul=100 \ // RUN: -analyzer-config ctu-max-nodes-min=1000 diff --git a/clang/test/Analysis/ctu-onego-small.cpp b/clang/test/Analysis/ctu-onego-small.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/ctu-onego-small.cpp @@ -0,0 +1,51 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -emit-pch -o %t/ctudir/ctu-onego-small-other.cpp.ast %S/Inputs/ctu-onego-small-other.cpp +// RUN: cp %S/Inputs/ctu-onego-small-other.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt + +// Small function defined in another TU. +int bar(); + +// Here we limit the ctu analysis to the first phase only (via the +// ctu-max-nodes config options). And we check whether the small foreign +// function `bar` is inlined. + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config display-ctu-progress=true \ +// RUN: -analyzer-display-progress \ +// RUN: -analyzer-config ctu-max-nodes-mul=0 \ +// RUN: -analyzer-config ctu-max-nodes-min=0 2>&1 %s | FileCheck %s +// CHECK: ANALYZE (Path, Inline_Regular): {{.*}} baruser(int){{.*}}CTU loaded AST file + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config ctu-max-nodes-mul=0 \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ +// RUN: -analyzer-config ctu-max-nodes-min=0 -verify=inline-none %s + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config ctu-max-nodes-mul=0 \ +// RUN: -analyzer-config ctu-phase1-inlining=small \ +// RUN: -analyzer-config ctu-max-nodes-min=0 -verify=inline-small %s + + +void clang_analyzer_eval(int); + +void baruser(int x) { + int y = bar(); + // inline-none-warning@+2{{UNKNOWN}} + // inline-small-warning@+1{{TRUE}} + clang_analyzer_eval(y == 0); +} diff --git a/clang/test/Analysis/ctu-onego-toplevel.cpp b/clang/test/Analysis/ctu-onego-toplevel.cpp --- a/clang/test/Analysis/ctu-onego-toplevel.cpp +++ b/clang/test/Analysis/ctu-onego-toplevel.cpp @@ -9,6 +9,7 @@ // RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ // RUN: -verify=ctu %s // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ @@ -16,6 +17,7 @@ // RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ // RUN: -analyzer-config display-ctu-progress=true \ // RUN: -analyzer-display-progress \ // RUN: -verify=ctu %s 2>&1 | FileCheck %s