add builtin for dictionary literals

Summary: public
Dictionary literals are normally implemented using
`+dictionaryWithObjects:forKeys:count:` but were modeled as
`+dictionaryWithObjectsAndKeys:`

In particular, `@{@"aaa": nil}` would trigger a sentinel error instead of an NPE.

This models dictionary literals as a special infer builtin that the backend
interprets so as to give NPEs when passed nil objects or keys.

Reviewed By: dulmarod

Differential Revision: D2550039

fb-gh-sync-id: 1a10656
master
Jules Villard 9 years ago committed by facebook-github-bot-1
parent 198c57aabc
commit 2af1687cd4

@ -2380,5 +2380,24 @@ module ModelBuiltins = struct
Builtin.register_procname Builtin.register_procname
(Procname.mangled_c_method "NSArray" "arrayWithObjects:" method_kind) (Procname.mangled_c_method "NSArray" "arrayWithObjects:" method_kind)
execute_NSArray_arrayWithObjects execute_NSArray_arrayWithObjects
let execute_objc_NSDictionary_alloc_no_fail cfg pdesc tenv symb_state ret_ids loc =
let nsdictionary_typ = Sil.Tvar (Sil.TN_csu (Sil.Class, Mangled.from_string "NSDictionary")) in
let nsdictionary_typ = Sil.expand_type tenv nsdictionary_typ in
execute_objc_alloc_no_fail cfg pdesc tenv symb_state ret_ids nsdictionary_typ loc
let execute___objc_dictionary_literal cfg pdesc instr tenv prop path ret_ids args callee_pname loc =
let n_formals = 1 in
let res' =
sym_exe_check_variadic_sentinel ~fails_on_nil: true cfg pdesc tenv prop path
n_formals args (0,1) callee_pname loc in
execute_objc_NSDictionary_alloc_no_fail cfg pdesc tenv res' ret_ids loc
let __objc_dictionary_literal =
let method_kind = Procname.mangled_of_objc_method_kind Procname.Class_objc_method in
let pname = Procname.mangled_c_method "NSDictionary" "__objc_dictionary_literal:" method_kind in
Builtin.register_procname pname execute___objc_dictionary_literal;
pname
end end
(* ============== END of ModelBuiltins ============== *) (* ============== END of ModelBuiltins ============== *)

@ -55,5 +55,6 @@ module ModelBuiltins : sig
val __set_autorelease_attribute : Procname.t val __set_autorelease_attribute : Procname.t
val __objc_release_autorelease_pool : Procname.t val __objc_release_autorelease_pool : Procname.t
val __objc_cast : Procname.t val __objc_cast : Procname.t
val __objc_dictionary_literal : Procname.t
val malloc_no_fail : Procname.t val malloc_no_fail : Procname.t
end end

@ -17,8 +17,6 @@ let array_with_objects_count_m = "arrayWithObjects:count:"
let object_at_indexed_subscript_m = "objectAtIndexedSubscript:" let object_at_indexed_subscript_m = "objectAtIndexedSubscript:"
let dict_with_objects_and_keys_m = "dictionaryWithObjectsAndKeys:"
let string_with_utf8_m = "stringWithUTF8String:" let string_with_utf8_m = "stringWithUTF8String:"
let nsstring_cl = "NSString" let nsstring_cl = "NSString"

@ -75,8 +75,6 @@ val array_with_objects_count_m : string
val object_at_indexed_subscript_m : string val object_at_indexed_subscript_m : string
val dict_with_objects_and_keys_m : string
val emtpy_name_category : string val emtpy_name_category : string
val objc_class : string val objc_class : string

@ -1697,8 +1697,10 @@ struct
and objCDictionaryLiteral_trans trans_state info stmt_info stmts = and objCDictionaryLiteral_trans trans_state info stmt_info stmts =
let typ = CTypes_decl.class_from_pointer_type trans_state.context.CContext.tenv info.Clang_ast_t.ei_type_ptr in let typ = CTypes_decl.class_from_pointer_type trans_state.context.CContext.tenv info.Clang_ast_t.ei_type_ptr in
let dictionary_literal_pname = SymExec.ModelBuiltins.__objc_dictionary_literal in
let dictionary_literal_s = Procname.c_get_method dictionary_literal_pname in
let obj_c_message_expr_info = let obj_c_message_expr_info =
Ast_expressions.make_obj_c_message_expr_info_class CFrontend_config.dict_with_objects_and_keys_m typ in Ast_expressions.make_obj_c_message_expr_info_class dictionary_literal_s typ in
let stmts = General_utils.swap_elements_list stmts in let stmts = General_utils.swap_elements_list stmts in
let stmts = stmts @ [Ast_expressions.create_nil stmt_info] in let stmts = stmts @ [Ast_expressions.create_nil stmt_info] in
let message_stmt = Clang_ast_t.ObjCMessageExpr (stmt_info, stmts, info, obj_c_message_expr_info) in let message_stmt = Clang_ast_t.ObjCMessageExpr (stmt_info, stmts, info, obj_c_message_expr_info) in

