diff --git a/infer/src/IR/QualifiedCppName.ml b/infer/src/IR/QualifiedCppName.ml index 6c299f24a..1df962fa5 100644 --- a/infer/src/IR/QualifiedCppName.ml +++ b/infer/src/IR/QualifiedCppName.ml @@ -76,7 +76,14 @@ module Match = struct (* Fail if we detect templates in the fuzzy name. Template instantiations are not taken into account when fuzzy matching, and templates may produce wrong results when parsing qualified names. *) - if String.contains qual_name '<' then + let filtered_qual_name = + (* Filter out the '<' in operator< and operator<= *) + let operator_less_length = 14 in + if String.is_prefix qual_name ~prefix:"std::operator<" then + String.drop_prefix qual_name operator_less_length + else qual_name + in + if String.contains filtered_qual_name '<' then failwithf "Unexpected template in fuzzy qualified name %s." qual_name ; of_qual_string qual_name diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 5cf299e55..df05bbefb 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -257,6 +257,12 @@ let whitelisted_cpp_methods = ; "std::max" ; "std::min" ; "std::move" + ; "std::operator!=" + ; "std::operator<" + ; "std::operator<=" + ; "std::operator==" + ; "std::operator>" + ; "std::operator>=" ; "std::swap" ] let whitelisted_cpp_classes = @@ -265,6 +271,12 @@ let whitelisted_cpp_classes = ; "std::__wrap_iter" (* libc++ internal name of vector iterator *) ; "std::__get_pair" (* libc++ internal support class for std::get *) ; "std::__pair_get" (* libstdc++ internal support class for std::get *) + ; "std::equal_to" + ; "std::greater" + ; "std::greater_equal" + ; "std::less" + ; "std::less_equal" + ; "std::not_equal_to" ; "std::pair" ] type dynamic_dispatch_policy = [`None | `Interface | `Sound | `Lazy] diff --git a/infer/tests/codetoanalyze/cpp/errors/issues.exp b/infer/tests/codetoanalyze/cpp/errors/issues.exp index 37b3b7f82..07dff6119 100644 --- a/infer/tests/codetoanalyze/cpp/errors/issues.exp +++ b/infer/tests/codetoanalyze/cpp/errors/issues.exp @@ -16,6 +16,18 @@ codetoanalyze/cpp/errors/include_header/header2.h, header2::div0_templ, 1, codetoanalyze/cpp/errors/memory_leaks/array_leak.cpp, leak, 4, MEMORY_LEAK, [start of procedure leak()] codetoanalyze/cpp/errors/memory_leaks/object_leak.cpp, object_leak, 0, MEMORY_LEAK, [start of procedure object_leak(),start of procedure Rectangle,return from a call to Rectangle_Rectangle] codetoanalyze/cpp/errors/memory_leaks/raii_malloc.cpp, memory_leak, 0, MEMORY_LEAK, [start of procedure memory_leak()] +codetoanalyze/cpp/errors/models/cmp.cpp, operator_eq_bad, 4, NULL_DEREFERENCE, [start of procedure operator_eq_bad(),start of procedure operator==(),Condition is true,Condition is true,return from a call to operator==,Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, operator_ge_bad, 4, NULL_DEREFERENCE, [start of procedure operator_ge_bad(),start of procedure operator>=(),start of procedure operator<(),Condition is false,Condition is false,Condition is false,return from a call to operator<,Condition is false,return from a call to operator>=,Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, operator_gt_bad, 4, NULL_DEREFERENCE, [start of procedure operator_gt_bad(),start of procedure operator>(),start of procedure operator<(),Condition is false,Condition is false,Condition is true,return from a call to operator<,return from a call to operator>,Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, operator_le_bad, 4, NULL_DEREFERENCE, [start of procedure operator_le_bad(),start of procedure operator<=(),start of procedure operator<(),Condition is false,Condition is false,Condition is false,return from a call to operator<,Condition is false,return from a call to operator<=,Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, operator_lt_bad, 4, NULL_DEREFERENCE, [start of procedure operator_lt_bad(),start of procedure operator<(),Condition is false,Condition is false,Condition is true,return from a call to operator<,Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, operator_neq_bad, 4, NULL_DEREFERENCE, [start of procedure operator_neq_bad(),start of procedure operator!=(),start of procedure operator==(),Condition is true,Condition is true,return from a call to operator==,Condition is true,return from a call to operator!=,Condition is false,Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, std_equal_to_bad, 4, NULL_DEREFERENCE, [start of procedure std_equal_to_bad(),Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, std_greater_bad, 4, NULL_DEREFERENCE, [start of procedure std_greater_bad(),Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, std_greater_equal_bad, 4, NULL_DEREFERENCE, [start of procedure std_greater_equal_bad(),Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, std_less_bad, 4, NULL_DEREFERENCE, [start of procedure std_less_bad(),Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, std_less_equal_bad, 4, NULL_DEREFERENCE, [start of procedure std_less_equal_bad(),Condition is true] +codetoanalyze/cpp/errors/models/cmp.cpp, std_not_equal_to_bad, 4, NULL_DEREFERENCE, [start of procedure std_not_equal_to_bad(),Condition is false,Condition is true] codetoanalyze/cpp/errors/models/move.cpp, move::div0_moved_from, 3, DIVIDE_BY_ZERO, [start of procedure move::div0_moved_from(),start of procedure X,return from a call to move::X_X,start of procedure X,return from a call to move::X_X] codetoanalyze/cpp/errors/models/move.cpp, move::div0_moved_to, 3, DIVIDE_BY_ZERO, [start of procedure move::div0_moved_to(),start of procedure X,return from a call to move::X_X,start of procedure X,return from a call to move::X_X] codetoanalyze/cpp/errors/models/pair.cpp, pair::deref_pair_null0_bad, 3, NULL_DEREFERENCE, [start of procedure pair::deref_pair_null0_bad(),start of procedure pair::pairOfZeroNull(),return from a call to pair::pairOfZeroNull] diff --git a/infer/tests/codetoanalyze/cpp/errors/models/cmp.cpp b/infer/tests/codetoanalyze/cpp/errors/models/cmp.cpp new file mode 100644 index 000000000..ecb10a357 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/models/cmp.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2017 - 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 A { + int x, y; +}; + +bool operator==(const A& lhs, const A& rhs) { + return lhs.x == rhs.x && lhs.y == rhs.y; +} +bool operator!=(const A& lhs, const A& rhs) { return !(lhs == rhs); } +bool operator<(const A& lhs, const A& rhs) { + if (lhs.x < rhs.x) + return true; + else if (lhs.x > rhs.x) + return false; + else + return lhs.y < rhs.y; +} +bool operator>(const A& lhs, const A& rhs) { return rhs < lhs; } +bool operator>=(const A& lhs, const A& rhs) { return !(lhs < rhs); } +bool operator<=(const A& lhs, const A& rhs) { return !(rhs < lhs); } + +void operator_eq_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (!(a == b)) + *p = 42; +} + +void operator_eq_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (a == b) + *p = 42; +} + +void operator_neq_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (a != b) + *p = 42; +} + +void operator_neq_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (!(a != b)) + *p = 42; +} + +void std_equal_to_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (!(std::equal_to()(a, b))) + *p = 42; +} + +void std_not_equal_to_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (std::not_equal_to()(a, b)) + *p = 42; +} + +void std_equal_to_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (std::equal_to()(a, b)) + *p = 42; +} + +void std_not_equal_to_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y}; + if (!(std::not_equal_to()(a, b))) + *p = 42; +} + +void operator_lt_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (!(a < b)) + *p = 42; +} + +void operator_lt_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (a < b) + *p = 42; +} + +void operator_le_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (!(a <= b)) + *p = 42; +} + +void operator_le_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (a <= b) + *p = 42; +} + +void operator_gt_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (!(a > b)) + *p = 42; +} + +void operator_gt_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (a > b) + *p = 42; +} + +void operator_ge_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (!(a >= b)) + *p = 42; +} + +void operator_ge_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (a >= b) + *p = 42; +} + +void std_less_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (!(std::less()(a, b))) + *p = 42; +} + +void std_less_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (std::less()(a, b)) + *p = 42; +} + +void std_less_equal_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (!(std::less_equal()(a, b))) + *p = 42; +} + +void std_less_equal_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y + 1}; + if (std::less_equal()(a, b)) + *p = 42; +} + +void std_greater_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (!(std::greater()(a, b))) + *p = 42; +} + +void std_greater_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (std::greater()(a, b)) + *p = 42; +} + +void std_greater_equal_ok(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (!(std::greater_equal()(a, b))) + *p = 42; +} + +void std_greater_equal_bad(const A& a) { + int* p = nullptr; + A b{a.x, a.y - 1}; + if (std::greater_equal()(a, b)) + *p = 42; +}