diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt @@ -4,6 +4,7 @@ ) add_clang_library(clangTidyPerformanceModule + DontUseEndlCheck.cpp FasterStringFindCheck.cpp ForRangeCopyCheck.cpp ImplicitConversionInLoopCheck.cpp diff --git a/clang-tools-extra/clang-tidy/performance/DontUseEndlCheck.h b/clang-tools-extra/clang-tidy/performance/DontUseEndlCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/performance/DontUseEndlCheck.h @@ -0,0 +1,35 @@ +//===--- DontUseEndlCheck.h - clang-tidy ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_DONTUSEENDLCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_DONTUSEENDLCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::performance { + +/// ClangTidyCheck to flag all uses of std::endl on iostreams. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/performance/dont-use-endl.html +class DontUseEndlCheck : public ClangTidyCheck { +public: + DontUseEndlCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::performance + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_DONTUSEENDLCHECK_H diff --git a/clang-tools-extra/clang-tidy/performance/DontUseEndlCheck.cpp b/clang-tools-extra/clang-tidy/performance/DontUseEndlCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/performance/DontUseEndlCheck.cpp @@ -0,0 +1,47 @@ +//===--- DontUseEndlCheck.cpp - clang-tidy --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DontUseEndlCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::performance { + +void DontUseEndlCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + findAll(cxxOperatorCallExpr( + hasOverloadedOperatorName("<<"), + callee(cxxMethodDecl(ofClass( + hasAnyName("::std::basic_ostream", "::std::basic_iostream")))), + hasDescendant( + expr(declRefExpr(to(functionDecl(hasName("::std::endl")))) + .bind("endl"))))), + this); +} + +void DontUseEndlCheck::check(const MatchFinder::MatchResult &Result) { + const auto *EndlCall = Result.Nodes.getNodeAs("endl"); + + if (!EndlCall) + return; + + auto Diag = diag(EndlCall->getBeginLoc(), + "do not use std::endl with iostreams; use '\\n' instead"); + + // Add a fix-it hint to replace std::endl with '\n' + Diag << FixItHint::CreateReplacement( + CharSourceRange::getCharRange(EndlCall->getSourceRange()), "'\\n'"); +} + +} // namespace clang::tidy::performance diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp --- a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "DontUseEndlCheck.h" #include "FasterStringFindCheck.h" #include "ForRangeCopyCheck.h" #include "ImplicitConversionInLoopCheck.h" @@ -31,6 +32,7 @@ class PerformanceModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck("performance-dont-use-endl"); CheckFactories.registerCheck( "performance-faster-string-find"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -142,6 +142,11 @@ Converts standard library type traits of the form ``traits<...>::type`` and ``traits<...>::value`` into ``traits_t<...>`` and ``traits_v<...>`` respectively. +- New :doc:`performance-dont-use-endl + ` check. + + Finds uses of ``std::endl`` on streams and replaces them with ``'\n'``. + - New :doc:`readability-avoid-unconditional-preprocessor-if ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -313,6 +313,7 @@ `objc-super-self `_, "Yes" `openmp-exception-escape `_, `openmp-use-default-none `_, + `performance-dont-use-endl `_, "Yes" `performance-faster-string-find `_, "Yes" `performance-for-range-copy `_, "Yes" `performance-implicit-conversion-in-loop `_, diff --git a/clang-tools-extra/docs/clang-tidy/checks/performance/dont-use-endl.rst b/clang-tools-extra/docs/clang-tidy/checks/performance/dont-use-endl.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/performance/dont-use-endl.rst @@ -0,0 +1,34 @@ +.. title:: clang-tidy - performance-dont-use-endl + +performance-dont-use-endl +========================= + +Checks for uses of ``std::endl`` on iostreams and suggests using the newline character ``'\n'`` instead. + +Rationale: +Using ``std::endl`` on iostreams can be less efficient than using the newline character ``'\n'`` because ``std::endl`` performs two operations: it writes a newline character to the output stream and then flushes the stream buffer. Writing a single newline character using ``'\n'`` does not trigger a flush, which can improve performance. In addition, flushing the stream buffer can cause additional overhead when working with streams that are buffered. + +Example: + +Consider the following code: + +.. code-block:: c++ + + #include + + int main() { + std::cout << "Hello" << std::endl; + } + +The ``std::endl`` on line 4 performs two operations: it writes a newline character to the ``std::cout`` stream and then flushes the stream buffer. This can be less efficient than using the newline character ``'\n'`` instead: + +.. code-block:: c++ + #include + + int main() { + std::cout << "Hello" << '\n'; + } + +This code writes a single newline character to the ``std::cout`` stream without flushing the stream buffer. + +If you do need to flush the stream buffer explicitly, you can just use ``std::flush``. diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/dont-use-endl.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/dont-use-endl.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/performance/dont-use-endl.cpp @@ -0,0 +1,71 @@ +// RUN: %check_clang_tidy %s performance-dont-use-endl %t + +namespace std +{ + template + class basic_ostream { + public: + template + basic_ostream& operator<<(T) { + return *this; + } + + basic_ostream& operator<<( basic_ostream& (*func) + (basic_ostream&)) + { + return func(*this); + } + }; + + template + class basic_iostream : public basic_ostream { + }; + + using ostream = basic_ostream; + using iostream = basic_iostream; + + iostream cout; + iostream cerr; + + template + basic_ostream& endl(basic_ostream& os) + { + return os; + } +} // namespace std + +void good() { + std::cout << "Hello" << '\n'; + std::cout << "World\n"; + + std::cerr << "Hello" << '\n'; + std::cerr << "World\n"; +} + +void bad() { + std::cout << "World" << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not use std::endl with iostreams; use '\n' instead + std::cerr << "World" << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not use std::endl with iostreams; use '\n' instead +} + +void bad_single_argument() { + std::cout << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use std::endl with iostreams; use '\n' instead + std::cerr << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use std::endl with iostreams; use '\n' instead +} + +void bad_multiple() { + std::cout << "Hello" << std::endl << "World" << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not use std::endl with iostreams; use '\n' instead + std::cerr << "Hello" << std::endl << "World" << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: do not use std::endl with iostreams; use '\n' instead +} + +void bad_user_stream() { + std::iostream my_stream; + + my_stream << "Hi" << std::endl; + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not use std::endl with iostreams; use '\n' instead +}