@ -0,0 +1,88 @@
/*
* Copyright (c) 2014 - 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 <Foundation/Foundation.h>
@interface A : NSObject {
}
@end
@implementation A
-(void) noProblem {
NSDictionary *foo = @{@"aaa":@"a value", @"bbb":@"b value"};
// check that dictionary literals create valid objects
NSArray *foofoo = @[foo];
}
-(void) nilInDictionaryLiteralKey0 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{str: @"a value"};
}
-(void) nilInDictionaryLiteralValue0 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{@"aaa": str};
}
-(void) nilInDictionaryLiteralKey1 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{str: @"a value", @"bbb":@"b value"};
}
-(void) nilInDictionaryLiteralValue1 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{@"aaa": str, @"bbb":@"b value"};
}
-(void) nilInDictionaryLiteralKey2 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{@"aaa":@"a value", str:@"b value", @"ccc":@"c value"};
}
-(void) nilInDictionaryLiteralValue2 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{@"aaa":@"a value", @"bbb":str, @"ccc":@"c value"};
}
-(void) nilInDictionaryLiteralKey3 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{@"aaa":@"a value", @"bbb":@"b value", str:@"c value"};
}
-(void) nilInDictionaryLiteralValue3 {
NSString *str = nil;
// nil argument in dictionary literal crashes
NSDictionary *foo = @{@"aaa":@"a value", @"bbb":@"b value", @"ccc":str};
}
@end
int main() {
A *a = [A alloc];
[a noProblem];
[a nilInDictionaryLiteralKey0];
return 0;
}

@ -1,5 +1,5 @@
digraph iCFG { digraph iCFG {
6 [label="6: Return Stmt \n n$1=_fun_NSString_stringWithUTF8String:(\"Matt\":char *) [line 23]\n n$2=_fun_NSString_stringWithUTF8String:(\"firstName\":char *) [line 23]\n n$3=_fun_NSString_stringWithUTF8String:(\"Galloway\":char *) [line 23]\n n$4=_fun_NSString_stringWithUTF8String:(\"lastName\":char *) [line 23]\n n$5=_fun_NSNumber_numberWithInt:(28:int ) [line 23]\n n$6=_fun_NSString_stringWithUTF8String:(\"age\":char *) [line 23]\n n$0=_fun_NSDictionary_dictionaryWithObjectsAndKeys:(n$1:struct objc_object *,n$2:struct objc_object *,n$3:struct objc_object *,n$4:struct objc_object *,n$5:struct objc_object *,n$6:struct objc_object *,0:struct objc_object *) [line 23]\n *&return:class NSDictionary *=n$0 [line 23]\n REMOVE_TEMPS(n$0,n$1,n$2,n$3,n$4,n$5,n$6); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] 6 [label="6: Return Stmt \n n$1=_fun_NSString_stringWithUTF8String:(\"Matt\":char *) [line 23]\n n$2=_fun_NSString_stringWithUTF8String:(\"firstName\":char *) [line 23]\n n$3=_fun_NSString_stringWithUTF8String:(\"Galloway\":char *) [line 23]\n n$4=_fun_NSString_stringWithUTF8String:(\"lastName\":char *) [line 23]\n n$5=_fun_NSNumber_numberWithInt:(28:int ) [line 23]\n n$6=_fun_NSString_stringWithUTF8String:(\"age\":char *) [line 23]\n n$0=_fun_NSDictionary___objc_dictionary_literal:(n$1:struct objc_object *,n$2:struct objc_object *,n$3:struct objc_object *,n$4:struct objc_object *,n$5:struct objc_object *,n$6:struct objc_object *,0:struct objc_object *) [line 23]\n *&return:class NSDictionary *=n$0 [line 23]\n REMOVE_TEMPS(n$0,n$1,n$2,n$3,n$4,n$5,n$6); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"]
6 -> 5 ; 6 -> 5 ;

@ -0,0 +1,79 @@
/*
* 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 endtoend.objc;
import static org.hamcrest.MatcherAssert.assertThat;
import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain;
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 NPEDictionaryLiteralTest {
public static final String PREMATURE_NIL_FILE =
"infer/tests/codetoanalyze/objc/errors/npe/nil_in_dictionary_literal.m";
private static ImmutableList<String> inferCmd;
public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE";
@ClassRule
public static DebuggableTemporaryFolder folderNPD = new DebuggableTemporaryFolder();
@BeforeClass
public static void runInfer() throws InterruptedException, IOException {
inferCmd = InferRunner.createObjCInferCommandWithMLBuckets(
folderNPD,
PREMATURE_NIL_FILE,
"cf",
true);
}
@Test
public void whenInferRunsOnTestThenNoNPENotFound()
throws InterruptedException, IOException, InferException {
InferResults inferResults = InferRunner.runInferObjC(inferCmd);
assertThat(
"NPE should not be found", inferResults,
doesNotContain(
NULL_DEREFERENCE,
PREMATURE_NIL_FILE,
"no_problem"));
String[] expectedNPEProcedures = {
"nilInDictionaryLiteralKey0",
"nilInDictionaryLiteralKey1",
"nilInDictionaryLiteralKey2",
"nilInDictionaryLiteralKey3",
"nilInDictionaryLiteralValue0",
"nilInDictionaryLiteralValue1",
"nilInDictionaryLiteralValue2",
"nilInDictionaryLiteralValue3"
};
assertThat(
"Only NPE should be found", inferResults,
containsExactly(
NULL_DEREFERENCE,
PREMATURE_NIL_FILE,
expectedNPEProcedures));
}
}
Loading…
Cancel
Save