diff options
author | Jonas Toth <jonas.toth@gmail.com> | 2019-01-09 20:50:50 +0000 |
---|---|---|
committer | Jonas Toth <jonas.toth@gmail.com> | 2019-01-09 20:50:50 +0000 |
commit | ca8e20cdf76b5cc59159c315d35e6968f8879cb3 (patch) | |
tree | fb48861015f27e612d4d277edc2d5b637603c3cd /clang-tools-extra | |
parent | [OpenMP] Avoid remainder operations for loop index values on a collapsed loop... (diff) | |
download | llvm-project-ca8e20cdf76b5cc59159c315d35e6968f8879cb3.tar.gz llvm-project-ca8e20cdf76b5cc59159c315d35e6968f8879cb3.tar.bz2 llvm-project-ca8e20cdf76b5cc59159c315d35e6968f8879cb3.zip |
[clang-tidy] Adding a new modernize use nodiscard checker
Summary: Adds a checker to clang-tidy to warn when a non void const member function, taking only parameters passed by value or const reference could be marked as '[[nodiscard]]'
Patch by MyDeveloperDay.
Reviewers: alexfh, stephenkelly, curdeius, aaron.ballman, hokein, JonasToth
Reviewed By: curdeius, JonasToth
Subscribers: Eugene.Zelenko, lefticus, lebedev.ri, mgorny, xazax.hun, cfe-commits
Tags: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D55433
llvm-svn: 350760
Diffstat (limited to 'clang-tools-extra')
14 files changed, 653 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index cfacc952faa6..bb5c0aebb661 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -26,6 +26,7 @@ add_clang_library(clangTidyModernizeModule UseEmplaceCheck.cpp UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp + UseNodiscardCheck.cpp UseNoexceptCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 6bad6cbefa91..fcf535b76aeb 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -32,6 +32,7 @@ #include "UseEmplaceCheck.h" #include "UseEqualsDefaultCheck.h" #include "UseEqualsDeleteCheck.h" +#include "UseNodiscardCheck.h" #include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" @@ -82,6 +83,8 @@ public: CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default"); CheckFactories.registerCheck<UseEqualsDeleteCheck>( "modernize-use-equals-delete"); + CheckFactories.registerCheck<UseNodiscardCheck>( + "modernize-use-nodiscard"); CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept"); CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr"); CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override"); diff --git a/clang-tools-extra/clang-tidy/modernize/UseNodiscardCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseNodiscardCheck.cpp new file mode 100644 index 000000000000..69e1734075ce --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseNodiscardCheck.cpp @@ -0,0 +1,145 @@ +//===--- UseNodiscardCheck.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 "UseNodiscardCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static bool doesNoDiscardMacroExist(ASTContext &Context, + const llvm::StringRef &MacroId) { + // Don't check for the Macro existence if we are using an attribute + // either a C++17 standard attribute or pre C++17 syntax + if (MacroId.startswith("[[") || MacroId.startswith("__attribute__")) + return true; + + // Otherwise look up the macro name in the context to see if its defined. + return Context.Idents.get(MacroId).hasMacroDefinition(); +} + +namespace { +AST_MATCHER(CXXMethodDecl, isOverloadedOperator) { + // Don't put ``[[nodiscard]]`` in front of operators. + return Node.isOverloadedOperator(); +} +AST_MATCHER(CXXMethodDecl, isConversionOperator) { + // Don't put ``[[nodiscard]]`` in front of a conversion decl + // like operator bool(). + return isa<CXXConversionDecl>(Node); +} +AST_MATCHER(CXXMethodDecl, hasClassMutableFields) { + // Don't put ``[[nodiscard]]`` on functions on classes with + // mutable member variables. + return Node.getParent()->hasMutableFields(); +} +AST_MATCHER(ParmVarDecl, hasParameterPack) { + // Don't put ``[[nodiscard]]`` on functions with parameter pack arguments. + return Node.isParameterPack(); +} +AST_MATCHER(CXXMethodDecl, hasTemplateReturnType) { + // Don't put ``[[nodiscard]]`` in front of functions returning a template + // type. + return Node.getReturnType()->isTemplateTypeParmType() || + Node.getReturnType()->isInstantiationDependentType(); +} +AST_MATCHER(CXXMethodDecl, isDefinitionOrInline) { + // A function definition, with optional inline but not the declaration. + return !(Node.isThisDeclarationADefinition() && Node.isOutOfLine()); +} +AST_MATCHER(QualType, isInstantiationDependentType) { + return Node->isInstantiationDependentType(); +} +AST_MATCHER(QualType, isNonConstReferenceOrPointer) { + // If the function has any non-const-reference arguments + // bool foo(A &a) + // or pointer arguments + // bool foo(A*) + // then they may not care about the return value because of passing data + // via the arguments. + return (Node->isTemplateTypeParmType() || Node->isPointerType() || + (Node->isReferenceType() && + !Node.getNonReferenceType().isConstQualified()) || + Node->isInstantiationDependentType()); +} +} // namespace + +UseNodiscardCheck::UseNodiscardCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + NoDiscardMacro(Options.get("ReplacementString", "[[nodiscard]]")) {} + +void UseNodiscardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ReplacementString", NoDiscardMacro); +} + +void UseNodiscardCheck::registerMatchers(MatchFinder *Finder) { + // If we use ``[[nodiscard]]`` attribute, we require at least C++17. Use a + // macro or ``__attribute__`` with pre c++17 compilers by using + // ReplacementString option. + if ((NoDiscardMacro == "[[nodiscard]]" && !getLangOpts().CPlusPlus17) || + !getLangOpts().CPlusPlus) + return; + + auto functionObj = + cxxRecordDecl(hasAnyName("::std::function", "::boost::function")); + + // Find all non-void const methods which have not already been marked to + // warn on unused result. + Finder->addMatcher( + cxxMethodDecl( + allOf(isConst(), isDefinitionOrInline(), + unless(anyOf( + returns(voidType()), isNoReturn(), isOverloadedOperator(), + isVariadic(), hasTemplateReturnType(), + hasClassMutableFields(), isConversionOperator(), + hasAttr(clang::attr::WarnUnusedResult), + hasType(isInstantiationDependentType()), + hasAnyParameter(anyOf( + parmVarDecl(anyOf(hasType(functionObj), + hasType(references(functionObj)))), + hasType(isNonConstReferenceOrPointer()), + hasParameterPack())))))) + .bind("no_discard"), + this); +} + +void UseNodiscardCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("no_discard"); + // Don't make replacements if the location is invalid or in a macro. + SourceLocation Loc = MatchedDecl->getLocation(); + if (Loc.isInvalid() || Loc.isMacroID()) + return; + + SourceLocation retLoc = MatchedDecl->getInnerLocStart(); + + ASTContext &Context = *Result.Context; + + auto Diag = diag(retLoc, "function %0 should be marked " + NoDiscardMacro) + << MatchedDecl; + + // Check for the existence of the keyword being used as the ``[[nodiscard]]``. + if (!doesNoDiscardMacroExist(Context, NoDiscardMacro)) + return; + + // Possible false positives include: + // 1. A const member function which returns a variable which is ignored + // but performs some external I/O operation and the return value could be + // ignored. + Diag << FixItHint::CreateInsertion(retLoc, NoDiscardMacro + " "); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/modernize/UseNodiscardCheck.h b/clang-tools-extra/clang-tidy/modernize/UseNodiscardCheck.h new file mode 100644 index 000000000000..00563e7f0bd2 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseNodiscardCheck.h @@ -0,0 +1,50 @@ +//===--- UseNodiscardCheck.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_MODERNIZE_USENODISCARDCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USENODISCARDCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// \brief Add ``[[nodiscard]]`` to non-void const-member functions with no +/// arguments or pass-by-value or pass by const-reference arguments. +/// \code +/// bool empty() const; +/// bool empty(const Bar &) const; +/// bool empty(int bar) const; +/// \endcode +/// Is converted to: +/// \code +/// [[nodiscard]] bool empty() const; +/// [[nodiscard]] bool empty(const Bar &) const; +/// [[nodiscard]] bool empty(int bar) const; +/// \endcode +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-nodiscard.html +class UseNodiscardCheck : public ClangTidyCheck { +public: + UseNodiscardCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const std::string NoDiscardMacro; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USENODISCARDCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 706e83d21e9d..f6cdc3efbdc0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -220,6 +220,12 @@ Improvements to clang-tidy Detects usage of the deprecated member types of ``std::ios_base`` and replaces those that have a non-deprecated equivalent. +- New :doc:`modernize-use-nodiscard + <clang-tidy/checks/modernize-use-nodiscard>` check. + + Adds ``[[nodiscard]]`` attributes (introduced in C++17) to member functions + to highlight at compile time which return values should not be ignored. + - New :doc:`readability-isolate-decl <clang-tidy/checks/readability-isolate-declaration>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 633ce30e2ba3..b4a60e76c8ef 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -207,6 +207,7 @@ Clang-Tidy Checks modernize-use-emplace modernize-use-equals-default modernize-use-equals-delete + modernize-use-nodiscard modernize-use-noexcept modernize-use-nullptr modernize-use-override diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize-use-nodiscard.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize-use-nodiscard.rst new file mode 100644 index 000000000000..88507e8e9abc --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize-use-nodiscard.rst @@ -0,0 +1,77 @@ +.. title:: clang-tidy - modernize-use-nodiscard + +modernize-use-nodiscard +======================= + +Adds ``[[nodiscard]]`` attributes (introduced in C++17) to member functions in +order to highlight at compile time which return values should not be ignored. + +Member functions need to satisfy the following conditions to be considered by +this check: + - no ``[[nodiscard]]``, ``[[noreturn]]``, ``__attribute__((warn_unused_result))``, ``[[clang::warn_unused_result]]`` nor ``[[gcc::warn_unused_result]]`` attribute, + - non-void return type, + - non-template return types, + - const member function, + - non-variadic functions, + - no non-const reference parameters, + - no pointer parameters, + - no template parameters, + - no template function parameters, + - not be a member of a class with mutable member variables, + - no Lambdas, + - no conversion functions. + +Such functions have no means of altering any state or passing values other than +via the return type. Unless the member functions are altering state via some +external call (e.g. I/O). + +Example +------- + +.. code-block:: c++ + + bool empty() const; + bool empty(int i) const; + +transforms to: + +.. code-block:: c++ + + [[nodiscard] bool empty() const; + [[nodiscard] bool empty(int i) const; + +Options +------- + +.. option:: ReplacementString + +Specifies a macro to use instead of ``[[nodiscard]]``. This is useful when +maintaining source code that needs to compile with a pre-C++17 compiler. + +Example +^^^^^^^ + +.. code-block:: c++ + + bool empty() const; + bool empty(int i) const; + +transforms to: + +.. code-block:: c++ + + NO_DISCARD bool empty() const; + NO_DISCARD bool empty(int i) const; + +if the :option:`ReplacementString` option is set to `NO_DISCARD`. + +.. note:: + +If the :option:`ReplacementString` is not a C++ attribute, but instead a macro, +then that macro must be defined in scope or the fix-it will not be applied. + +.. note:: + + For alternative ``__attribute__`` syntax options to mark functions as + ``[[nodiscard]]`` in non-c++17 source code. + See https://clang.llvm.org/docs/AttributeReference.html#nodiscard-warn-unused-result diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-clang-unused.cpp b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-clang-unused.cpp new file mode 100644 index 000000000000..0951e6118d4a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-clang-unused.cpp @@ -0,0 +1,25 @@ +// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: '[[clang::warn_unused_result]]'}]}" \ +// RUN: -- -std=c++11 + +class Foo +{ +public: + bool f1() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f1' should be marked {{\[\[clang::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[clang::warn_unused_result\]\]}} bool f1() const; + + bool f2(int) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f2' should be marked {{\[\[clang::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[clang::warn_unused_result\]\]}} bool f2(int) const; + + bool f3(const int &) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f3' should be marked {{\[\[clang::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[clang::warn_unused_result\]\]}} bool f3(const int &) const; + + bool f4(void) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f4' should be marked {{\[\[clang::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[clang::warn_unused_result\]\]}} bool f4(void) const; + +}; + diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-cxx11.cpp b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-cxx11.cpp new file mode 100644 index 000000000000..1a72bf4936a6 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-cxx11.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: '__attribute__((warn_unused_result))'}]}" \ +// RUN: -- -std=c++11 \ + +class Foo +{ +public: + bool f1() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f1' should be marked __attribute__((warn_unused_result)) [modernize-use-nodiscard] + // CHECK-FIXES: __attribute__((warn_unused_result)) bool f1() const; + + bool f2(int) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f2' should be marked __attribute__((warn_unused_result)) [modernize-use-nodiscard] + // CHECK-FIXES: __attribute__((warn_unused_result)) bool f2(int) const; + + bool f3(const int &) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f3' should be marked __attribute__((warn_unused_result)) [modernize-use-nodiscard] + // CHECK-FIXES: __attribute__((warn_unused_result)) bool f3(const int &) const; + + bool f4(void) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f4' should be marked __attribute__((warn_unused_result)) [modernize-use-nodiscard] + // CHECK-FIXES: __attribute__((warn_unused_result)) bool f4(void) const; +}; + diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-gcc-unused.cpp b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-gcc-unused.cpp new file mode 100644 index 000000000000..54b808ed726b --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-gcc-unused.cpp @@ -0,0 +1,25 @@ +// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: '[[gcc::warn_unused_result]]'}]}" \ +// RUN: -- -std=c++11 + +class Foo +{ +public: + bool f1() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f1' should be marked {{\[\[gcc::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[gcc::warn_unused_result\]\]}} bool f1() const; + + bool f2(int) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f2' should be marked {{\[\[gcc::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[gcc::warn_unused_result\]\]}} bool f2(int) const; + + bool f3(const int &) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f3' should be marked {{\[\[gcc::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[gcc::warn_unused_result\]\]}} bool f3(const int &) const; + + bool f4(void) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f4' should be marked {{\[\[gcc::warn_unused_result\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[gcc::warn_unused_result\]\]}} bool f4(void) const; + +}; + diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-no-macro-inscope-cxx11.cpp b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-no-macro-inscope-cxx11.cpp new file mode 100644 index 000000000000..0564a1336031 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-no-macro-inscope-cxx11.cpp @@ -0,0 +1,13 @@ +// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: 'CUSTOM_NO_DISCARD'}]}" -- -std=c++11 + +// As if the macro was not defined. +// #define CUSTOM_NO_DISCARD __attribute_((warn_unused_result)) + +class Foo +{ +public: + bool f1() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f1' should be marked CUSTOM_NO_DISCARD [modernize-use-nodiscard] +}; + diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-no-macro.cpp b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-no-macro.cpp new file mode 100644 index 000000000000..7898b6ede343 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard-no-macro.cpp @@ -0,0 +1,22 @@ +// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- -- -std=c++17 + +class Foo +{ +public: + bool f1() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f1' should be marked {{\[\[nodiscard\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[nodiscard\]\]}} bool f1() const; + + bool f2(int) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f2' should be marked {{\[\[nodiscard\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[nodiscard\]\]}} bool f2(int) const; + + bool f3(const int &) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f3' should be marked {{\[\[nodiscard\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[nodiscard\]\]}} bool f3(const int &) const; + + bool f4(void) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f4' should be marked {{\[\[nodiscard\]\]}} [modernize-use-nodiscard] + // CHECK-FIXES: {{\[\[nodiscard\]\]}} bool f4(void) const; + +}; diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard.cpp b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard.cpp new file mode 100644 index 000000000000..f4408320154a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard.cpp @@ -0,0 +1,256 @@ +// RUN: %check_clang_tidy %s modernize-use-nodiscard %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-nodiscard.ReplacementString, value: 'NO_DISCARD'}]}" \ +// RUN: -- -std=c++17 + +#include <functional> + +namespace boost { +template <class> +class function; +} + +#include "modernize-use-nodiscard.h" + +#define BOOLEAN_FUNC bool f23() const + +typedef unsigned my_unsigned; +typedef unsigned &my_unsigned_reference; +typedef const unsigned &my_unsigned_const_reference; + +class Foo { +public: + using size_type = unsigned; + + bool f1() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f1' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f1() const; + + bool f2(int) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f2' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f2(int) const; + + bool f3(const int &) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f3' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f3(const int &) const; + + bool f4(void) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f4' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f4(void) const; + + // negative tests + + void f5() const; + + bool f6(); + + bool f7(int &); + + bool f8(int &) const; + + bool f9(int *) const; + + bool f10(const int &, int &) const; + + NO_DISCARD bool f12() const; + + MUST_USE_RESULT bool f13() const; + + [[nodiscard]] bool f11() const; + + [[clang::warn_unused_result]] bool f11a() const; + + [[gnu::warn_unused_result]] bool f11b() const; + + bool _f20() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function '_f20' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool _f20() const; + + NO_RETURN bool f21() const; + + ~Foo(); + + bool operator+=(int) const; + + // extra keywords (virtual,inline,const) on return type + + virtual bool f14() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f14' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD virtual bool f14() const; + + const bool f15() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f15' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD const bool f15() const; + + inline const bool f16() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f16' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD inline const bool f16() const; + + inline const std::string &f45() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f45' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD inline const std::string &f45() const; + + inline virtual const bool f17() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f17' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD inline virtual const bool f17() const; + + // inline with body + bool f18() const + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f18' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f18() const + { + return true; + } + + bool f19() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f19' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f19() const; + + BOOLEAN_FUNC; + + bool f24(size_type) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f24' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f24(size_type) const; + + bool f28(my_unsigned) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f28' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f28(my_unsigned) const; + + bool f29(my_unsigned_reference) const; + + bool f30(my_unsigned_const_reference) const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f30' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool f30(my_unsigned_const_reference) const; + + template <class F> + F f37(F a, F b) const; + + template <class F> + bool f38(F a) const; + + bool f39(const std::function<bool()> &predicate) const; + + bool f39a(std::function<bool()> predicate) const; + + bool f39b(const std::function<bool()> predicate) const; + + bool f45(const boost::function<bool()> &predicate) const; + + bool f45a(boost::function<bool()> predicate) const; + + bool f45b(const boost::function<bool()> predicate) const; + + // Do not add ``[[nodiscard]]`` to parameter packs. + template <class... Args> + bool ParameterPack(Args... args) const; + + template <typename... Targs> + bool ParameterPack2(Targs... Fargs) const; + + // Do not add ``[[nodiscard]]`` to variadic functions. + bool VariadicFunctionTest(const int &, ...) const; + + // Do not add ``[[nodiscard]]`` to non constant static functions. + static bool not_empty(); + + // Do not add ``[[nodiscard]]`` to conversion functions. + // explicit operator bool() const { return true; } +}; + +// Do not add ``[[nodiscard]]`` to Lambda. +const auto nonConstReferenceType = [] { + return true; +}; + +auto lambda1 = [](int a, int b) { return a < b; }; +auto lambda1a = [](int a) { return a; }; +auto lambda1b = []() { return true;}; + +auto get_functor = [](bool check) { + return [&](const std::string& sr)->std::string { + if(check){ + return "some string"; + } + return "another string"; + }; +}; + +// Do not add ``[[nodiscard]]`` to function definition. +bool Foo::f19() const { + return true; +} + +template <class T> +class Bar { +public: + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + + // Do not add ``[[nodiscard]]`` to non explicit conversion functions. + operator bool() const { return true; } + + bool empty() const; + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'empty' should be marked NO_DISCARD [modernize-use-nodiscard] + // CHECK-FIXES: NO_DISCARD bool empty() const; + + // we cannot assume that the template parameter isn't a pointer + bool f25(value_type) const; + + bool f27(reference) const; + + typename T::value_type f35() const; + + T f34() const; + + bool f31(T) const; + + bool f33(T &) const; + + bool f26(const_reference) const; + + bool f32(const T &) const; +}; + +template <typename _Tp, int cn> +class Vec { +public: + Vec(_Tp v0, _Tp v1); //!< 2-element vector constructor + + Vec cross(const Vec &v) const; + + template <typename T2> + operator Vec<T2, cn>() const; +}; + +template <class T> +class Bar2 { +public: + typedef T value_type; + typedef value_type &reference; + typedef const value_type &const_reference; + + // we cannot assume that the template parameter isn't a pointer + bool f40(value_type) const; + + bool f41(reference) const; + + value_type f42() const; + + typename T::value_type f43() const; + + bool f44(const_reference) const; +}; + +template <class T> +bool Bar<T>::empty() const { + return true; +} + +// don't mark typical ``[[nodiscard]]`` candidates if the class +// has mutable member variables +class MutableExample { + mutable bool m_isempty; + +public: + bool empty() const; +}; diff --git a/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard.h b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard.h new file mode 100644 index 000000000000..56f3c2960238 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/modernize-use-nodiscard.h @@ -0,0 +1,5 @@ + +#define MUST_USE_RESULT __attribute__((warn_unused_result)) +#define NO_DISCARD [[nodiscard]] +#define NO_RETURN [[noreturn]] + |