Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -16,6 +16,7 @@ MoveConstantArgumentCheck.cpp MoveConstructorInitCheck.cpp NewDeleteOverloadsCheck.cpp + NoSideEffectsCheck.cpp NoexceptMoveConstructorCheck.cpp NonCopyableObjects.cpp SizeofContainerCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -24,6 +24,7 @@ #include "MoveConstantArgumentCheck.h" #include "MoveConstructorInitCheck.h" #include "NewDeleteOverloadsCheck.h" +#include "NoSideEffectsCheck.h" #include "NoexceptMoveConstructorCheck.h" #include "NonCopyableObjects.h" #include "SizeofContainerCheck.h" @@ -72,6 +73,8 @@ "misc-move-constructor-init"); CheckFactories.registerCheck( "misc-new-delete-overloads"); + CheckFactories.registerCheck( + "misc-no-side-effects"); CheckFactories.registerCheck( "misc-noexcept-move-constructor"); CheckFactories.registerCheck( Index: clang-tidy/misc/NoSideEffectsCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/NoSideEffectsCheck.h @@ -0,0 +1,37 @@ +//===--- NoSideEffectsCheck.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_MISC_NO_SIDE_EFFECTS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NO_SIDE_EFFECTS_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Finds calls of side-effect-free methods on STL containers that ignore +/// the return value. This could catch mistakes like using .empty() to +/// (try to) empty a container. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-no-side-effects.html +class NoSideEffectsCheck : public ClangTidyCheck { +public: + NoSideEffectsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NO_SIDE_EFFECTS_H Index: clang-tidy/misc/NoSideEffectsCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/NoSideEffectsCheck.cpp @@ -0,0 +1,120 @@ +//===--- NoSideEffectsCheck.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 "NoSideEffectsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +AST_MATCHER(CXXRecordDecl, isStdContainer) { + static const std::unordered_set whitelist{ + "std::array", + "std::deque", + "std::forward_list", + "std::list", + "std::map", + "std::multimap", + "std::multiset", + "std::priority_queue", + "std::queue", + "std::set", + "std::stack", + "std::unordered_map", + "std::unordered_multimap", + "std::unordered_multiset", + "std::unordered_set", + "std::vector", + "std::__vector_base" + }; + + std::string QualName; + llvm::raw_string_ostream OS{QualName}; + PrintingPolicy Policy = Node.getASTContext().getPrintingPolicy(); + Policy.SuppressUnwrittenScope = true; + Node.printQualifiedName(OS, Policy); + + return whitelist.find(OS.str()) != whitelist.end(); +} + +AST_MATCHER(CXXMethodDecl, isAPurelyInformationalMethod) { + static const std::unordered_set whitelist{ + "get_allocator", + "top", + "at", + "front", + "back", + "data", + "before_begin", + "cbefore_begin", + "begin", + "cbegin", + "end", + "cend", + "rbegin", + "crbegin", + "rend", + "crend", + "empty", + "size", + "max_size", + "capacity", + "count", + "find", + "equal_range", + "lower_bound", + "upper_bound", + "bucket_count", + "max_bucket_count", + "bucket_size", + "bucket", + "load_factor", + "max_load_factor", + "key_comp", + "value_comp", + "hash_function", + "key_eq" + }; + + // Some containers have a getter and a setter with the same name + // (e.g. max_load_factor). The setter will return void. + if (Node.getReturnType()->isVoidType()) { + return false; + } + + return whitelist.find(Node.getName()) != whitelist.end(); +} + + +void NoSideEffectsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(cxxMemberCallExpr(allOf( + anyOf(hasParent(compoundStmt()), hasParent(exprWithCleanups())), + callee(cxxMethodDecl(allOf( + ofClass(isStdContainer()), + isAPurelyInformationalMethod() + ))) + )).bind("call"), this); +} + +void NoSideEffectsCheck::check(const MatchFinder::MatchResult &Result) { + const auto &Call = Result.Nodes.getNodeAs("call"); + + diag(Call->getCallee()->getExprLoc(), "method '%0' has no side effects and its return value is not used") + << Call->getMethodDecl()->getName() + ; +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -56,6 +56,7 @@ misc-misplaced-widening-cast misc-move-constructor-init misc-new-delete-overloads + misc-no-side-effects misc-noexcept-move-constructor misc-non-copyable-objects misc-sizeof-container Index: docs/clang-tidy/checks/misc-no-side-effects.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-no-side-effects.rst @@ -0,0 +1,8 @@ +.. title:: clang-tidy - misc-no-side-effects + +misc-no-side-effects +==================== + +Warn when specific methods without side effects (e.g. ``v.empty()``) are called +and the return value ignored: the programmer might think that it does have side +effects (e.g. emptying the container). Index: test/clang-tidy/misc-no-side-effects.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-no-side-effects.cpp @@ -0,0 +1,18 @@ +// RUN: %check_clang_tidy %s misc-no-side-effects %t +#include + +class derivedVector: public std::vector {}; +class customVector { public: bool empty() { return false; } }; + +int main() { + + std::vector{}.empty(); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: method 'empty' has no side effects and its return value is not used [misc-no-side-effects] + derivedVector{}.empty(); + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: method 'empty' has no side effects and its return value is not used [misc-no-side-effects] + + // A future version of this checker might catch this one either by + // determining that the method has no side effects or by an annotation, but + // the current one should not. + customVector{}.empty(); +}