diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index 7d3d2d118..7b4ba2390 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -518,6 +518,9 @@ struct let mangled_name = Mangled.mangled name.Clang_ast_t.ni_name mangled in Sil.mk_pvar mangled_name procname | None -> Sil.mk_pvar simple_name procname + + let is_cpp_translation language = + language = CFrontend_config.CPP || language = CFrontend_config.OBJCPP end diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 42350b13f..82da174e2 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -154,4 +154,6 @@ sig val mk_sil_var : Clang_ast_t.named_decl_info -> var_info option -> Procname.t -> Procname.t -> Sil.pvar + + val is_cpp_translation : CFrontend_config.lang -> bool end diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index b4965a48f..252f33ba5 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -495,7 +495,9 @@ struct let instrs_after_assign, assign_ids, exp_to_parent = if (is_binary_assign_op binary_operator_info) - && ((not creating_node) || (is_return_temp trans_state.continuation)) then ( + (* assignment operator result is lvalue in CPP, rvalue in C, hence the difference *) + && (not (General_utils.is_cpp_translation !CFrontend_config.language)) + && ((not creating_node) || (is_return_temp trans_state.continuation)) then ( (* We are in this case when an assignment is inside *) (* another operator that creates a node. Eg. another *) (* assignment. *) diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 2603c41c8..b819f3116 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -443,6 +443,9 @@ let compute_instr_ids_exp_to_parent stmt_info instr ids e lhs typ loc pri = (* The current AST element has created a node then instr and ids have *) (* been already included in the node. *) [], [], e + ) else if General_utils.is_cpp_translation !CFrontend_config.language then ( + (* assignment operator result is lvalue in CPP, rvalue in C, hence the difference *) + instr, ids, e ) else ( (* The node will be created by the parent. We pass the instr and ids. *) (* For the expression we need to save the constend of the lhs in a new *) diff --git a/infer/tests/codetoanalyze/c/frontend/nestedoperators/assign_in_condition.cpp b/infer/tests/codetoanalyze/c/frontend/nestedoperators/assign_in_condition.cpp new file mode 120000 index 000000000..68d1b814d --- /dev/null +++ b/infer/tests/codetoanalyze/c/frontend/nestedoperators/assign_in_condition.cpp @@ -0,0 +1 @@ +assign_in_condition.c \ No newline at end of file diff --git a/infer/tests/codetoanalyze/c/frontend/nestedoperators/assign_in_condition.cpp.dot b/infer/tests/codetoanalyze/c/frontend/nestedoperators/assign_in_condition.cpp.dot new file mode 100644 index 000000000..b786067ef --- /dev/null +++ b/infer/tests/codetoanalyze/c/frontend/nestedoperators/assign_in_condition.cpp.dot @@ -0,0 +1,34 @@ +digraph iCFG { +8 [label="8: Return Stmt \n *&return:int =32 [line 12]\n APPLY_ABSTRACTION; [line 12]\n " shape="box"] + + + 8 -> 2 ; +7 [label="7: Prune (false branch) \n n$1=*n$0:int [line 11]\n PRUNE((n$1 == 0), false); [line 11]\n REMOVE_TEMPS(n$0,n$1); [line 11]\n " shape="invhouse"] + + + 7 -> 4 ; +6 [label="6: Prune (true branch) \n n$1=*n$0:int [line 11]\n PRUNE((n$1 != 0), true); [line 11]\n REMOVE_TEMPS(n$0,n$1); [line 11]\n " shape="invhouse"] + + + 6 -> 8 ; +5 [label="5: BinaryOperatorStmt: Assign \n n$0=*&p:int * [line 11]\n *n$0:int =0 [line 11]\n NULLIFY(&p,false); [line 11]\n " shape="box"] + + + 5 -> 6 ; + 5 -> 7 ; +4 [label="4: + \n " ] + + + 4 -> 3 ; +3 [label="3: Return Stmt \n *&return:int =52 [line 14]\n APPLY_ABSTRACTION; [line 14]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit foo \n " color=yellow style=filled] + + +1 [label="1: Start foo\nFormals: p:int *\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled] + + + 1 -> 5 ; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/nested_assignment.cpp b/infer/tests/codetoanalyze/cpp/frontend/reference/nested_assignment.cpp new file mode 100644 index 000000000..f14e318b0 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/nested_assignment.cpp @@ -0,0 +1,30 @@ +/* +* Copyright (c) 2015 - present Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ + +void normal() { + int a = 3; + int &ref_from_val = a; + int &ref_from_ref = ref_from_val; +} + +void nested() { + int a = 3; + int &ref_from_val = a = 4; + int &ref_from_ref = ref_from_val = 6; +} + + +void crazy_nested() { + int a = 3; + int b = a; + + // a will refer to same object as ref_from_val and ref_from_ref, but different than b + int& ref_from_val = a = b = 4; + int& ref_from_ref = ref_from_val = b = 5; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/nested_assignment.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/nested_assignment.cpp.dot new file mode 100644 index 000000000..dfb4399e5 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/nested_assignment.cpp.dot @@ -0,0 +1,63 @@ +digraph iCFG { +16 [label="16: DeclStmt \n *&a:int =3 [line 24]\n " shape="box"] + + + 16 -> 15 ; +15 [label="15: DeclStmt \n n$3=*&a:int [line 25]\n *&b:int =n$3 [line 25]\n REMOVE_TEMPS(n$3); [line 25]\n NULLIFY(&b,false); [line 25]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: DeclStmt \n *&b:int =4 [line 28]\n n$2=*&b:int [line 28]\n *&a:int =n$2 [line 28]\n *&ref_from_val:int =&a [line 28]\n REMOVE_TEMPS(n$2); [line 28]\n NULLIFY(&b,false); [line 28]\n " shape="box"] + + + 14 -> 13 ; +13 [label="13: DeclStmt \n n$0=*&ref_from_val:int & [line 29]\n *&b:int =5 [line 29]\n n$1=*&b:int [line 29]\n *n$0:int =n$1 [line 29]\n *&ref_from_ref:int =n$0 [line 29]\n REMOVE_TEMPS(n$0,n$1); [line 29]\n NULLIFY(&b,false); [line 29]\n NULLIFY(&ref_from_ref,false); [line 29]\n NULLIFY(&ref_from_val,false); [line 29]\n NULLIFY(&a,false); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] + + + 13 -> 12 ; +12 [label="12: Exit crazy_nested \n " color=yellow style=filled] + + +11 [label="11: Start crazy_nested\nFormals: \nLocals: ref_from_ref:int & ref_from_val:int & b:int a:int \n DECLARE_LOCALS(&return,&ref_from_ref,&ref_from_val,&b,&a); [line 23]\n NULLIFY(&b,false); [line 23]\n NULLIFY(&ref_from_ref,false); [line 23]\n NULLIFY(&ref_from_val,false); [line 23]\n " color=yellow style=filled] + + + 11 -> 16 ; +10 [label="10: DeclStmt \n *&a:int =3 [line 17]\n " shape="box"] + + + 10 -> 9 ; +9 [label="9: DeclStmt \n *&a:int =4 [line 18]\n *&ref_from_val:int =&a [line 18]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: DeclStmt \n n$0=*&ref_from_val:int & [line 19]\n *n$0:int =6 [line 19]\n *&ref_from_ref:int =n$0 [line 19]\n REMOVE_TEMPS(n$0); [line 19]\n NULLIFY(&ref_from_ref,false); [line 19]\n NULLIFY(&ref_from_val,false); [line 19]\n NULLIFY(&a,false); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: Exit nested \n " color=yellow style=filled] + + +6 [label="6: Start nested\nFormals: \nLocals: ref_from_ref:int & ref_from_val:int & a:int \n DECLARE_LOCALS(&return,&ref_from_ref,&ref_from_val,&a); [line 16]\n NULLIFY(&ref_from_ref,false); [line 16]\n NULLIFY(&ref_from_val,false); [line 16]\n " color=yellow style=filled] + + + 6 -> 10 ; +5 [label="5: DeclStmt \n *&a:int =3 [line 11]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: DeclStmt \n *&ref_from_val:int =&a [line 12]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: DeclStmt \n n$0=*&ref_from_val:int & [line 13]\n *&ref_from_ref:int =n$0 [line 13]\n REMOVE_TEMPS(n$0); [line 13]\n NULLIFY(&ref_from_ref,false); [line 13]\n NULLIFY(&ref_from_val,false); [line 13]\n NULLIFY(&a,false); [line 13]\n APPLY_ABSTRACTION; [line 13]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit normal \n " color=yellow style=filled] + + +1 [label="1: Start normal\nFormals: \nLocals: ref_from_ref:int & ref_from_val:int & a:int \n DECLARE_LOCALS(&return,&ref_from_ref,&ref_from_val,&a); [line 10]\n NULLIFY(&ref_from_ref,false); [line 10]\n NULLIFY(&ref_from_val,false); [line 10]\n " color=yellow style=filled] + + + 1 -> 5 ; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp index 746b280f1..84ee7d2ec 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp @@ -49,3 +49,21 @@ int ref_div0_function_temp_var() { zero_ref(r); return 1/a; } + +int ref_div0_nested_assignment() { + int a = 2; + int b = 3; + int &r1 = a; + int &r2 = r1 = b = 1; + r2 = 0; + return 1/a; +} + +int ref_div1_nested_assignment() { + int a = 2; + int b = 3; + int &r1 = a; + int &r2 = r1 = b = 1; + r2 = 0; + return 1/b; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot index d142b481c..434117f15 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot @@ -1,4 +1,66 @@ digraph iCFG { +56 [label="56: DeclStmt \n *&a:int =2 [line 63]\n " shape="box"] + + + 56 -> 55 ; +55 [label="55: DeclStmt \n *&b:int =3 [line 64]\n NULLIFY(&b,false); [line 64]\n " shape="box"] + + + 55 -> 54 ; +54 [label="54: DeclStmt \n *&r1:int =&a [line 65]\n " shape="box"] + + + 54 -> 53 ; +53 [label="53: DeclStmt \n n$2=*&r1:int & [line 66]\n *&b:int =1 [line 66]\n n$3=*&b:int [line 66]\n *n$2:int =n$3 [line 66]\n *&r2:int =n$2 [line 66]\n REMOVE_TEMPS(n$2,n$3); [line 66]\n NULLIFY(&r1,false); [line 66]\n " shape="box"] + + + 53 -> 52 ; +52 [label="52: BinaryOperatorStmt: Assign \n n$1=*&r2:int & [line 67]\n *n$1:int =0 [line 67]\n REMOVE_TEMPS(n$1); [line 67]\n NULLIFY(&r2,false); [line 67]\n " shape="box"] + + + 52 -> 51 ; +51 [label="51: Return Stmt \n n$0=*&b:int [line 68]\n *&return:int =(1 / n$0) [line 68]\n REMOVE_TEMPS(n$0); [line 68]\n NULLIFY(&b,false); [line 68]\n NULLIFY(&a,false); [line 68]\n APPLY_ABSTRACTION; [line 68]\n " shape="box"] + + + 51 -> 50 ; +50 [label="50: Exit ref_div1_nested_assignment \n " color=yellow style=filled] + + +49 [label="49: Start ref_div1_nested_assignment\nFormals: \nLocals: r2:int & r1:int & b:int a:int \n DECLARE_LOCALS(&return,&r2,&r1,&b,&a); [line 62]\n NULLIFY(&b,false); [line 62]\n NULLIFY(&r1,false); [line 62]\n NULLIFY(&r2,false); [line 62]\n " color=yellow style=filled] + + + 49 -> 56 ; +48 [label="48: DeclStmt \n *&a:int =2 [line 54]\n " shape="box"] + + + 48 -> 47 ; +47 [label="47: DeclStmt \n *&b:int =3 [line 55]\n NULLIFY(&b,false); [line 55]\n " shape="box"] + + + 47 -> 46 ; +46 [label="46: DeclStmt \n *&r1:int =&a [line 56]\n " shape="box"] + + + 46 -> 45 ; +45 [label="45: DeclStmt \n n$2=*&r1:int & [line 57]\n *&b:int =1 [line 57]\n n$3=*&b:int [line 57]\n *n$2:int =n$3 [line 57]\n *&r2:int =n$2 [line 57]\n REMOVE_TEMPS(n$2,n$3); [line 57]\n NULLIFY(&b,false); [line 57]\n NULLIFY(&r1,false); [line 57]\n " shape="box"] + + + 45 -> 44 ; +44 [label="44: BinaryOperatorStmt: Assign \n n$1=*&r2:int & [line 58]\n *n$1:int =0 [line 58]\n REMOVE_TEMPS(n$1); [line 58]\n NULLIFY(&r2,false); [line 58]\n " shape="box"] + + + 44 -> 43 ; +43 [label="43: Return Stmt \n n$0=*&a:int [line 59]\n *&return:int =(1 / n$0) [line 59]\n REMOVE_TEMPS(n$0); [line 59]\n NULLIFY(&a,false); [line 59]\n APPLY_ABSTRACTION; [line 59]\n " shape="box"] + + + 43 -> 42 ; +42 [label="42: Exit ref_div0_nested_assignment \n " color=yellow style=filled] + + +41 [label="41: Start ref_div0_nested_assignment\nFormals: \nLocals: r2:int & r1:int & b:int a:int \n DECLARE_LOCALS(&return,&r2,&r1,&b,&a); [line 53]\n NULLIFY(&b,false); [line 53]\n NULLIFY(&r1,false); [line 53]\n NULLIFY(&r2,false); [line 53]\n " color=yellow style=filled] + + + 41 -> 48 ; 40 [label="40: DeclStmt \n *&a:int =2 [line 47]\n " shape="box"] diff --git a/infer/tests/endtoend/cpp/ReferenceTypeTest.java b/infer/tests/endtoend/cpp/ReferenceTypeTest.java index de53fb318..ab333e4c6 100644 --- a/infer/tests/endtoend/cpp/ReferenceTypeTest.java +++ b/infer/tests/endtoend/cpp/ReferenceTypeTest.java @@ -53,7 +53,8 @@ public class ReferenceTypeTest { "ptr_div0_function_temp_var", "ref_div0", "ref_div0_function", - "ref_div0_function_temp_var" + "ref_div0_function_temp_var", + "ref_div0_nested_assignment" }; assertThat( "Results should contain the expected divide by zero", diff --git a/infer/tests/frontend/cpp/NestedOperatorsTest.java b/infer/tests/frontend/cpp/NestedOperatorsTest.java new file mode 100644 index 000000000..60767b7f8 --- /dev/null +++ b/infer/tests/frontend/cpp/NestedOperatorsTest.java @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2013 - present Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ + +package frontend.cpp; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.ClangFrontendUtils; + +/** + * Run C++ frontend on C code and compare it to .dot files produced by C frontend. + */ +public class NestedOperatorsTest { + + String nestedOperatorsBasePath = "infer/tests/codetoanalyze/c/frontend/nestedoperators/"; + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + void frontendTest(String fileRelative) throws InterruptedException, IOException, InferException { + ClangFrontendUtils.createAndCompareCppDotFiles(folder, nestedOperatorsBasePath + fileRelative); + } + + + @Test + public void whenCaptureRunEnumThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + frontendTest("nestedassignment.c"); + } + + @Test + public void whenCaptureRunUnionThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + frontendTest("union.c"); + } + + @Test + public void whenCaptureRunAssignInConditionThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + // .dot file for cpp is slightly different (but semantically equivalent). + // We need to have different dot file to compare against. + // assign_in_condition.cpp is in fact pointing to assign_in_condition.c + frontendTest("assign_in_condition.cpp"); + } + +} diff --git a/infer/tests/frontend/cpp/ReferenceTest.java b/infer/tests/frontend/cpp/ReferenceTest.java index c0fdb11ed..c9d4c0cee 100644 --- a/infer/tests/frontend/cpp/ReferenceTest.java +++ b/infer/tests/frontend/cpp/ReferenceTest.java @@ -58,6 +58,12 @@ public class ReferenceTest { throws InterruptedException, IOException, InferException { frontendTest("member_access_from_return.cpp"); } + + @Test + public void testNestedAssignmentDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("nested_assignment.cpp"); + } @Test public void testReferenceTypeE2EDotFilesMatch()