diff options
author | AMS21 <AMS21.github@gmail.com> | 2023-04-22 10:50:42 +0000 |
---|---|---|
committer | Piotr Zegar <me@piotrzegar.pl> | 2023-04-22 10:50:51 +0000 |
commit | cd893308b5d4d058b8ec6a2c4b00843d89e25552 (patch) | |
tree | 39559cbc1037e9b3573df575acf2efc1b8e0db9b /clang-tools-extra/clang-tidy | |
parent | a79a6ea4a831facc564a36911d79d4472ad93cec (diff) | |
download | llvm-cd893308b5d4d058b8ec6a2c4b00843d89e25552.tar.gz |
[clang-tidy] Add `performance-avoid-endl` check
This check flags uses of `std::endl` on streams and suggests using the newline character `'\n'` instead. `std::endl` performs two operations: it writes a newline character to the output stream and then flushes the stream buffer, which can be less efficient than writing a single newline character using `'\n'`.
This fixes llvm#35321
Reviewed By: PiotrZSL
Differential Revision: https://reviews.llvm.org/D148318
Diffstat (limited to 'clang-tools-extra/clang-tidy')
4 files changed, 127 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.cpp b/clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.cpp new file mode 100644 index 000000000000..8ecaa41754fb --- /dev/null +++ b/clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.cpp @@ -0,0 +1,84 @@ +//===--- AvoidEndlCheck.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 "AvoidEndlCheck.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" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::performance { + +void AvoidEndlCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr( + unless(isExpansionInSystemHeader()), + anyOf(cxxOperatorCallExpr( + hasOverloadedOperatorName("<<"), + hasRHS(declRefExpr(to(namedDecl(hasName("::std::endl")))) + .bind("expr"))), + callExpr(argumentCountIs(1), + callee(functionDecl(hasName("::std::endl")))) + .bind("expr"))), + this); +} + +void AvoidEndlCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Expression = Result.Nodes.getNodeAs<Expr>("expr"); + assert(Expression); + assert(isa<DeclRefExpr>(Expression) || isa<CallExpr>(Expression)); + + // FIXME: It would be great if we could transform + // 'std::cout << "Hi" << std::endl;' into + // 'std::cout << "Hi\n"'; + + if (llvm::isa<DeclRefExpr>(Expression)) { + // Handle the more common streaming '... << std::endl' case + const CharSourceRange TokenRange = + CharSourceRange::getTokenRange(Expression->getSourceRange()); + const StringRef SourceText = Lexer::getSourceText( + TokenRange, *Result.SourceManager, Result.Context->getLangOpts()); + + auto Diag = diag(Expression->getBeginLoc(), + "do not use '%0' with streams; use '\\n' instead") + << SourceText; + + Diag << FixItHint::CreateReplacement(TokenRange, "'\\n'"); + } else { + // Handle the less common function call 'std::endl(...)' case + const auto *CallExpression = llvm::cast<CallExpr>(Expression); + assert(CallExpression->getNumArgs() == 1); + + const StringRef SourceText = Lexer::getSourceText( + CharSourceRange::getTokenRange( + CallExpression->getCallee()->getSourceRange()), + *Result.SourceManager, Result.Context->getLangOpts()); + + const CharSourceRange ArgTokenRange = CharSourceRange::getTokenRange( + CallExpression->getArg(0)->getSourceRange()); + const StringRef ArgSourceText = Lexer::getSourceText( + ArgTokenRange, *Result.SourceManager, Result.Context->getLangOpts()); + + const std::string ReplacementString = + std::string(ArgSourceText) + " << '\\n'"; + + diag(CallExpression->getBeginLoc(), + "do not use '%0' with streams; use '\\n' instead") + << SourceText + << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(CallExpression->getSourceRange()), + ReplacementString); + } +} + +} // namespace clang::tidy::performance diff --git a/clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.h b/clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.h new file mode 100644 index 000000000000..db75fbcf4e89 --- /dev/null +++ b/clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.h @@ -0,0 +1,40 @@ +//===--- AvoidEndlCheck.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_AVOIDENDLCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_AVOIDENDLCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::performance { + +/// ClangTidyCheck Checks to flag for uses of 'std::endl' on streams and +/// suggests using the newline character '"\n"' instead. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/performance/avoid-endl.html +class AvoidEndlCheck : public ClangTidyCheck { +public: + AvoidEndlCheck(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; + + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::performance + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_AVOIDENDLCHECK_H diff --git a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt index 315364641692..f6676bcf4be4 100644 --- a/clang-tools-extra/clang-tidy/performance/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/performance/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangTidyPerformanceModule + AvoidEndlCheck.cpp FasterStringFindCheck.cpp ForRangeCopyCheck.cpp ImplicitConversionInLoopCheck.cpp diff --git a/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp b/clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp index 30a195f76b4e..b5151a1cc893 100644 --- 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 "AvoidEndlCheck.h" #include "FasterStringFindCheck.h" #include "ForRangeCopyCheck.h" #include "ImplicitConversionInLoopCheck.h" @@ -31,6 +32,7 @@ namespace performance { class PerformanceModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<AvoidEndlCheck>("performance-avoid-endl"); CheckFactories.registerCheck<FasterStringFindCheck>( "performance-faster-string-find"); CheckFactories.registerCheck<ForRangeCopyCheck>( |