Skip to content

Commit

Permalink
[CFG] Provide construction contexts when constructors have cleanups.
Browse files Browse the repository at this point in the history
Now that we make it possible to query the CFG constructor element to find
information about the construction site, possible cleanup work represented by
ExprWithCleanups should not prevent us from providing this information.

This allows us to have a correct construction context for variables initialized
"by value" via elidable copy-constructors, such as 'i' in

  iterator i = vector.begin();

Differential Revision: https://reviews.llvm.org/D42719

llvm-svn: 324798
  • Loading branch information
haoNoQ committed Feb 10, 2018
1 parent c8c9d4f commit 08225bb
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 13 deletions.
2 changes: 2 additions & 0 deletions clang/lib/Analysis/CFG.cpp
Expand Up @@ -1158,6 +1158,8 @@ void CFGBuilder::EnterConstructionContextIfNecessary(
assert(CurrentConstructionContext.isNull() &&
"Already within a construction context!");
CurrentConstructionContext = ConstructionContext(Trigger);
} else if (auto *Cleanups = dyn_cast<ExprWithCleanups>(Child)) {
EnterConstructionContextIfNecessary(Trigger, Cleanups->getSubExpr());
}
}

Expand Down
77 changes: 75 additions & 2 deletions clang/test/Analysis/cfg-rich-constructors.cpp
Expand Up @@ -92,18 +92,45 @@ void simpleVariableWithOperatorNewInBraces() {
C c{new C()};
}

// TODO: Should find construction target here.
// CHECK: void simpleVariableInitializedByValue()
// CHECK: 1: C::get
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
// CHECK-NEXT: 3: [B1.2]()
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, class C)
// CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], class C)
// CHECK-NEXT: 6: C c = C::get();
void simpleVariableInitializedByValue() {
C c = C::get();
}

// TODO: Should find construction target for the three temporaries as well.
// CHECK: void simpleVariableWithTernaryOperator(bool coin)
// CHECK: [B1]
// CHECK-NEXT: 1: [B4.2] ? [B2.5] : [B3.6]
// CHECK-NEXT: 2: [B1.1]
// CHECK-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.4], class C)
// CHECK-NEXT: 4: C c = coin ? C::get() : C(0);
// CHECK: [B2]
// CHECK-NEXT: 1: C::get
// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
// CHECK-NEXT: 3: [B2.2]()
// CHECK-NEXT: 4: [B2.3]
// CHECK-NEXT: 5: [B2.4] (CXXConstructExpr, class C)
// CHECK: [B3]
// CHECK-NEXT: 1: 0
// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
// CHECK-NEXT: 3: [B3.2] (CXXConstructExpr, class C)
// CHECK-NEXT: 4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
// CHECK-NEXT: 5: [B3.4]
// CHECK-NEXT: 6: [B3.5] (CXXConstructExpr, class C)
// CHECK: [B4]
// CHECK-NEXT: 1: coin
// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK-NEXT: T: [B4.2] ? ... : ...
void simpleVariableWithTernaryOperator(bool coin) {
C c = coin ? C::get() : C(0);
}

// TODO: Should find construction target here.
// CHECK: void referenceVariableWithConstructor()
// CHECK: 1: 0
Expand All @@ -125,6 +152,34 @@ void referenceVariableWithInitializer() {
const C &c = C();
}

