diff --git a/infer/lib/clang_wrappers/clang_wrapper b/infer/lib/clang_wrappers/clang_wrapper index eca5f3517..604619555 100755 --- a/infer/lib/clang_wrappers/clang_wrapper +++ b/infer/lib/clang_wrappers/clang_wrapper @@ -11,6 +11,8 @@ CLANG_COMPILER="${SCRIPT_DIR}/../../../facebook-clang-plugins/clang/bin/clang" if [ "${0%++}" != "$0" ]; then XX="++"; else XX=""; fi COMMAND=("${CLANG_COMPILER}${XX}") +COMMAND+=("-isystem") +COMMAND+=("${SCRIPT_DIR}/../../models/cpp/include") # Remove command line options not supported by the opensource compiler or the plugins. PREV="" diff --git a/infer/models/cpp/include/bits/shared_ptr.h b/infer/models/cpp/include/bits/shared_ptr.h new file mode 100644 index 000000000..1f7a01994 --- /dev/null +++ b/infer/models/cpp/include/bits/shared_ptr.h @@ -0,0 +1,15 @@ +/* + * 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. + */ + +// this file exists in gcc headers and we need to capture those includes +#include +#include_next +#include + +#include diff --git a/infer/models/cpp/include/infer_model/begin_name_override.inc b/infer/models/cpp/include/infer_model/begin_name_override.inc new file mode 100644 index 000000000..e7a8cffe1 --- /dev/null +++ b/infer/models/cpp/include/infer_model/begin_name_override.inc @@ -0,0 +1,3 @@ +#define make_shared std__make_shared +#define shared_ptr std__shared_ptr + diff --git a/infer/models/cpp/include/infer_model/common.h b/infer/models/cpp/include/infer_model/common.h new file mode 100644 index 000000000..0f58bdfaa --- /dev/null +++ b/infer/models/cpp/include/infer_model/common.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +#define static_assert(...) + +namespace infer_model { +// code compiled with infer headers is not supposed to be executed +struct AbortWhenRun { + AbortWhenRun() { + fprintf(stderr, + "!!! This program must not be run !!!\n" + "This code was compiled to be analyzed by Infer.\n" + "To run this program, recompile it without Infer.\n"); + std::abort(); + } +}; + +static AbortWhenRun a{}; +} diff --git a/infer/models/cpp/include/infer_model/end_name_override.inc b/infer/models/cpp/include/infer_model/end_name_override.inc new file mode 100644 index 000000000..53b074f1c --- /dev/null +++ b/infer/models/cpp/include/infer_model/end_name_override.inc @@ -0,0 +1,2 @@ +#undef make_shared +#undef shared_ptr diff --git a/infer/models/cpp/include/infer_model/portability.h b/infer/models/cpp/include/infer_model/portability.h new file mode 100644 index 000000000..aa3695fb0 --- /dev/null +++ b/infer/models/cpp/include/infer_model/portability.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#pragma once +// TODO set it in configure script instead +// This is hacky attempt to follow what folly does +// https://github.com/facebook/folly/blob/b1eb6819f3ffe6b645f39d505ca8ace3116b7873/folly/configure.ac#L232 +#if !defined(INFER_USE_LIBCPP) && defined(__APPLE__) +#define INFER_USE_LIBCPP 1 +#elif defined(FOLLY_USE_LIBCPP) +#define INFER_USE_LIBCPP 1 +#endif + +// Follow what folly does - gnu libstdc++ implementation is different from +// llvm's libc++. This way folly can forward declare decls from std library +// even when they are infer models +// https://github.com/facebook/folly/blob/b1eb6819f3ffe6b645f39d505ca8ace3116b7873/folly/Portability.h#L253-L255 +#if INFER_USE_LIBCPP +#include <__config> +#define INFER_NAMESPACE_STD_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD +#define INFER_NAMESPACE_STD_END _LIBCPP_END_NAMESPACE_STD +#else +#define INFER_NAMESPACE_STD_BEGIN namespace std { +#define INFER_NAMESPACE_STD_END } +#endif diff --git a/infer/models/cpp/include/infer_model/shared_ptr.h b/infer/models/cpp/include/infer_model/shared_ptr.h new file mode 100644 index 000000000..64c634088 --- /dev/null +++ b/infer/models/cpp/include/infer_model/shared_ptr.h @@ -0,0 +1,235 @@ +/* + * 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. + */ + +#pragma once +// ASSERT that __cplusplus >= 201103L + +#include + +INFER_NAMESPACE_STD_BEGIN + +// use inheritance to avoid compilation errors when using +// methods / non-member functions that are not modeled +// WARNING: sizeof(shared_ptr) = 24, not 16 - this may +// lead to compilation errors +template +class shared_ptr : public std__shared_ptr { + + public: + // Conversion constructors to allow implicit conversions. + // it's here purely to avoid compilation errors + template ::value>::type> + shared_ptr(const std__shared_ptr& r) {} + + template + shared_ptr(const std__shared_ptr& r, T* p) noexcept {} + + T* data; + + // constructors: + constexpr shared_ptr() noexcept : data(nullptr) {} + + shared_ptr(nullptr_t) : shared_ptr() {} + + // Extra template argument is used to create constructors/assignment overloads + // for Y types where it's possible to convert Y* to T*. + // typename = typename enable_if::value>::type + // thanks to that, clang will not create some functions that would cause + // compilation errors. More info: + // http://en.cppreference.com/w/cpp/language/sfinae + template ::value>::type> + explicit shared_ptr(Y* p) { + data = p; + } + + template ::value>::type> + shared_ptr(Y* p, D d) : shared_ptr(p) {} + + template ::value>::type> + shared_ptr(Y* p, D d, A a) : shared_ptr(p) {} + + template + shared_ptr(nullptr_t p, D d) : shared_ptr(p) {} + + template + shared_ptr(nullptr_t p, D d, A a) : shared_ptr(p) {} + + template + shared_ptr(const shared_ptr& r, T* p) noexcept : data(nullptr) { /* TODO */ + } + + shared_ptr(const shared_ptr& r) noexcept + : shared_ptr(r.data) { /* TODO - increase refcount*/ + } + + template ::value>::type> + shared_ptr(const shared_ptr& r) noexcept + : shared_ptr(r.data) { /* TODO - increase refcount*/ + } + + shared_ptr(shared_ptr&& r) noexcept : shared_ptr(r.data) { + r.data = nullptr; + } + + template ::value>::type> + shared_ptr(shared_ptr&& r) noexcept : shared_ptr(r.data) { + r.data = nullptr; + } + + template ::value>::type> + explicit shared_ptr(const weak_ptr& r) {} + + /* Because of implementation differences between libc++ and stdlibc++, don't + * define this constructor (it will be defined elsewhere in case of + * stdlibc++). Because it may be defined elsewhere, don't check whether Y* + * converts to T* - otherwise there might be compilation error (out-of-line + * definition). + * No definition here might cause compilation problems if project is + * using auto_ptrs with libc++ */ + template + shared_ptr(auto_ptr&& r); // {} + + template ::value>::type> + shared_ptr(unique_ptr&& r) : shared_ptr(r.release()) {} + + // destructor: + ~shared_ptr() { reset((T*)nullptr); } + + // assignment: + shared_ptr& operator=(const shared_ptr& r) noexcept { + // shared_ptr(r).swap(*this); + data = r.data; + return *this; + } + + template ::value>::type> + shared_ptr& operator=(const shared_ptr& r) noexcept { + // shared_ptr(r).swap(*this); + data = r.data; + return *this; + } + + shared_ptr& operator=(shared_ptr&& r) noexcept { + // shared_ptr(std::move(r)).swap(*this); + data = r.data; + return *this; + } + + template ::value>::type> + shared_ptr& operator=(shared_ptr&& r) { + // shared_ptr(std::move(r)).swap(*this); + data = r.data; + return *this; + } + + template ::value>::type> + shared_ptr& operator=(auto_ptr&& r) { /* ?? */ + } + template ::value>::type> + shared_ptr& operator=(unique_ptr&& r) { + // shared_ptr(std::move(r)).swap(*this); + return *this; + } + + // modifiers: + void swap(shared_ptr& r) noexcept { + T* tmp = r.data; + r.data = data; + data = tmp; + } + + void reset() noexcept { reset((T*)nullptr); } + + template ::value>::type> + void reset(Y* p) { + /* + if (unique()) { + delete data; + } + */ + data = p; + // TODO adjust refcounts + } + + template ::value>::type> + void reset(Y* p, D d) { + reset(p); + } + + template ::value>::type> + void reset(Y* p, D d, A a) { + reset(p); + } + + // observers: + T* get() const noexcept { return data; } + typename add_lvalue_reference::type operator*() const noexcept { + return *data; + } + T* operator->() const noexcept { return data; } + long use_count() const noexcept { return 2; /* FIXME */ } + bool unique() const noexcept { return use_count() == 1; /* FIXME */ } + explicit operator bool() const noexcept { return (bool)data; } + template + bool owner_before(shared_ptr const& b) const { + return true; /* FIXME - use non-det*/ + } + template + bool owner_before(weak_ptr const& b) const { + return true; /* FIXME - use non-det */ + } +}; + +template +struct hash> : public hash> {}; + +// shared_ptr casts - call original functions but change return type to +// std::shared_ptr +template +shared_ptr static_pointer_cast(shared_ptr const& r) noexcept { + return static_pointer_cast((const std__shared_ptr&)r); +} +template +shared_ptr dynamic_pointer_cast(shared_ptr const& r) noexcept { + return dynamic_pointer_cast((const std__shared_ptr&)r); +} +template +shared_ptr const_pointer_cast(shared_ptr const& r) noexcept { + return const_pointer_cast((const std__shared_ptr&)r); +} + +template +shared_ptr make_shared(Args&&... args) { + return shared_ptr(new T(std::forward(args)...)); +} + +INFER_NAMESPACE_STD_END diff --git a/infer/models/cpp/include/memory b/infer/models/cpp/include/memory new file mode 100644 index 000000000..1eccbca41 --- /dev/null +++ b/infer/models/cpp/include/memory @@ -0,0 +1,13 @@ + +#if __cplusplus >= 201103L + +#include +#include_next +#include + +#include + +#else // __cplusplus < 201103L +// don't model memory for pre-C++11 code +#include_next +#endif diff --git a/infer/src/clang/cLocation.ml b/infer/src/clang/cLocation.ml index b79803077..3d6231da0 100644 --- a/infer/src/clang/cLocation.ml +++ b/infer/src/clang/cLocation.ml @@ -99,11 +99,14 @@ let should_translate (loc_start, loc_end) = in let file_in_project = map_path_of file_in_project loc_end || map_path_of file_in_project loc_start in + let file_in_models file = Str.string_match (Str.regexp "^.*/infer/models/cpp/include/") file 0 in + let file_in_models = map_path_of file_in_models loc_end + || map_path_of file_in_models loc_start in equal_current_source !curr_file || map_file_of equal_current_source loc_end || map_file_of equal_current_source loc_start - || (!CFrontend_config.cxx_experimental && file_in_project - && not (!CFrontend_config.testing_mode)) + || file_in_models + || (!CFrontend_config.cxx_experimental && file_in_project) let should_translate_lib source_range = not !CFrontend_config.no_translate_libs diff --git a/infer/tests/codetoanalyze/cpp/errors/npe/boxed_ptr.cpp b/infer/tests/codetoanalyze/cpp/errors/npe/boxed_ptr.cpp new file mode 100644 index 000000000..77bcedba9 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/npe/boxed_ptr.cpp @@ -0,0 +1,64 @@ +/* + * 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 { + int field; + int* getPtr() { return &field; } + int* getNull() { return nullptr; } +}; + +struct SmartPtr { + SmartPtr() : data(nullptr) {} + X* data; + X* get() { return data; } +}; + +void smart_ptr_null_field_deref() { + SmartPtr p; + int f = p.get()->field; +} + +void smart_ptr_null_method_deref() { + SmartPtr p; + int* f = p.get()->getPtr(); +} + +void smart_ptr_null_method_deref2() { + SmartPtr p; + int* f = p.get()->getNull(); +} + +void smart_ptr_ok_field_deref() { + SmartPtr p; + X x; + p.data = &x; + int f = p.get()->field; +} + +void smart_ptr_ok_method_deref() { + SmartPtr p; + X x; + p.data = &x; + int* f = p.get()->getNull(); + int* g = f; +} + +void smart_ptr_result_method_null_deref() { + SmartPtr p; + X x; + p.data = &x; + int f = *(p.get()->getNull()); +} + +void smart_ptr_result_method_ok_deref() { + SmartPtr p; + X x; + p.data = &x; + int f = *(p.get()->getPtr()); +} diff --git a/infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_constructors.cpp b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_constructors.cpp new file mode 100644 index 000000000..a7cdcb189 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_constructors.cpp @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#include + +struct Base { + int* f1; +}; + +struct Derived : public Base { + int* f2; +}; + +std::shared_ptr getFromBase1(Base* b) { return std::shared_ptr(b); } + +std::shared_ptr getFromBase2(Base* b) { + std::shared_ptr result; + result = std::shared_ptr(b); // assignment operator + return result; +} + +std::shared_ptr getFromDerived1(Derived* d) { + return std::shared_ptr(d); +} + +std::shared_ptr getFromDerived2(Derived* d) { + std::shared_ptr sd(d); + return std::shared_ptr(sd); +} + +std::shared_ptr getFromDerived3(Derived* d) { + std::shared_ptr sd(d); + std::shared_ptr result; + result = sd; // assignment operator + return result; +} + +void get_from_base1_nullptr_deref() { Base b = *(getFromBase1(nullptr)); } + +void get_from_base2_nullptr_deref() { Base b = *(getFromBase2(nullptr)); } + +void get_from_derived1_nullptr_deref() { Base b = *(getFromDerived1(nullptr)); } + +void get_from_derived2_nullptr_deref() { Base b = *(getFromDerived2(nullptr)); } + +void get_from_derived3_nullptr_deref() { Base b = *(getFromDerived3(nullptr)); } + +void get_from_base1_null_f1_deref() { + Base b; + int v; + b.f1 = &v; + std::shared_ptr p = getFromBase1(&b); + b.f1 = nullptr; + int r = *(p->f1); +} + +void get_from_base2_null_f1_deref() { + Base b; + int v; + b.f1 = &v; + std::shared_ptr p = getFromBase2(&b); + b.f1 = nullptr; + int r = *(p->f1); +} + +void get_from_derived1_null_f1_deref() { + Derived b; + int v; + b.f1 = &v; + std::shared_ptr p = getFromDerived1(&b); + b.f1 = nullptr; + int r = *(p->f1); +} + +void get_from_derived2_null_f1_deref() { + Derived b; + int v; + b.f1 = &v; + std::shared_ptr p = getFromDerived2(&b); + b.f1 = nullptr; + int r = *(p->f1); +} + +void get_from_derived3_null_f1_deref() { + Derived b; + int v; + b.f1 = &v; + std::shared_ptr p = getFromDerived3(&b); + b.f1 = nullptr; + int r = *(p->f1); +} diff --git a/infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_deref.cpp b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_deref.cpp new file mode 100644 index 000000000..f832a783f --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_deref.cpp @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#include + +struct X { + int field; + int get() { return field; } + void set(int value) { field = value; } +}; + +int empty_ptr_access() { + std::shared_ptr x; + int* p = x.get(); // no dereference + if (p) { + return 1; + } + return 0; +} + +int empty_ptr_deref() { + std::shared_ptr x; + return *x; +} + +int nullptr_ptr_deref() { + std::shared_ptr x(nullptr); + return *x; +} + +int empty_ptr_field_deref() { + std::shared_ptr x; + return x.get()->field; +} + +int empty_ptr_field_deref2() { + std::shared_ptr x; + return x->field; +} + +int empty_ptr_method_deref() { + std::shared_ptr x; + return x->get(); +} + +int reset_ptr_null_deref() { + std::shared_ptr x(new int); + x.reset(); + return *x; +} + +int reset_ptr_null_deref2() { + std::shared_ptr x(new int); + x.reset(new int); + x.reset(); + return *x; +} + +int reset_ptr_ok_deref() { + std::shared_ptr x; + x.reset(new int); + return *x; +} + +int reset_ptr_ok_deref2() { + std::shared_ptr x; + x.reset(); + x.reset(new int); + return *x; +} + +int shared_ptr_copy_null_deref() { + std::shared_ptr p1; + std::shared_ptr p2 = p1; + return *p2; +} + +int shared_ptr_assign_null_deref() { + std::shared_ptr p1(new int); + std::shared_ptr p2; + p1 = p2; + return *p1; +} + +int shared_ptr_copy_ok_deref() { + std::shared_ptr p1(new int); + std::shared_ptr p2 = p1; + return *p2; +} + +int shared_ptr_assign_ok_deref() { + std::shared_ptr p1(new int); + std::shared_ptr p2; + p2 = p1; + p1.reset(); + return *p2; +} diff --git a/infer/tests/endtoend/cpp/BoxedPtrTest.java b/infer/tests/endtoend/cpp/BoxedPtrTest.java new file mode 100644 index 000000000..5042050c3 --- /dev/null +++ b/infer/tests/endtoend/cpp/BoxedPtrTest.java @@ -0,0 +1,67 @@ +/* + * 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.cpp; + +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 BoxedPtrTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/errors/npe/boxed_ptr.cpp"; + + 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.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsNullDerefFunctionsErrorIsFound() + throws InterruptedException, IOException, InferException { + String[] procedures = { + "smart_ptr_null_field_deref", + "smart_ptr_null_method_deref", + "smart_ptr_null_method_deref2", + "smart_ptr_result_method_null_deref", + }; + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + assertThat( + "Results should contain divide by 0 error", + inferResults, + containsExactly( + NULL_DEREFERENCE, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/endtoend/cpp/SharedPtrConstructorsTest.java b/infer/tests/endtoend/cpp/SharedPtrConstructorsTest.java new file mode 100644 index 000000000..b69b250e4 --- /dev/null +++ b/infer/tests/endtoend/cpp/SharedPtrConstructorsTest.java @@ -0,0 +1,73 @@ +/* + * 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.cpp; + +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 SharedPtrConstructorsTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_constructors.cpp"; + + 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.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsNullDerefFunctionsErrorIsFound() + throws InterruptedException, IOException, InferException { + String[] procedures = { + "get_from_base1_nullptr_deref", + "get_from_base2_nullptr_deref", + "get_from_derived1_nullptr_deref", + "get_from_derived2_nullptr_deref", + "get_from_derived3_nullptr_deref", + "get_from_base1_null_f1_deref", + "get_from_base2_null_f1_deref", + "get_from_derived1_null_f1_deref", + "get_from_derived2_null_f1_deref", + "get_from_derived3_null_f1_deref", + }; + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + assertThat( + "Results should contain divide by 0 error", + inferResults, + containsExactly( + NULL_DEREFERENCE, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/endtoend/cpp/SharedPtrDerefTest.java b/infer/tests/endtoend/cpp/SharedPtrDerefTest.java new file mode 100644 index 000000000..859c8793c --- /dev/null +++ b/infer/tests/endtoend/cpp/SharedPtrDerefTest.java @@ -0,0 +1,72 @@ +/* + * 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.cpp; + +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 SharedPtrDerefTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/errors/smart_ptr/shared_ptr_deref.cpp"; + + 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.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsNullDerefFunctionsErrorIsFound() + throws InterruptedException, IOException, InferException { + String[] procedures = { + "empty_ptr_deref", + "nullptr_ptr_deref", + "empty_ptr_field_deref", + "empty_ptr_field_deref2", + "empty_ptr_method_deref", + "reset_ptr_null_deref", + "reset_ptr_null_deref2", + "shared_ptr_copy_null_deref", + "shared_ptr_assign_null_deref", + }; + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + assertThat( + "Results should contain divide by 0 error", + inferResults, + containsExactly( + NULL_DEREFERENCE, + FILE, + procedures + ) + ); + } +} diff --git a/infer/tests/utils/InferRunner.java b/infer/tests/utils/InferRunner.java index 687968336..948bc60fb 100644 --- a/infer/tests/utils/InferRunner.java +++ b/infer/tests/utils/InferRunner.java @@ -52,6 +52,9 @@ public class InferRunner { "/dependencies/java/jackson/jackson-2.2.3.jar", }; + private static final String CXX_INCLUDE_DIR = + "/facebook-clang-plugins/clang/include/c++/v1/"; + private static final String IPHONESIMULATOR_ISYSROOT_SUFFIX = "/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk"; @@ -202,6 +205,21 @@ public class InferRunner { return stdParam; } + private static String getSystemHeaderFlag(Language lang) { + String headerFlag = ""; + switch (lang) { + case CPP: + String current_dir = System.getProperty("user.dir"); + headerFlag = new StringBuilder() + .append("-isystem") + .append(current_dir) + .append(CXX_INCLUDE_DIR) + .toString(); + break; + } + return headerFlag; + } + public static ImmutableList createClangCommand( String sourceFile, Language lang, @@ -226,6 +244,7 @@ public class InferRunner { .add("-x") .add(getClangLangOption(lang)) .add(getStdParam(lang)) + .add(getSystemHeaderFlag(lang)) .addAll(isysrootOption.build()) .addAll(arcOption.build()) .add("-c")