Index: clang-tidy/obvious/CMakeLists.txt =================================================================== --- clang-tidy/obvious/CMakeLists.txt +++ clang-tidy/obvious/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_library(clangTidyObviousModule ObviousTidyModule.cpp + InvalidRangeCheck.cpp LINK_LIBS clangAST Index: clang-tidy/obvious/InvalidRangeCheck.h =================================================================== --- /dev/null +++ clang-tidy/obvious/InvalidRangeCheck.h @@ -0,0 +1,40 @@ +//===--- InvalidRangeCheck.h - clang-tidy------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBVIOUS_INVALID_RANGE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBVIOUS_INVALID_RANGE_H + +#include "../ClangTidy.h" +#include +#include + +namespace clang { +namespace tidy { +namespace obvious { + +/// Find invalid call to algorithm with obviously invalid range of iterators. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-invalid-range.html +class InvalidRangeCheck : public ClangTidyCheck { +public: + InvalidRangeCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + std::vector AlgorithmNames; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBVIOUS_INVALID_RANGE_H Index: clang-tidy/obvious/InvalidRangeCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/obvious/InvalidRangeCheck.cpp @@ -0,0 +1,96 @@ +//===--- InvalidRangeCheck.cpp - clang-tidy--------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InvalidRangeCheck.h" +#include "../utils/OptionsUtils.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace obvious { + +const std::string CXX_AlgorithmNames = + "std::for_each; std::find; std::find_if; std::find_end; " + "std::find_first_of; std::adjacent_find; std::count; std::count_if;" + "std::mismatch; std::equal; std::search; std::copy; " + "std::copy_backward; std::swap_ranges; std::transform; std::replace" + "std::replace_if; std::replace_copy; std::replace_copy_if; std::fill; " + "std::fill_n; std::generate; std::generate_n; std::remove; std::remove_if" + "std::remove_copy; std::remove_copy_if; std::unique; std::unique_copy;" + "std::reverse; std::reverse_copy; std::rotate; std::rotate_copy; " + "std::random_shuffle; std::partition; std::stable_partition; std::sort;" + "std::stable_sort; std::partial_sort; std::partial_sort_copy; " + "std::nth_element; std::lower_bound; std::upper_bound; std::equal_range;" + "std::binary_search; std::merge; std::inplace_merge; std::includes; " + "std::set_union; std::set_intersection; std::set_difference; " + "std::set_symetric_difference; std::push_heap; std::pop_heap" + "std::make_heap; std::sort_heap; std::min; std::max; std::min_element; " + "std::max_element; std::lexicographical_compare; std::next_permutation; " + "std::prev_permutation"; + +const auto CXX11_AlgorithmNames = + CXX_AlgorithmNames + "; " + "std::all_of; std::any_of; std::none_of; std::find_if_not; " + "std::is_permutation; std::copy_n; std::copy_if; std::move; " + "std::move_backward; std::shuffle; std::is_partitioned;" + "std::partition_copy; std::partition_point; std::is_sorted" + "std::is_sorted_until; std::is_heap; std::is_heap_until; std::minmax; " + "std::minmax_element"; + +InvalidRangeCheck::InvalidRangeCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AlgorithmNames(utils::options::parseStringList(Options.get( + "AlgorithmNames", getLangOpts().CPlusPlus11 ? CXX11_AlgorithmNames + : CXX_AlgorithmNames))) {} + +void InvalidRangeCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // Not so small vector. 80 because there are about that many algorithms. + const auto Names = + SmallVector(AlgorithmNames.begin(), AlgorithmNames.end()); + + auto CallsAlgorithm = hasDeclaration( + functionDecl(Names.size() > 0 ? hasAnyName(Names) : anything())); + + auto MemberCallWithName = [](StringRef MethodName, StringRef BindThisName) { + return cxxMemberCallExpr( + hasDeclaration(cxxMethodDecl(hasName(MethodName))), + onImplicitObjectArgument(declRefExpr().bind(BindThisName))); + }; + // FIXME: match for std::begin(arg) and std::end(arg2) + Finder->addMatcher( + callExpr(hasArgument(0, MemberCallWithName("begin", "first_arg")), + hasArgument(1, MemberCallWithName("end", "second_arg")), + CallsAlgorithm), + this); +} + +void InvalidRangeCheck::check(const MatchFinder::MatchResult &Result) { + + const auto *FirstArg = Result.Nodes.getNodeAs("first_arg"); + const auto *SecondArg = Result.Nodes.getNodeAs("second_arg"); + if (FirstArg->getNameInfo().getAsString() == + SecondArg->getNameInfo().getAsString()) + return; + + diag(FirstArg->getExprLoc(), + "call to algorithm with begin and end from different objects"); +} + +void InvalidRangeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AlgorithmNames", + utils::options::serializeStringList(AlgorithmNames)); +} + +} // namespace obvious +} // namespace tidy +} // namespace clang Index: clang-tidy/obvious/ObviousTidyModule.cpp =================================================================== --- clang-tidy/obvious/ObviousTidyModule.cpp +++ clang-tidy/obvious/ObviousTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "InvalidRangeCheck.h" using namespace clang::ast_matchers; namespace clang { @@ -20,7 +21,7 @@ class ObviousModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { - // Add obvious checks here. + CheckFactories.registerCheck("misc-invalid-range"); } }; Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -114,6 +114,15 @@ Flags MPI function calls with a buffer type and MPI data type mismatch. +- New obvious module with checks for obvious bugs that probably won't be found + in working code, but can be found while looking for an obvious bug in broken + code. +- New `obvious-invalid-range + `_ check + + Warns if algorithm is used with ``.begin()`` and ``.end()`` from different + variables. + - New `performance-inefficient-string-concatenation `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -65,6 +65,7 @@ misc-inaccurate-erase misc-incorrect-roundings misc-inefficient-algorithm + misc-invalid-range misc-macro-parentheses misc-macro-repeated-side-effects misc-misplaced-const Index: docs/clang-tidy/checks/misc-invalid-range.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-invalid-range.rst @@ -0,0 +1,17 @@ +.. title:: clang-tidy - misc-invalid-range + +misc-invalid-range +================== + +This check finds invalid calls to algorithms with obviously invalid range of +iterators like: + +.. code-block:: c++ + std::fill(v.begin(), v2.end(), it); + +It checks if first and second argument of call is call to ``begin()`` +and ``end()``, but on different objects (having different name). + +By default it looks for all algorithms from ````. This can be +changed by using ``AlgorithmNames`` option, where empty list means that any +function name will be accepted. \ No newline at end of file Index: test/clang-tidy/misc-invalid-range.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-invalid-range.cpp @@ -0,0 +1,47 @@ +// RUN: %check_clang_tidy %s misc-invalid-range %t + +namespace std { + +template +OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { + return result; +} + +template +OutputIterator move(InputIterator first, InputIterator last, OutputIterator result) { + return result; +} + +template +T move(T &&) { return T(); } + +template +void fill(InputIterator first, InputIterator last, const Val &v) { +} + +template +class vector { +public: + T *begin(); + T *end(); +}; +} + +void test_copy() { + std::vector v, v2; + std::copy(v.begin(), v2.end(), v2.begin()); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: call to algorithm with begin and end from different objects [misc-invalid-range] + + std::copy(v.begin(), v.end(), v2.begin()); +} + +void test_move() { + std::vector v; + auto &v2 = v; + std::move(v.begin(), v2.end(), v2.begin()); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: call to algorithm with begin and end from different objects [misc-invalid-range] + + std::move(v.begin(), v.end(), v2.begin()); + std::move(v.begin()); + test_copy(); +}