summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/readability
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/readability')
-rw-r--r--clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp257
-rw-r--r--clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h61
-rw-r--r--clang-tools-extra/clang-tidy/readability/CMakeLists.txt3
-rw-r--r--clang-tools-extra/clang-tidy/readability/FunctionSize.cpp106
-rw-r--r--clang-tools-extra/clang-tidy/readability/FunctionSize.h48
-rw-r--r--clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.cpp123
-rw-r--r--clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.h38
7 files changed, 636 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
new file mode 100644
index 00000000000..19825aa67cd
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
@@ -0,0 +1,257 @@
+//===--- BracesAroundStatementsCheck.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 "BracesAroundStatementsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+
+tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
+ const ASTContext *Context) {
+ Token Tok;
+ SourceLocation Beginning =
+ Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
+ const bool Invalid =
+ Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
+ assert(!Invalid && "Expected a valid token.");
+
+ if (Invalid)
+ return tok::NUM_TOKENS;
+
+ return Tok.getKind();
+}
+
+SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ assert(Loc.isValid());
+ for (;;) {
+ while (isWhitespace(*FullSourceLoc(Loc, SM).getCharacterData()))
+ Loc = Loc.getLocWithOffset(1);
+
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind == tok::NUM_TOKENS || TokKind != tok::comment)
+ return Loc;
+
+ // Fast-forward current token.
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ }
+}
+
+SourceLocation findEndLocation(SourceLocation LastTokenLoc,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ SourceLocation Loc = LastTokenLoc;
+ // Loc points to the beginning of the last (non-comment non-ws) token
+ // before end or ';'.
+ assert(Loc.isValid());
+ bool SkipEndWhitespaceAndComments = true;
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
+ TokKind == tok::r_brace) {
+ // If we are at ";" or "}", we found the last token. We could use as well
+ // `if (isa<NullStmt>(S))`, but it wouldn't work for nested statements.
+ SkipEndWhitespaceAndComments = false;
+ }
+
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ // Loc points past the last token before end or after ';'.
+
+ if (SkipEndWhitespaceAndComments) {
+ Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind == tok::semi)
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ }
+
+ for (;;) {
+ assert(Loc.isValid());
+ while (isHorizontalWhitespace(*FullSourceLoc(Loc, SM).getCharacterData()))
+ Loc = Loc.getLocWithOffset(1);
+
+ if (isVerticalWhitespace(*FullSourceLoc(Loc, SM).getCharacterData())) {
+ // EOL, insert brace before.
+ break;
+ }
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind != tok::comment) {
+ // Non-comment token, insert brace before.
+ break;
+ }
+
+ SourceLocation TokEndLoc =
+ Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ SourceRange TokRange(Loc, TokEndLoc);
+ StringRef Comment = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
+ if (Comment.startswith("/*") && Comment.find('\n') != StringRef::npos) {
+ // Multi-line block comment, insert brace before.
+ break;
+ }
+ // else: Trailing comment, insert brace after the newline.
+
+ // Fast-forward current token.
+ Loc = TokEndLoc;
+ }
+ return Loc;
+}
+
+} // namespace
+
+BracesAroundStatementsCheck::BracesAroundStatementsCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ // Always add braces by default.
+ ShortStatementLines(Options.get("ShortStatementLines", 0U)) {}
+
+void
+BracesAroundStatementsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ShortStatementLines", ShortStatementLines);
+}
+
+void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(ifStmt().bind("if"), this);
+ Finder->addMatcher(whileStmt().bind("while"), this);
+ Finder->addMatcher(doStmt().bind("do"), this);
+ Finder->addMatcher(forStmt().bind("for"), this);
+ Finder->addMatcher(forRangeStmt().bind("for-range"), this);
+}
+
+void
+BracesAroundStatementsCheck::check(const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const ASTContext *Context = Result.Context;
+
+ // Get location of closing parenthesis or 'do' to insert opening brace.
+ if (auto S = Result.Nodes.getNodeAs<ForStmt>("for")) {
+ checkStmt(Result, S->getBody(), S->getRParenLoc());
+ } else if (auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>("for-range")) {
+ checkStmt(Result, S->getBody(), S->getRParenLoc());
+ } else if (auto S = Result.Nodes.getNodeAs<DoStmt>("do")) {
+ checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
+ } else if (auto S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ if (StartLoc.isInvalid())
+ return;
+ checkStmt(Result, S->getBody(), StartLoc);
+ } else if (auto S = Result.Nodes.getNodeAs<IfStmt>("if")) {
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ if (StartLoc.isInvalid())
+ return;
+ checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
+ const Stmt *Else = S->getElse();
+ if (Else && !isa<IfStmt>(Else)) {
+ // Omit 'else if' statements here, they will be handled directly.
+ checkStmt(Result, Else, S->getElseLoc());
+ }
+ } else {
+ llvm_unreachable("Invalid match");
+ }
+}
+
+/// Find location of right parenthesis closing condition
+template <typename IfOrWhileStmt>
+SourceLocation
+BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ // Skip macros
+ if (S->getLocStart().isMacroID())
+ return SourceLocation();
+
+ static const char *const ErrorMessage =
+ "cannot find location of closing parenthesis ')'";
+ SourceLocation CondEndLoc = S->getCond()->getLocEnd();
+ if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
+ CondEndLoc = CondVar->getLocEnd();
+
+ assert(CondEndLoc.isValid());
+ SourceLocation PastCondEndLoc =
+ Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
+ if (PastCondEndLoc.isInvalid()) {
+ diag(CondEndLoc, ErrorMessage);
+ return SourceLocation();
+ }
+ SourceLocation RParenLoc =
+ forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
+ if (RParenLoc.isInvalid()) {
+ diag(PastCondEndLoc, ErrorMessage);
+ return SourceLocation();
+ }
+ tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
+ if (TokKind != tok::r_paren) {
+ diag(RParenLoc, ErrorMessage);
+ return SourceLocation();
+ }
+ return RParenLoc;
+}
+
+void
+BracesAroundStatementsCheck::checkStmt(const MatchFinder::MatchResult &Result,
+ const Stmt *S, SourceLocation InitialLoc,
+ SourceLocation EndLocHint) {
+ // 1) If there's a corresponding "else" or "while", the check inserts "} "
+ // right before that token.
+ // 2) If there's a multi-line block comment starting on the same line after
+ // the location we're inserting the closing brace at, or there's a non-comment
+ // token, the check inserts "\n}" right before that token.
+ // 3) Otherwise the check finds the end of line (possibly after some block or
+ // line comments) and inserts "\n}" right before that EOL.
+ if (!S || isa<CompoundStmt>(S)) {
+ // Already inside braces.
+ return;
+ }
+ // Skip macros.
+ if (S->getLocStart().isMacroID())
+ return;
+
+ const SourceManager &SM = *Result.SourceManager;
+ const ASTContext *Context = Result.Context;
+
+ // InitialLoc points at the last token before opening brace to be inserted.
+ assert(InitialLoc.isValid());
+ SourceLocation StartLoc =
+ Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
+ // StartLoc points at the location of the opening brace to be inserted.
+ SourceLocation EndLoc;
+ std::string ClosingInsertion;
+ if (EndLocHint.isValid()) {
+ EndLoc = EndLocHint;
+ ClosingInsertion = "} ";
+ } else {
+ EndLoc = findEndLocation(S->getLocEnd(), SM, Context);
+ ClosingInsertion = "\n}";
+ }
+
+ assert(StartLoc.isValid());
+ assert(EndLoc.isValid());
+ // Don't require braces for statements spanning less than certain number of
+ // lines.
+ if (ShortStatementLines) {
+ unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
+ unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
+ if (EndLine - StartLine < ShortStatementLines)
+ return;
+ }
+
+ auto Diag = diag(StartLoc, "statement should be inside braces");
+ Diag << FixItHint::CreateInsertion(StartLoc, " {")
+ << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h
new file mode 100644
index 00000000000..80987d84b37
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.h
@@ -0,0 +1,61 @@
+//===--- BracesAroundStatementsCheck.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_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks that bodies of 'if' statements and loops ('for', 'range-for',
+/// 'do-while', and 'while') are inside braces
+///
+/// Before:
+/// if (condition)
+/// statement;
+///
+/// After:
+/// if (condition) {
+/// statement;
+/// }
+///
+/// Additionally, one can define an option `ShortStatementLines` defining the
+/// minimal number of lines that the statement should have in order to trigger
+/// this check.
+/// The number of lines is counted from the end of condition or initial keyword
+/// (do/else) until the last line of the inner statement.
+/// Default value 0 means that braces will be added to all statements (not
+/// having them already).
+class BracesAroundStatementsCheck : public ClangTidyCheck {
+public:
+ BracesAroundStatementsCheck(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:
+ void checkStmt(const ast_matchers::MatchFinder::MatchResult &Result,
+ const Stmt *S, SourceLocation StartLoc,
+ SourceLocation EndLocHint = SourceLocation());
+ template <typename IfOrWhileStmt>
+ SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
+ const ASTContext *Context);
+
+private:
+ const unsigned ShortStatementLines;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index dc805cb219c..edbeb1fb39a 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -1,7 +1,10 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyReadability
+ BracesAroundStatementsCheck.cpp
+ FunctionSize.cpp
NamespaceCommentCheck.cpp
+ RedundantSmartptrGet.cpp
LINK_LIBS
clangAST
diff --git a/clang-tools-extra/clang-tidy/readability/FunctionSize.cpp b/clang-tools-extra/clang-tidy/readability/FunctionSize.cpp
new file mode 100644
index 00000000000..0c980e6a996
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/FunctionSize.cpp
@@ -0,0 +1,106 @@
+//===--- FunctionSize.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 "FunctionSize.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ LineThreshold(Options.get("LineThreshold", -1U)),
+ StatementThreshold(Options.get("StatementThreshold", 800U)),
+ BranchThreshold(Options.get("BranchThreshold", -1U)) {}
+
+void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "LineThreshold", LineThreshold);
+ Options.store(Opts, "StatementThreshold", StatementThreshold);
+ Options.store(Opts, "BranchThreshold", BranchThreshold);
+}
+
+void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ functionDecl(
+ unless(isInstantiated()),
+ forEachDescendant(
+ stmt(unless(compoundStmt()),
+ hasParent(stmt(anyOf(compoundStmt(), ifStmt(),
+ anyOf(whileStmt(), doStmt(),
+ forRangeStmt(), forStmt())))))
+ .bind("stmt"))).bind("func"),
+ this);
+}
+
+void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ FunctionInfo &FI = FunctionInfos[Func];
+
+ // Count the lines including whitespace and comments. Really simple.
+ if (!FI.Lines) {
+ if (const Stmt *Body = Func->getBody()) {
+ SourceManager *SM = Result.SourceManager;
+ if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
+ FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
+ SM->getSpellingLineNumber(Body->getLocStart());
+ }
+ }
+ }
+
+ const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt");
+ ++FI.Statements;
+
+ // TODO: switch cases, gotos
+ if (isa<IfStmt>(Statement) || isa<WhileStmt>(Statement) ||
+ isa<ForStmt>(Statement) || isa<SwitchStmt>(Statement) ||
+ isa<DoStmt>(Statement) || isa<CXXForRangeStmt>(Statement))
+ ++FI.Branches;
+}
+
+void FunctionSizeCheck::onEndOfTranslationUnit() {
+ // If we're above the limit emit a warning.
+ for (const auto &P : FunctionInfos) {
+ const FunctionInfo &FI = P.second;
+ if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
+ FI.Branches > BranchThreshold) {
+ diag(P.first->getLocation(),
+ "function '%0' exceeds recommended size/complexity thresholds")
+ << P.first->getNameAsString();
+ }
+
+ if (FI.Lines > LineThreshold) {
+ diag(P.first->getLocation(),
+ "%0 lines including whitespace and comments (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Lines << LineThreshold;
+ }
+
+ if (FI.Statements > StatementThreshold) {
+ diag(P.first->getLocation(), "%0 statements (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Statements << StatementThreshold;
+ }
+
+ if (FI.Branches > BranchThreshold) {
+ diag(P.first->getLocation(), "%0 branches (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Branches << BranchThreshold;
+ }
+ }
+
+ FunctionInfos.clear();
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/readability/FunctionSize.h b/clang-tools-extra/clang-tidy/readability/FunctionSize.h
new file mode 100644
index 00000000000..c1869f07bf1
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/FunctionSize.h
@@ -0,0 +1,48 @@
+//===--- FunctionSize.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_READABILITY_FUNCTIONSIZE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks for large functions based on various metrics.
+class FunctionSizeCheck : public ClangTidyCheck {
+public:
+ FunctionSizeCheck(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;
+ void onEndOfTranslationUnit() override;
+
+private:
+ struct FunctionInfo {
+ FunctionInfo() : Lines(0), Statements(0), Branches(0) {}
+ unsigned Lines;
+ unsigned Statements;
+ unsigned Branches;
+ };
+
+ const unsigned LineThreshold;
+ const unsigned StatementThreshold;
+ const unsigned BranchThreshold;
+
+ llvm::DenseMap<const FunctionDecl *, FunctionInfo> FunctionInfos;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZE_H
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.cpp b/clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.cpp
new file mode 100644
index 00000000000..8cf427fdb5c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.cpp
@@ -0,0 +1,123 @@
+//===--- RedundantSmartptrGet.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 "RedundantSmartptrGet.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+internal::Matcher<Expr> callToGet(internal::Matcher<Decl> OnClass) {
+ return memberCallExpr(
+ on(expr(anyOf(hasType(OnClass),
+ hasType(qualType(pointsTo(decl(OnClass).bind(
+ "ptr_to_ptr")))))).bind("smart_pointer")),
+ unless(callee(memberExpr(hasObjectExpression(thisExpr())))),
+ callee(methodDecl(hasName("get")))).bind("redundant_get");
+}
+
+void registerMatchersForGetArrowStart(MatchFinder *Finder,
+ MatchFinder::MatchCallback *Callback) {
+ const auto QuacksLikeASmartptr = recordDecl(
+ recordDecl().bind("duck_typing"),
+ has(methodDecl(hasName("operator->"),
+ returns(qualType(pointsTo(type().bind("op->Type")))))),
+ has(methodDecl(hasName("operator*"),
+ returns(qualType(references(type().bind("op*Type")))))),
+ has(methodDecl(hasName("get"),
+ returns(qualType(pointsTo(type().bind("getType")))))));
+
+ // Catch 'ptr.get()->Foo()'
+ Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(),
+ hasObjectExpression(ignoringImpCasts(
+ callToGet(QuacksLikeASmartptr)))),
+ Callback);
+
+ // Catch '*ptr.get()' or '*ptr->get()'
+ Finder->addMatcher(
+ unaryOperator(hasOperatorName("*"),
+ hasUnaryOperand(callToGet(QuacksLikeASmartptr))),
+ Callback);
+}
+
+void registerMatchersForGetEquals(MatchFinder *Finder,
+ MatchFinder::MatchCallback *Callback) {
+ // This one is harder to do with duck typing.
+ // The operator==/!= that we are looking for might be member or non-member,
+ // might be on global namespace or found by ADL, might be a template, etc.
+ // For now, lets keep a list of known standard types.
+
+ const auto IsAKnownSmartptr = recordDecl(
+ anyOf(hasName("::std::unique_ptr"), hasName("::std::shared_ptr")));
+
+ // Matches against nullptr.
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+ hasEitherOperand(ignoringImpCasts(nullPtrLiteralExpr())),
+ hasEitherOperand(callToGet(IsAKnownSmartptr))),
+ Callback);
+ // TODO: Catch ptr.get() == other_ptr.get()
+}
+
+
+} // namespace
+
+void RedundantSmartptrGet::registerMatchers(MatchFinder *Finder) {
+ registerMatchersForGetArrowStart(Finder, this);
+ registerMatchersForGetEquals(Finder, this);
+}
+
+namespace {
+bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
+ if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
+ return true;
+ // Verify that the types match.
+ // We can't do this on the matcher because the type nodes can be different,
+ // even though they represent the same type. This difference comes from how
+ // the type is referenced (eg. through a typedef, a type trait, etc).
+ const Type *OpArrowType =
+ Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
+ const Type *OpStarType =
+ Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
+ const Type *GetType =
+ Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
+ return OpArrowType == OpStarType && OpArrowType == GetType;
+}
+} // namespace
+
+void RedundantSmartptrGet::check(const MatchFinder::MatchResult &Result) {
+ if (!allReturnTypesMatch(Result)) return;
+
+ bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
+ bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
+ const Expr *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
+ const Expr *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
+
+ if (IsPtrToPtr && IsMemberExpr) {
+ // Ignore this case (eg. Foo->get()->DoSomething());
+ return;
+ }
+
+ StringRef SmartptrText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
+ *Result.SourceManager, LangOptions());
+ // Replace foo->get() with *foo, and foo.get() with foo.
+ std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
+ diag(GetCall->getLocStart(), "Redundant get() call on smart pointer.")
+ << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.h b/clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.h
new file mode 100644
index 00000000000..40f0c74cb5c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/RedundantSmartptrGet.h
@@ -0,0 +1,38 @@
+//===--- RedundantSmartptrGet.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_READABILITY_REDUNDANTSMARTPTRGET_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGET_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Find and remove redundant calls to smart pointer's .get() method.
+///
+/// Examples:
+/// ptr.get()->Foo() ==> ptr->Foo()
+/// *ptr.get() ==> *ptr
+/// *ptr->get() ==> **ptr
+class RedundantSmartptrGet : public ClangTidyCheck {
+public:
+ RedundantSmartptrGet(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGET_H
+
OpenPOWER on IntegriCloud