diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 5135138a9..5722250be 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -472,39 +472,6 @@ struct { empty_res_trans with exps = [(Exp.Const (Const.Cfun pname), typ)] } end - let var_deref_trans trans_state stmt_info decl_ref = - let open CContext in - let context = trans_state.context in - let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in - let ast_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in - let typ = match ast_typ with - | Typ.Tstruct _ when decl_ref.Clang_ast_t.dr_kind = `ParmVar -> - if General_utils.is_cpp_translation Config.clang_lang then - Typ.Tptr (ast_typ, Typ.Pk_reference) - else ast_typ - | _ -> ast_typ in - let procname = Cfg.Procdesc.get_proc_name context.procdesc in - let sil_loc = CLocation.get_sil_location stmt_info context in - let pvar = CVar_decl.sil_var_of_decl_ref context decl_ref procname in - CContext.add_block_static_var context procname (pvar, typ); - let e = Exp.Lvar pvar in - let exps = if Self.is_var_self pvar (CContext.is_objc_method context) then - let curr_class = CContext.get_curr_class context in - if (CTypes.is_class typ) then - raise (Self.SelfClassException (CContext.get_curr_class_name curr_class)) - else - let typ = CTypes.add_pointer_to_typ - (CTypes_decl.get_type_curr_class_objc context.tenv curr_class) in - [(e, typ)] - else [(e, typ)] in - Printing.log_out "\n\n PVAR ='%s'\n\n" (Pvar.to_string pvar); - let res_trans = { empty_res_trans with exps = exps } in - match typ with - | Typ.Tptr (_, Typ.Pk_reference) -> - (* dereference pvar due to the behavior of reference types in clang's AST *) - dereference_value_from_result sil_loc res_trans ~strip_pointer:true - | _ -> res_trans - let field_deref_trans trans_state stmt_info pre_trans_result decl_ref ~is_constructor_init = let open CContext in let context = trans_state.context in @@ -632,6 +599,57 @@ struct Cfg.Node.set_succs_exn trans_state.context.cfg root_node' res_trans.root_nodes []; { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } + and var_deref_trans trans_state stmt_info (decl_ref : Clang_ast_t.decl_ref) = + let context = trans_state.context in + let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in + let ast_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in + let typ = match ast_typ with + | Tstruct _ when decl_ref.dr_kind = `ParmVar -> + if General_utils.is_cpp_translation Config.clang_lang then + Typ.Tptr (ast_typ, Pk_reference) + else ast_typ + | _ -> ast_typ in + let procname = Cfg.Procdesc.get_proc_name context.procdesc in + let sil_loc = CLocation.get_sil_location stmt_info context in + let pvar = CVar_decl.sil_var_of_decl_ref context decl_ref procname in + CContext.add_block_static_var context procname (pvar, typ); + let var_exp = Exp.Lvar pvar in + (* handle references to global const *) + (* if there's a reference to a global const, add a fake instruction that *) + (* assigns the global again to its initialization value right before the *) + (* place where it is used *) + let trans_result' = + let is_global_const, init_expr = + match Ast_utils.get_decl decl_ref.dr_decl_pointer with + | Some VarDecl (_, _, qual_type, vdi) -> + (match ast_typ with + | Tstruct _ + when not (General_utils.is_cpp_translation Config.clang_lang) -> + (* Do not convert a global struct to a local because SIL + values do not include structs, they must all be heap-allocated *) + false, None + | _ -> vdi.vdi_is_global && qual_type.is_const, vdi.vdi_init_expr) + | _ -> false, None in + if is_global_const then + init_expr_trans trans_state (var_exp, typ) stmt_info init_expr + else empty_res_trans in + let exps = if Self.is_var_self pvar (CContext.is_objc_method context) then + let curr_class = CContext.get_curr_class context in + if (CTypes.is_class typ) then + raise (Self.SelfClassException (CContext.get_curr_class_name curr_class)) + else + let typ = CTypes.add_pointer_to_typ + (CTypes_decl.get_type_curr_class_objc context.tenv curr_class) in + [(var_exp, typ)] + else [(var_exp, typ)] in + Printing.log_out "\n\n PVAR ='%s'\n\n" (Pvar.to_string pvar); + let res_trans = { trans_result' with exps } in + match typ with + | Tptr (_, Pk_reference) -> + (* dereference pvar due to the behavior of reference types in clang's AST *) + dereference_value_from_result sil_loc res_trans ~strip_pointer:true + | _ -> res_trans + and decl_ref_trans trans_state pre_trans_result stmt_info decl_ref ~is_constructor_init = Printing.log_out " priority node free = '%s'\n@." (string_of_bool (PriorityNode.is_priority_free trans_state)); diff --git a/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp new file mode 100644 index 000000000..b31c73427 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 - 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. + */ + +struct X { + X() {} +}; +const X global; + +X test() { return global; } + +static const int v = 2; + +int test2() { + int local = v; + return v; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp.dot new file mode 100644 index 000000000..8e6fef636 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp.dot @@ -0,0 +1,43 @@ +/* @generated */ +digraph iCFG { +11 [label="11: DeclStmt \n *&#GB$v:int =2 [line 20]\n n$1=*&#GB$v:int [line 20]\n *&local:int =n$1 [line 20]\n " shape="box"] + + + 11 -> 10 ; +10 [label="10: Return Stmt \n *&#GB$v:int =2 [line 21]\n n$0=*&#GB$v:int [line 21]\n *&return:int =n$0 [line 21]\n " shape="box"] + + + 10 -> 9 ; +9 [label="9: Exit test2 \n " color=yellow style=filled] + + +8 [label="8: Start test2\nFormals: \nLocals: local:int \n DECLARE_LOCALS(&return,&local); [line 19]\n " color=yellow style=filled] + + + 8 -> 11 ; +7 [label="7: Return Stmt \n n$0=*&__return_param:class X * [line 15]\n _fun_X_X(&#GB$global:class X *) [line 13]\n _fun_X_X(n$0:class X *,&#GB$global:class X &) [line 15]\n " shape="box"] + + + 7 -> 6 ; +6 [label="6: Exit test \n " color=yellow style=filled] + + +5 [label="5: Start test\nFormals: __return_param:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 15]\n " color=yellow style=filled] + + + 5 -> 7 ; +4 [label="4: Exit X_X \n " color=yellow style=filled] + + +3 [label="3: Start X_X\nFormals: this:class X * __param_0:class X &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled] + + + 3 -> 4 ; +2 [label="2: Exit X_X \n " color=yellow style=filled] + + +1 [label="1: Start X_X\nFormals: this:class X *\nLocals: \n DECLARE_LOCALS(&return); [line 11]\n " color=yellow style=filled] + + + 1 -> 2 ; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp new file mode 100644 index 000000000..b1ee53730 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2016 - 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. + */ + +const int global = 1 ? 2 : 3; +int test() { return global; } diff --git a/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp.dot new file mode 100644 index 000000000..4e74c7edf --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp.dot @@ -0,0 +1,35 @@ +/* @generated */ +digraph iCFG { +8 [label="8: Return Stmt \n n$1=*&0$?%__sil_tmpSIL_temp_conditional___n$0:int [line 10]\n *&#GB$global:int =n$1 [line 11]\n n$2=*&#GB$global:int [line 11]\n *&return:int =n$2 [line 11]\n " shape="box"] + + + 8 -> 2 ; +7 [label="7: ConditinalStmt Branch \n *&0$?%__sil_tmpSIL_temp_conditional___n$0:int =3 [line 10]\n " shape="box"] + + + 7 -> 3 ; +6 [label="6: ConditinalStmt Branch \n *&0$?%__sil_tmpSIL_temp_conditional___n$0:int =2 [line 10]\n " shape="box"] + + + 6 -> 3 ; +5 [label="5: Prune (false branch) \n PRUNE((1 == 0), false); [line 10]\n " shape="invhouse"] + + + 5 -> 7 ; +4 [label="4: Prune (true branch) \n PRUNE((1 != 0), true); [line 10]\n " shape="invhouse"] + + + 4 -> 6 ; +3 [label="3: + \n " ] + + + 3 -> 8 ; +2 [label="2: Exit test \n " color=yellow style=filled] + + +1 [label="1: Start test\nFormals: \nLocals: 0$?%__sil_tmpSIL_temp_conditional___n$0:int \n DECLARE_LOCALS(&return,&0$?%__sil_tmpSIL_temp_conditional___n$0); [line 11]\n " color=yellow style=filled] + + + 1 -> 4 ; + 1 -> 5 ; +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/namespace/namespace.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/namespace/namespace.cpp.dot index 179b45367..55c343d94 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/namespace/namespace.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/namespace/namespace.cpp.dot @@ -32,7 +32,7 @@ digraph iCFG { 17 -> 16 ; -16 [label="16: BinaryOperatorStmt: Assign \n n$0=*&#GB$bar::pi:double [line 57]\n *&j:double =n$0 [line 57]\n " shape="box"] +16 [label="16: BinaryOperatorStmt: Assign \n *&#GB$bar::pi:double =3.141600 [line 57]\n n$0=*&#GB$bar::pi:double [line 57]\n *&j:double =n$0 [line 57]\n " shape="box"] 16 -> 15 ; @@ -54,7 +54,7 @@ digraph iCFG { 11 -> 12 ; -10 [label="10: Return Stmt \n n$0=*&#GB$bar::pi:double [line 30]\n *&return:double =(2 * n$0) [line 30]\n " shape="box"] +10 [label="10: Return Stmt \n *&#GB$bar::pi:double =3.141600 [line 30]\n n$0=*&#GB$bar::pi:double [line 30]\n *&return:double =(2 * n$0) [line 30]\n " shape="box"] 10 -> 9 ; diff --git a/infer/tests/codetoanalyze/objc/errors/global_const/global_const.m b/infer/tests/codetoanalyze/objc/errors/global_const/global_const.m new file mode 100644 index 000000000..a00be0397 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/errors/global_const/global_const.m @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 - 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 + +static NSString* const GlobalConst = @"A Global Const!"; +static NSString* Global = @"A Global!"; + +@interface SimpleRoot : NSObject + +@end + +@implementation SimpleRoot + +- (void)doSomethingOkWithDict:(NSMutableDictionary*)dict + andString:(NSString*)input { + NSString* str = [GlobalConst stringByAppendingString:input]; + [dict removeObjectForKey:str]; +} + +- (void)doSomethingBadWithDict:(NSMutableDictionary*)dict + andString:(NSString*)input { + NSString* str = [Global stringByAppendingString:input]; + [dict removeObjectForKey:str]; +} + +@end diff --git a/infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm b/infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm new file mode 100644 index 000000000..ff77ff16c --- /dev/null +++ b/infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016 - 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. + */ + +typedef struct { + float field1; + float field2; + float field3; +} Fields; + +static const Fields __someFields = { + .field1 = 1, .field2 = 2, .field3 = 3, +}; + +Fields fields() { return __someFields; } diff --git a/infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm.dot b/infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm.dot new file mode 100644 index 000000000..9e36e6df4 --- /dev/null +++ b/infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm.dot @@ -0,0 +1,33 @@ +/* @generated */ +digraph iCFG { +8 [label="8: Return Stmt \n n$0=*&__return_param:class Fields * [line 20]\n *&#GB$__someFields.field1:float =1 [line 16]\n *&#GB$__someFields.field2:float =2 [line 16]\n *&#GB$__someFields.field3:float =3 [line 16]\n _fun_Fields_(n$0:class Fields *,&#GB$__someFields:class Fields &) [line 20]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: Exit fields \n " color=yellow style=filled] + + +6 [label="6: Start fields\nFormals: __return_param:class Fields *\nLocals: \n DECLARE_LOCALS(&return); [line 20]\n " color=yellow style=filled] + + + 6 -> 8 ; +5 [label="5: Constructor Init \n n$6=*&this:class Fields * [line 10]\n n$7=*&__param_0:class Fields & [line 10]\n n$8=*n$7.field1:float [line 10]\n *n$6.field1:float =n$8 [line 10]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: Constructor Init \n n$3=*&this:class Fields * [line 10]\n n$4=*&__param_0:class Fields & [line 10]\n n$5=*n$4.field2:float [line 10]\n *n$3.field2:float =n$5 [line 10]\n " shape="box"] + + + 4 -> 3 ; +3 [label="3: Constructor Init \n n$0=*&this:class Fields * [line 10]\n n$1=*&__param_0:class Fields & [line 10]\n n$2=*n$1.field3:float [line 10]\n *n$0.field3:float =n$2 [line 10]\n " shape="box"] + + + 3 -> 2 ; +2 [label="2: Exit Fields_ \n " color=yellow style=filled] + + +1 [label="1: Start Fields_\nFormals: this:class Fields * __param_0:class Fields &\nLocals: \n DECLARE_LOCALS(&return); [line 10]\n " color=yellow style=filled] + + + 1 -> 5 ; +} diff --git a/infer/tests/endtoend/objc/infer/GlobalConstNPETest.java b/infer/tests/endtoend/objc/infer/GlobalConstNPETest.java new file mode 100644 index 000000000..24c199272 --- /dev/null +++ b/infer/tests/endtoend/objc/infer/GlobalConstNPETest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 - 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.infer; + +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 GlobalConstNPETest { + + public static final String FILE = + "infer/tests/codetoanalyze/objc/errors/global_const/global_const.m"; + + private static ImmutableList inferCmd; + + public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE"; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createObjCInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsOnMain1ThenDivideByZeroIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferObjC(inferCmd); + String[] procedures = {"doSomethingBadWithDict:andString:"}; + assertThat( + "Results should contain the expected " + NULL_DEREFERENCE, + inferResults, + containsExactly(NULL_DEREFERENCE, FILE, procedures) + ); + } +} diff --git a/infer/tests/frontend/cpp/GlobalConstTest.java b/infer/tests/frontend/cpp/GlobalConstTest.java new file mode 100644 index 000000000..f3b051eb8 --- /dev/null +++ b/infer/tests/frontend/cpp/GlobalConstTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 - 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.ClangFrontendUtils; +import utils.DebuggableTemporaryFolder; +import utils.InferException; + +public class GlobalConstTest { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunCommaThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = + "infer/tests/codetoanalyze/cpp/frontend/global_const/global_const1.cpp"; + ClangFrontendUtils.createAndCompareCppDotFiles(folder, src); + } +} diff --git a/infer/tests/frontend/cpp/GlobalConstTest2.java b/infer/tests/frontend/cpp/GlobalConstTest2.java new file mode 100644 index 000000000..9d92c9e21 --- /dev/null +++ b/infer/tests/frontend/cpp/GlobalConstTest2.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 - 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.ClangFrontendUtils; +import utils.DebuggableTemporaryFolder; +import utils.InferException; + +public class GlobalConstTest2 { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunCommaThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String src = + "infer/tests/codetoanalyze/cpp/frontend/global_const/global_const2.cpp"; + ClangFrontendUtils.createAndCompareCppDotFiles(folder, src); + } +} diff --git a/infer/tests/frontend/objcpp/GlobalConstTest.java b/infer/tests/frontend/objcpp/GlobalConstTest.java new file mode 100644 index 000000000..72b487c13 --- /dev/null +++ b/infer/tests/frontend/objcpp/GlobalConstTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 - 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.objcpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.DotFilesEqual.dotFileEqualTo; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferRunner; + +public class GlobalConstTest { + + @Rule + public DebuggableTemporaryFolder folder = new DebuggableTemporaryFolder(); + + @Test + public void whenCaptureRunSwitchStmtThenDotFilesAreTheSame() + throws InterruptedException, IOException, InferException { + String switch_src = + "infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm"; + + String switch_dotty = + "infer/tests/codetoanalyze/objcpp/frontend/global_const/global_const.mm.dot"; + + ImmutableList inferCmd = + InferRunner.createObjCPPInferCommand(folder, switch_src); + File newDotFile = InferRunner.runInferFrontend(inferCmd); + assertThat( + "In the capture of " + switch_src + + " the dotty files should be the same.", + newDotFile, dotFileEqualTo(switch_dotty)); + } +}