// TODO: Should find construction targets here.
// CHECK: void referenceVariableWithTernaryOperator(bool coin)
// CHECK: [B1]
// CHECK-NEXT: 1: [B4.2] ? [B2.5] : [B3.6]
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, NoOp, const class C)
// CHECK-NEXT: 3: [B1.2]
// CHECK-NEXT: 4: const C &c = coin ? C::get() : C(0);
// CHECK: [B2]
// CHECK-NEXT: 1: C::get
// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
// CHECK-NEXT: 3: [B2.2]()
// CHECK-NEXT: 4: [B2.3]
// CHECK-NEXT: 5: [B2.4] (CXXConstructExpr, class C)
// CHECK: [B3]
// CHECK-NEXT: 1: 0
// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, NullToPointer, class C *)
// CHECK-NEXT: 3: [B3.2] (CXXConstructExpr, class C)
// CHECK-NEXT: 4: C([B3.3]) (CXXFunctionalCastExpr, ConstructorConversion, class C)
// CHECK-NEXT: 5: [B3.4]
// CHECK-NEXT: 6: [B3.5] (CXXConstructExpr, class C)
// CHECK: [B4]
// CHECK-NEXT: 1: coin
// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
// CHECK-NEXT: T: [B4.2] ? ... : ...
void referenceVariableWithTernaryOperator(bool coin) {
const C &c = coin ? C::get() : C(0);
}

} // end namespace decl_stmt

namespace ctor_initializers {
Expand All @@ -148,6 +203,24 @@ class D: public C {
// CHECK: 1: (CXXConstructExpr, D() (Delegating initializer), class ctor_initializers::D)
// CHECK-NEXT: 2: D([B1.1]) (Delegating initializer)
D(int): D() {}

// CHECK: D(double)
// CHECK: 1: C::get
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
// CHECK-NEXT: 3: [B1.2]()
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, C([B1.4]) (Base initializer), class C)
// CHECK-NEXT: 6: C([B1.5]) (Base initializer)
// CHECK-NEXT: 7: CFGNewAllocator(C *)
// CHECK-NEXT: 8: C::get
// CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void))
// CHECK-NEXT: 10: [B1.9]()
// CHECK-NEXT: 11: [B1.10]
// CHECK-NEXT: 12: [B1.11] (CXXConstructExpr, [B1.13], class C)
// CHECK-NEXT: 13: new C([B1.12])
// CHECK-NEXT: 14: [B1.13] (CXXConstructExpr, c1([B1.13]) (Member initializer), class C)
// CHECK-NEXT: 15: c1([B1.14]) (Member initializer)
D(double): C(C::get()), c1(new C(C::get())) {}
};

} // end namespace ctor_initializers
44 changes: 33 additions & 11 deletions clang/test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -1,8 +1,23 @@
// RUN: rm -f %t
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-temporary-dtors=true -std=c++98 %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefix=CXX98 -check-prefix=CHECK %s
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-temporary-dtors=true -std=c++11 %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefix=CXX11 -check-prefix=CHECK %s
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-temporary-dtors=true,cfg-rich-constructors=false -std=c++98 %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX98,WARNINGS,CXX98-WARNINGS %s
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-temporary-dtors=true,cfg-rich-constructors=false -std=c++11 %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,WARNINGS,CXX11-WARNINGS %s
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-temporary-dtors=true,cfg-rich-constructors=true -std=c++98 %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX98,ANALYZER,CXX98-ANALYZER %s
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-temporary-dtors=true,cfg-rich-constructors=true -std=c++11 %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,ANALYZER,CXX11-ANALYZER %s

// This file tests how we construct two different flavors of the Clang CFG -
// the CFG used by the Sema analysis-based warnings and the CFG used by the
// static analyzer. The difference in the behavior is checked via FileCheck
// prefixes (WARNINGS and ANALYZER respectively). When introducing new analyzer
// flags, no new run lines should be added - just these flags would go to the
// respective line depending on where is it turned on and where is it turned
// off. Feel free to add tests that test only one of the CFG flavors if you're
// not sure how the other flavor is supposed to work in your case.

// Additionally, different C++ standards are checked.

