1 //===--- AvoidEndlCheck.cpp - clang-tidy ----------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "AvoidEndlCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/AST/DeclCXX.h" 12 #include "clang/AST/Expr.h" 13 #include "clang/AST/ExprCXX.h" 14 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 #include "clang/ASTMatchers/ASTMatchers.h" 16 #include "clang/Lex/Lexer.h" 17 18 using namespace clang::ast_matchers; 19 20 namespace clang::tidy::performance { 21 22 void AvoidEndlCheck::registerMatchers(MatchFinder *Finder) { 23 Finder->addMatcher( 24 callExpr( 25 unless(isExpansionInSystemHeader()), 26 anyOf(cxxOperatorCallExpr( 27 hasOverloadedOperatorName("<<"), 28 hasRHS(declRefExpr(to(namedDecl(hasName("::std::endl")))) 29 .bind("expr"))), 30 callExpr(argumentCountIs(1), 31 callee(functionDecl(hasName("::std::endl")))) 32 .bind("expr"))), 33 this); 34 } 35 36 void AvoidEndlCheck::check(const MatchFinder::MatchResult &Result) { 37 const auto *Expression = Result.Nodes.getNodeAs<Expr>("expr"); 38 assert(Expression); 39 assert(isa<DeclRefExpr>(Expression) || isa<CallExpr>(Expression)); 40 41 // FIXME: It would be great if we could transform 42 // 'std::cout << "Hi" << std::endl;' into 43 // 'std::cout << "Hi\n"'; 44 45 if (llvm::isa<DeclRefExpr>(Expression)) { 46 // Handle the more common streaming '... << std::endl' case 47 const CharSourceRange TokenRange = 48 CharSourceRange::getTokenRange(Expression->getSourceRange()); 49 StringRef SourceText = Lexer::getSourceText( 50 TokenRange, *Result.SourceManager, Result.Context->getLangOpts()); 51 if (SourceText.empty()) 52 SourceText = "std::endl"; 53 auto Diag = diag(Expression->getBeginLoc(), 54 "do not use '%0' with streams; use '\\n' instead") 55 << SourceText; 56 if (TokenRange.isValid()) 57 Diag << FixItHint::CreateReplacement(TokenRange, "'\\n'"); 58 } else { 59 // Handle the less common function call 'std::endl(...)' case 60 const auto *CallExpression = llvm::cast<CallExpr>(Expression); 61 assert(CallExpression->getNumArgs() == 1); 62 63 StringRef SourceText = Lexer::getSourceText( 64 CharSourceRange::getTokenRange( 65 CallExpression->getCallee()->getSourceRange()), 66 *Result.SourceManager, Result.Context->getLangOpts()); 67 if (SourceText.empty()) 68 SourceText = "std::endl"; 69 auto Diag = diag(CallExpression->getBeginLoc(), 70 "do not use '%0' with streams; use '\\n' instead") 71 << SourceText; 72 73 const CharSourceRange ArgTokenRange = CharSourceRange::getTokenRange( 74 CallExpression->getArg(0)->getSourceRange()); 75 const StringRef ArgSourceText = Lexer::getSourceText( 76 ArgTokenRange, *Result.SourceManager, Result.Context->getLangOpts()); 77 const CharSourceRange ReplacementRange = 78 CharSourceRange::getTokenRange(CallExpression->getSourceRange()); 79 if (!ArgSourceText.empty() && ReplacementRange.isValid()) { 80 const std::string ReplacementString = 81 std::string(ArgSourceText) + " << '\\n'"; 82 Diag << FixItHint::CreateReplacement(ReplacementRange, ReplacementString); 83 } 84 } 85 } 86 87 } // namespace clang::tidy::performance 88