diff --git a/infer/src/clang/ast_expressions.ml b/infer/src/clang/ast_expressions.ml index 8d684efa0..2b344058f 100644 --- a/infer/src/clang/ast_expressions.ml +++ b/infer/src/clang/ast_expressions.ml @@ -366,7 +366,13 @@ let make_next_object_exp stmt_info item items = let message_call = make_message_expr create_id_type CFrontend_config.next_object items stmt_info false in let boi = { Clang_ast_t.boi_kind = `Assign } in - make_binary_stmt var_decl_ref message_call stmt_info (make_expr_info_with_objc_kind var_type `ObjCProperty) boi + let expr_info = make_expr_info_with_objc_kind var_type `ObjCProperty in + let assignment = make_binary_stmt var_decl_ref message_call stmt_info expr_info boi in + let boi' = { Clang_ast_t.boi_kind = `NE } in + let cast = create_implicit_cast_expr stmt_info [var_decl_ref] var_type `LValueToRValue in + let nil_exp = create_nil stmt_info in + let loop_cond = make_binary_stmt cast nil_exp stmt_info expr_info boi' in + assignment, loop_cond (* dispatch_once(v,block_def) is transformed as: *) (* void (^block_var)()=block_def; block_var() *) diff --git a/infer/src/clang/ast_expressions.mli b/infer/src/clang/ast_expressions.mli index 6638d2b55..d02aa9ae9 100644 --- a/infer/src/clang/ast_expressions.mli +++ b/infer/src/clang/ast_expressions.mli @@ -68,7 +68,8 @@ val make_cast_expr : type_ptr -> decl_info -> decl_ref_expr_info -> object_kind val make_self_field : string -> decl_info -> type_ptr -> named_decl_info -> stmt -val make_next_object_exp : stmt_info -> stmt -> Clang_ast_t.stmt -> Clang_ast_t.stmt +val make_next_object_exp : stmt_info -> stmt -> Clang_ast_t.stmt -> + Clang_ast_t.stmt * Clang_ast_t.stmt val create_nil : stmt_info -> stmt diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index f2e44ce85..cfb75d30c 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -1354,13 +1354,23 @@ struct let dowhile_kind = Loops.DoWhile (cond, body) in loop_instruction trans_state dowhile_kind stmt_info + (* Fast iteration for colection + for (type_it i in collection) { body } + is translate as + { + i = type_next_object(); + while(i != nil) { body; i = type_next_object();} + } + *) and objCForCollectionStmt_trans trans_state item items body stmt_info = let _ = instruction trans_state item in (* Here we do ast transformation, so we don't need the value of the translation of the *) (* variable item but we still need to add the variable to the locals *) - let bin_op = Ast_expressions.make_next_object_exp stmt_info item items in - let while_kind = Loops.While (None, bin_op, body) in - loop_instruction trans_state while_kind stmt_info + let assign_next_object, cond = Ast_expressions.make_next_object_exp stmt_info item items in + let body' = Clang_ast_t.CompoundStmt (stmt_info, [body; assign_next_object]) in + let null_stmt = Clang_ast_t.NullStmt (stmt_info,[]) in + let loop = Clang_ast_t.WhileStmt (stmt_info, [null_stmt; cond; body']) in + instruction trans_state (Clang_ast_t.CompoundStmt (stmt_info, [assign_next_object; loop])) (* Assumption: We expect precisely 2 stmt corresponding to the 2 operands. *) and compoundAssignOperator trans_state stmt_info binary_operator_info expr_info stmt_list = diff --git a/infer/tests/codetoanalyze/objc/errors/bad_ptr_comparisons/badpointer.m b/infer/tests/codetoanalyze/objc/errors/bad_ptr_comparisons/badpointer.m new file mode 100644 index 000000000..224feb62d --- /dev/null +++ b/infer/tests/codetoanalyze/objc/errors/bad_ptr_comparisons/badpointer.m @@ -0,0 +1,57 @@ +/* +* 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. +*/ + +#import + +void bad1() { + + NSNumber *n2; + int i; + + if (!n2) { + i = 0; + } else { + i = 1; + } +} + + +void bad2(NSArray *a, NSNumber *n) { + int i = 0; + for (; !n; ) { + i = n.intValue; + } +} + +void bad3(NSArray *a, NSNumber *n) { + int i = 0; + while (!n) { + i = n.intValue; + } +} + + +void good1(NSArray *a) { + int i = 0; + for (NSNumber *n in a) { + i = n.intValue; + } +} + +void good2 () { + + NSNumber *n2; + int i; + + if (n2 != nil) { + i = 1; + } else { + i = 0; + } +} diff --git a/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot b/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot index e69e7dc7d..b554ba3d0 100644 --- a/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot +++ b/infer/tests/codetoanalyze/objc/frontend/boxing/array.dot @@ -1,25 +1,33 @@ digraph iCFG { -10 [label="10: DeclStmt \n n$8=_fun_NSString_stringWithUTF8String:(\"Mercedes-Benz\":char *) [line 18]\n n$9=_fun_NSString_stringWithUTF8String:(\"BMW\":char *) [line 18]\n n$10=_fun_NSString_stringWithUTF8String:(\"Porsche\":char *) [line 18]\n n$11=_fun_NSString_stringWithUTF8String:(\"Opel\":char *) [line 19]\n n$12=_fun_NSString_stringWithUTF8String:(\"Volkswagen\":char *) [line 19]\n n$13=_fun_NSString_stringWithUTF8String:(\"Audi\":char *) [line 19]\n n$7=_fun_NSArray_arrayWithObjects:count:(n$8:struct objc_object *,n$9:struct objc_object *,n$10:struct objc_object *,n$11:struct objc_object *,n$12:struct objc_object *,n$13:struct objc_object *,0:struct objc_object *) [line 18]\n *&germanCars:class NSArray *=n$7 [line 18]\n REMOVE_TEMPS(n$7,n$8,n$9,n$10,n$11,n$12,n$13); [line 18]\n " shape="box"] +12 [label="12: DeclStmt \n n$10=_fun_NSString_stringWithUTF8String:(\"Mercedes-Benz\":char *) [line 18]\n n$11=_fun_NSString_stringWithUTF8String:(\"BMW\":char *) [line 18]\n n$12=_fun_NSString_stringWithUTF8String:(\"Porsche\":char *) [line 18]\n n$13=_fun_NSString_stringWithUTF8String:(\"Opel\":char *) [line 19]\n n$14=_fun_NSString_stringWithUTF8String:(\"Volkswagen\":char *) [line 19]\n n$15=_fun_NSString_stringWithUTF8String:(\"Audi\":char *) [line 19]\n n$9=_fun_NSArray_arrayWithObjects:count:(n$10:struct objc_object *,n$11:struct objc_object *,n$12:struct objc_object *,n$13:struct objc_object *,n$14:struct objc_object *,n$15:struct objc_object *,0:struct objc_object *) [line 18]\n *&germanCars:class NSArray *=n$9 [line 18]\n REMOVE_TEMPS(n$9,n$10,n$11,n$12,n$13,n$14,n$15); [line 18]\n " shape="box"] - 10 -> 9 ; -9 [label="9: BinaryOperatorStmt: Assign \n n$6=*&germanCars:class NSArray * [line 20]\n n$5=_fun_NSArray_objectAtIndexedSubscript:(n$6:class NSArray *,3:unsigned long ) virtual [line 20]\n *&s:class NSString *=n$5 [line 20]\n REMOVE_TEMPS(n$5,n$6); [line 20]\n NULLIFY(&s,false); [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="box"] + 12 -> 11 ; +11 [label="11: BinaryOperatorStmt: Assign \n n$8=*&germanCars:class NSArray * [line 20]\n n$7=_fun_NSArray_objectAtIndexedSubscript:(n$8:class NSArray *,3:unsigned long ) virtual [line 20]\n *&s:class NSString *=n$7 [line 20]\n REMOVE_TEMPS(n$7,n$8); [line 20]\n NULLIFY(&s,false); [line 20]\n " shape="box"] - 9 -> 4 ; -8 [label="8: Call _fun_NSLog \n n$3=_fun_NSString_stringWithUTF8String:(\"%@\":char *) [line 23]\n n$4=*&item:class NSString * [line 23]\n _fun_NSLog(n$3:struct objc_object *,n$4:class NSString *) [line 23]\n REMOVE_TEMPS(n$3,n$4); [line 23]\n NULLIFY(&item,false); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] + 11 -> 10 ; +10 [label="10: BinaryOperatorStmt: Assign \n n$6=*&germanCars:class NSArray * [line 22]\n n$5=_fun_NSArray_nextObject(n$6:class NSArray *) virtual [line 22]\n *&item:class NSString *=n$5 [line 22]\n REMOVE_TEMPS(n$5,n$6); [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] + + + 10 -> 4 ; +9 [label="9: Call _fun_NSLog \n n$3=_fun_NSString_stringWithUTF8String:(\"%@\":char *) [line 23]\n n$4=*&item:class NSString * [line 23]\n _fun_NSLog(n$3:struct objc_object *,n$4:class NSString *) [line 23]\n REMOVE_TEMPS(n$3,n$4); [line 23]\n NULLIFY(&item,false); [line 23]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: BinaryOperatorStmt: Assign \n n$2=*&germanCars:class NSArray * [line 22]\n n$1=_fun_NSArray_nextObject(n$2:class NSArray *) virtual [line 22]\n *&item:class NSString *=n$1 [line 22]\n REMOVE_TEMPS(n$1,n$2); [line 22]\n APPLY_ABSTRACTION; [line 22]\n " shape="box"] 8 -> 4 ; -7 [label="7: Prune (false branch) \n PRUNE((n$2 == 0), false); [line 22]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 22]\n " shape="invhouse"] +7 [label="7: Prune (false branch) \n PRUNE(((n$0 != 0) == 0), false); [line 22]\n REMOVE_TEMPS(n$0); [line 22]\n " shape="invhouse"] 7 -> 3 ; -6 [label="6: Prune (true branch) \n PRUNE((n$2 != 0), true); [line 22]\n REMOVE_TEMPS(n$0,n$1,n$2); [line 22]\n " shape="invhouse"] +6 [label="6: Prune (true branch) \n PRUNE(((n$0 != 0) != 0), true); [line 22]\n REMOVE_TEMPS(n$0); [line 22]\n " shape="invhouse"] - 6 -> 8 ; -5 [label="5: BinaryOperatorStmt: Assign \n n$1=*&germanCars:class NSArray * [line 22]\n n$0=_fun_NSArray_nextObject(n$1:class NSArray *) virtual [line 22]\n *&item:class NSString *=n$0 [line 22]\n n$2=*&item:class NSString * [line 22]\n " shape="box"] + 6 -> 9 ; +5 [label="5: BinaryOperatorStmt: NE \n n$0=*&item:class NSString * [line 22]\n " shape="box"] 5 -> 6 ; @@ -38,5 +46,5 @@ digraph iCFG { 1 [label="1: Start main\nFormals: \nLocals: item:class NSString * germanCars:class NSArray * s:class NSString * \n DECLARE_LOCALS(&return,&item,&germanCars,&s); [line 14]\n NULLIFY(&germanCars,false); [line 14]\n NULLIFY(&item,false); [line 14]\n NULLIFY(&s,false); [line 14]\n " color=yellow style=filled] - 1 -> 10 ; + 1 -> 12 ; } diff --git a/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot b/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot index e2ade226b..e42dbb11f 100644 --- a/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot +++ b/infer/tests/codetoanalyze/objc/frontend/fast_enumeration/Fast_enumeration.dot @@ -1,61 +1,69 @@ digraph iCFG { -19 [label="19: DeclStmt \n *&size:int =0 [line 26]\n " shape="box"] +21 [label="21: DeclStmt \n *&size:int =0 [line 26]\n " shape="box"] - 19 -> 18 ; -18 [label="18: DeclStmt \n *&item:class NSArray *=0 [line 27]\n NULLIFY(&item,false); [line 27]\n APPLY_ABSTRACTION; [line 27]\n " shape="box"] + 21 -> 20 ; +20 [label="20: DeclStmt \n *&item:class NSArray *=0 [line 27]\n NULLIFY(&item,false); [line 27]\n APPLY_ABSTRACTION; [line 27]\n " shape="box"] - 18 -> 13 ; -17 [label="17: ComppoundAssignStmt \n n$12=*&item:class NSArray * [line 29]\n n$11=_fun_NSArray_count(n$12:class NSArray *) virtual [line 29]\n n$13=*&size:int [line 29]\n *&size:int =(n$13 + n$11) [line 29]\n REMOVE_TEMPS(n$11,n$12,n$13); [line 29]\n NULLIFY(&item,false); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] + 20 -> 15 ; +19 [label="19: ComppoundAssignStmt \n n$14=*&item:class NSArray * [line 29]\n n$13=_fun_NSArray_count(n$14:class NSArray *) virtual [line 29]\n n$15=*&size:int [line 29]\n *&size:int =(n$15 + n$13) [line 29]\n REMOVE_TEMPS(n$13,n$14,n$15); [line 29]\n NULLIFY(&item,false); [line 29]\n APPLY_ABSTRACTION; [line 29]\n " shape="box"] - 17 -> 13 ; -16 [label="16: Prune (false branch) \n PRUNE((n$10 == 0), false); [line 28]\n REMOVE_TEMPS(n$8,n$9,n$10); [line 28]\n " shape="invhouse"] + 19 -> 15 ; +18 [label="18: Prune (false branch) \n PRUNE((n$12 == 0), false); [line 28]\n REMOVE_TEMPS(n$10,n$11,n$12); [line 28]\n " shape="invhouse"] - 16 -> 12 ; -15 [label="15: Prune (true branch) \n PRUNE((n$10 != 0), true); [line 28]\n REMOVE_TEMPS(n$8,n$9,n$10); [line 28]\n " shape="invhouse"] + 18 -> 14 ; +17 [label="17: Prune (true branch) \n PRUNE((n$12 != 0), true); [line 28]\n REMOVE_TEMPS(n$10,n$11,n$12); [line 28]\n " shape="invhouse"] - 15 -> 17 ; -14 [label="14: BinaryOperatorStmt: Assign \n n$9=*&items:class NSArray * [line 28]\n n$8=_fun_NSArray_nextObject(n$9:class NSArray *) virtual [line 28]\n *&item:class NSArray *=n$8 [line 28]\n n$10=*&item:class NSArray * [line 28]\n " shape="box"] + 17 -> 19 ; +16 [label="16: BinaryOperatorStmt: Assign \n n$11=*&items:class NSArray * [line 28]\n n$10=_fun_NSArray_nextObject(n$11:class NSArray *) virtual [line 28]\n *&item:class NSArray *=n$10 [line 28]\n n$12=*&item:class NSArray * [line 28]\n " shape="box"] - 14 -> 15 ; - 14 -> 16 ; -13 [label="13: + \n " ] + 16 -> 17 ; + 16 -> 18 ; +15 [label="15: + \n " ] - 13 -> 14 ; -12 [label="12: Return Stmt \n NULLIFY(&item,false); [line 31]\n NULLIFY(&items,false); [line 31]\n n$7=*&size:int [line 31]\n *&return:int =n$7 [line 31]\n REMOVE_TEMPS(n$7); [line 31]\n NULLIFY(&size,false); [line 31]\n APPLY_ABSTRACTION; [line 31]\n " shape="box"] + 15 -> 16 ; +14 [label="14: Return Stmt \n NULLIFY(&item,false); [line 31]\n NULLIFY(&items,false); [line 31]\n n$9=*&size:int [line 31]\n *&return:int =n$9 [line 31]\n REMOVE_TEMPS(n$9); [line 31]\n NULLIFY(&size,false); [line 31]\n APPLY_ABSTRACTION; [line 31]\n " shape="box"] - 12 -> 11 ; -11 [label="11: Exit A_while_loop: \n " color=yellow style=filled] + 14 -> 13 ; +13 [label="13: Exit A_while_loop: \n " color=yellow style=filled] -10 [label="10: Start A_while_loop:\nFormals: self:class A * items:class NSArray *\nLocals: item:class NSArray * size:int \n DECLARE_LOCALS(&return,&item,&size); [line 25]\n NULLIFY(&item,false); [line 25]\n NULLIFY(&self,false); [line 25]\n NULLIFY(&size,false); [line 25]\n " color=yellow style=filled] +12 [label="12: Start A_while_loop:\nFormals: self:class A * items:class NSArray *\nLocals: item:class NSArray * size:int \n DECLARE_LOCALS(&return,&item,&size); [line 25]\n NULLIFY(&item,false); [line 25]\n NULLIFY(&self,false); [line 25]\n NULLIFY(&size,false); [line 25]\n " color=yellow style=filled] - 10 -> 19 ; -9 [label="9: DeclStmt \n *&size:int =0 [line 18]\n APPLY_ABSTRACTION; [line 18]\n " shape="box"] + 12 -> 21 ; +11 [label="11: DeclStmt \n *&size:int =0 [line 18]\n " shape="box"] - 9 -> 4 ; -8 [label="8: ComppoundAssignStmt \n n$5=*&item:class NSArray * [line 20]\n n$4=_fun_NSArray_count(n$5:class NSArray *) virtual [line 20]\n n$6=*&size:int [line 20]\n *&size:int =(n$6 + n$4) [line 20]\n REMOVE_TEMPS(n$4,n$5,n$6); [line 20]\n NULLIFY(&item,false); [line 20]\n APPLY_ABSTRACTION; [line 20]\n " shape="box"] + 11 -> 10 ; +10 [label="10: BinaryOperatorStmt: Assign \n n$8=*&items:class NSArray * [line 19]\n n$7=_fun_NSArray_nextObject(n$8:class NSArray *) virtual [line 19]\n *&item:class NSArray *=n$7 [line 19]\n REMOVE_TEMPS(n$7,n$8); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] + + + 10 -> 4 ; +9 [label="9: ComppoundAssignStmt \n n$5=*&item:class NSArray * [line 20]\n n$4=_fun_NSArray_count(n$5:class NSArray *) virtual [line 20]\n n$6=*&size:int [line 20]\n *&size:int =(n$6 + n$4) [line 20]\n REMOVE_TEMPS(n$4,n$5,n$6); [line 20]\n NULLIFY(&item,false); [line 20]\n " shape="box"] + + + 9 -> 8 ; +8 [label="8: BinaryOperatorStmt: Assign \n n$3=*&items:class NSArray * [line 19]\n n$2=_fun_NSArray_nextObject(n$3:class NSArray *) virtual [line 19]\n *&item:class NSArray *=n$2 [line 19]\n REMOVE_TEMPS(n$2,n$3); [line 19]\n APPLY_ABSTRACTION; [line 19]\n " shape="box"] 8 -> 4 ; -7 [label="7: Prune (false branch) \n PRUNE((n$3 == 0), false); [line 19]\n REMOVE_TEMPS(n$1,n$2,n$3); [line 19]\n " shape="invhouse"] +7 [label="7: Prune (false branch) \n PRUNE(((n$1 != 0) == 0), false); [line 19]\n REMOVE_TEMPS(n$1); [line 19]\n " shape="invhouse"] 7 -> 3 ; -6 [label="6: Prune (true branch) \n PRUNE((n$3 != 0), true); [line 19]\n REMOVE_TEMPS(n$1,n$2,n$3); [line 19]\n " shape="invhouse"] +6 [label="6: Prune (true branch) \n PRUNE(((n$1 != 0) != 0), true); [line 19]\n REMOVE_TEMPS(n$1); [line 19]\n " shape="invhouse"] - 6 -> 8 ; -5 [label="5: BinaryOperatorStmt: Assign \n n$2=*&items:class NSArray * [line 19]\n n$1=_fun_NSArray_nextObject(n$2:class NSArray *) virtual [line 19]\n *&item:class NSArray *=n$1 [line 19]\n n$3=*&item:class NSArray * [line 19]\n " shape="box"] + 6 -> 9 ; +5 [label="5: BinaryOperatorStmt: NE \n n$1=*&item:class NSArray * [line 19]\n " shape="box"] 5 -> 6 ; @@ -74,5 +82,5 @@ digraph iCFG { 1 [label="1: Start A_fast_loop:\nFormals: self:class A * items:class NSArray *\nLocals: item:class NSArray * size:int \n DECLARE_LOCALS(&return,&item,&size); [line 17]\n NULLIFY(&item,false); [line 17]\n NULLIFY(&self,false); [line 17]\n NULLIFY(&size,false); [line 17]\n " color=yellow style=filled] - 1 -> 9 ; + 1 -> 11 ; } diff --git a/infer/tests/endtoend/objc/NSNumber2Test.java b/infer/tests/endtoend/objc/NSNumber2Test.java new file mode 100644 index 000000000..5dc9cabaf --- /dev/null +++ b/infer/tests/endtoend/objc/NSNumber2Test.java @@ -0,0 +1,67 @@ +/* +* 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. +*/ + +package endtoend.objc; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class NSNumber2Test { + + public static final String NSNUMBER_FILE = + "infer/tests/codetoanalyze/objc/errors/bad_ptr_comparisons/badpointer.m"; + + private static ImmutableList inferCmd; + + public static final String BAD_POINTER_COMPARISON = "BAD_POINTER_COMPARISON"; + + @ClassRule + public static DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createObjCInferCommand( + folder, + NSNUMBER_FILE); + } + + @Test + public void badNSNumberPointerComparisonShouldBeFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferObjC(inferCmd); + String[] methods = { + "bad1", + "bad2", + "bad3" + }; + assertThat( + "Results should contain " + BAD_POINTER_COMPARISON, + inferResults, + containsExactly( + BAD_POINTER_COMPARISON, + NSNUMBER_FILE, + methods + ) + ); + } + +}