summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clang-tidy
diff options
context:
space:
mode:
authorAMS21 <AMS21.github@gmail.com>2023-04-22 10:50:42 +0000
committerPiotr Zegar <me@piotrzegar.pl>2023-04-22 10:50:51 +0000
commitcd893308b5d4d058b8ec6a2c4b00843d89e25552 (patch)
tree39559cbc1037e9b3573df575acf2efc1b8e0db9b /clang-tools-extra/clang-tidy
parenta79a6ea4a831facc564a36911d79d4472ad93cec (diff)
downloadllvm-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')
-rw-r--r--clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.cpp84
-rw-r--r--clang-tools-extra/clang-tidy/performance/AvoidEndlCheck.h40
-rw-r--r--clang-tools-extra/clang-tidy/performance/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp2
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>(