Handle assignment operations correctly

Summary: public
C++ assignment operation result is lvalue, while in C it was rvalue.
This leads to different AST produced by clang for then same code!
Use language information from clang (`-x` flag) to distinguish these cases.

More specifically, let's look at following code:

  int r;
  int f = (r = 3);

  // type of (r = 3) expression:
  // C/objC -> int rvalue
  // C++/objC++ -> int lvalue

Existing code did extra dereference because it was rvalue in C and there was no cast afterwards
in C++ there will be extra LValueToRvalue cast when neccesary so we don't have to do extra dereference manually

Reference:
http://en.cppreference.com/w/c/language/value_category (search for 'assignment and compound assignment operators')

NOTE: AST output doesn't change when something is hidden behind `extern "C"`, so we should use global language information

Reviewed By: ddino

Differential Revision: D2549866

fb-gh-sync-id: b193b11
master
Andrzej Kotulski 9 years ago committed by facebook-github-bot-7
parent b86af1e5d1
commit 5a07f767bb

@ -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

@ -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

@ -495,6 +495,8 @@ struct
let instrs_after_assign, assign_ids, exp_to_parent =
if (is_binary_assign_op binary_operator_info)
(* 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 *)

@ -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 *)

@ -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 ;
}

@ -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;
}

@ -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 ;
}

@ -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;
}

@ -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"]

@ -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",

@ -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");
}
}

@ -59,6 +59,12 @@ public class ReferenceTest {
frontendTest("member_access_from_return.cpp");
}
@Test
public void testNestedAssignmentDotFilesMatch()
throws InterruptedException, IOException, InferException {
frontendTest("nested_assignment.cpp");
}
@Test
public void testReferenceTypeE2EDotFilesMatch()
throws InterruptedException, IOException, InferException {

Loading…
Cancel
Save