class A {
public:
Expand Down Expand Up @@ -492,7 +507,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: 1: [B10.5] ? [B8.6] : [B9.15]
// CHECK: 2: [B7.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B7.2]
// CHECK: 4: [B7.3] (CXXConstructExpr, class A)
// WARNINGS: 4: [B7.3] (CXXConstructExpr, class A)
// ANALYZER: 4: [B7.3] (CXXConstructExpr, [B7.5], class A)
// CHECK: 5: A a = B() ? A() : A(B());
// CHECK: T: (Temp Dtor) [B9.2]
// CHECK: Preds (2): B8 B9
Expand Down Expand Up @@ -625,7 +641,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: 2: [B4.1] (BindTemporary)
// CHECK: 3: [B4.2] (ImplicitCastExpr, NoOp, const struct C)
// CHECK: 4: [B4.3]
// CHECK: 5: [B4.4] (CXXConstructExpr, struct C)
// WARNINGS: 5: [B4.4] (CXXConstructExpr, struct C)
// ANALYZER: 5: [B4.4] (CXXConstructExpr, [B4.6], struct C)
// CHECK: 6: C c = C();
// CHECK: 7: ~C() (Temporary object destructor)
// CHECK: 8: c
Expand Down Expand Up @@ -675,15 +692,17 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: 1: D() (CXXConstructExpr, struct D)
// CXX98: 2: [B3.1] (ImplicitCastExpr, NoOp, const struct D)
// CXX98: 3: [B3.2]
// CXX98: 4: [B3.3] (CXXConstructExpr, struct D)
// CXX98-WARNINGS: 4: [B3.3] (CXXConstructExpr, struct D)
// CXX98-ANALYZER: 4: [B3.3] (CXXConstructExpr, [B3.5], struct D)
// CXX98: 5: D d = D();
// CXX98: 6: d
// CXX98: 7: [B3.6].operator bool
// CXX98: 8: [B3.6]
// CXX98: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool)
// CXX98: T: if [B3.9]
// CXX11: 2: [B3.1]
// CXX11: 3: [B3.2] (CXXConstructExpr, struct D)
// CXX11-WARNINGS: 3: [B3.2] (CXXConstructExpr, struct D)
// CXX11-ANALYZER: 3: [B3.2] (CXXConstructExpr, [B3.4], struct D)
// CXX11: 4: D d = D();
// CXX11: 5: d
// CXX11: 6: [B3.5].operator bool
Expand Down Expand Up @@ -838,7 +857,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CXX11: 1: [B7.3] ?: [B6.6]
// CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 3: [B4.2]
// CHECK: 4: [B4.3] (CXXConstructExpr, class A)
// WARNINGS: 4: [B4.3] (CXXConstructExpr, class A)
// ANALYZER: 4: [B4.3] (CXXConstructExpr, [B4.5], class A)
// CHECK: 5: A a = A() ?: A();
// CHECK: T: (Temp Dtor) [B6.2]
// CHECK: Preds (2): B5 B6
Expand Down Expand Up @@ -993,7 +1013,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: 2: [B1.1] (BindTemporary)
// CHECK: 3: [B1.2] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 4: [B1.3]
// CHECK: 5: [B1.4] (CXXConstructExpr, class A)
// WARNINGS: 5: [B1.4] (CXXConstructExpr, class A)
// ANALYZER: 5: [B1.4] (CXXConstructExpr, [B1.6], class A)
// CHECK: 6: A a = A();
// CHECK: 7: ~A() (Temporary object destructor)
// CHECK: 8: int b;
Expand Down Expand Up @@ -1033,7 +1054,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: 4: [B1.3] (BindTemporary)
// CHECK: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A)
// CHECK: 6: [B1.5]
// CHECK: 7: [B1.6] (CXXConstructExpr, class A)
// WARNINGS: 7: [B1.6] (CXXConstructExpr, class A)
// ANALYZER: 7: [B1.6] (CXXConstructExpr, [B1.8], class A)
// CHECK: 8: A a = A::make();
// CHECK: 9: ~A() (Temporary object destructor)
// CHECK: 10: int b;
Expand Down

0 comments on commit 08225bb

Please sign in to comment.