diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy')
4 files changed, 334 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index f8129767505..e14faaf877d 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyModernizeModule MakeUniqueCheck.cpp ModernizeTidyModule.cpp PassByValueCheck.cpp + RedundantVoidArgCheck.cpp ReplaceAutoPtrCheck.cpp ShrinkToFitCheck.cpp UseAutoCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index d7463abf30f..924461ca331 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -13,6 +13,7 @@ #include "LoopConvertCheck.h" #include "MakeUniqueCheck.h" #include "PassByValueCheck.h" +#include "RedundantVoidArgCheck.h" #include "ReplaceAutoPtrCheck.h" #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" @@ -32,6 +33,8 @@ public: CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert"); CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique"); CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); + CheckFactories.registerCheck<RedundantVoidArgCheck>( + "modernize-redundant-void-arg"); CheckFactories.registerCheck<ReplaceAutoPtrCheck>( "modernize-replace-auto-ptr"); CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit"); diff --git a/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp new file mode 100644 index 00000000000..5de65e4d101 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp @@ -0,0 +1,254 @@ +//===- RedundantVoidArgCheck.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 "RedundantVoidArgCheck.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { + +namespace { + +// Determine if the given QualType is a nullary function or pointer to same. +bool protoTypeHasNoParms(QualType QT) { + if (auto PT = QT->getAs<PointerType>()) { + QT = PT->getPointeeType(); + } + if (auto *MPT = QT->getAs<MemberPointerType>()) { + QT = MPT->getPointeeType(); + } + if (auto FP = QT->getAs<FunctionProtoType>()) { + return FP->getNumParams() == 0; + } + return false; +} + +const char FunctionId[] = "function"; +const char TypedefId[] = "typedef"; +const char FieldId[] = "field"; +const char VarId[] = "var"; +const char NamedCastId[] = "named-cast"; +const char CStyleCastId[] = "c-style-cast"; +const char ExplicitCastId[] = "explicit-cast"; +const char LambdaId[] = "lambda"; + +} // namespace + +namespace tidy { +namespace modernize { + +void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(functionDecl(isExpansionInMainFile(), parameterCountIs(0), + unless(isImplicit()), + unless(isExternC())).bind(FunctionId), + this); + Finder->addMatcher(typedefDecl(isExpansionInMainFile()).bind(TypedefId), + this); + auto ParenFunctionType = parenType(innerType(functionType())); + auto PointerToFunctionType = pointee(ParenFunctionType); + auto FunctionOrMemberPointer = + anyOf(hasType(pointerType(PointerToFunctionType)), + hasType(memberPointerType(PointerToFunctionType))); + Finder->addMatcher( + fieldDecl(isExpansionInMainFile(), FunctionOrMemberPointer).bind(FieldId), + this); + Finder->addMatcher( + varDecl(isExpansionInMainFile(), FunctionOrMemberPointer).bind(VarId), + this); + auto CastDestinationIsFunction = + hasDestinationType(pointsTo(ParenFunctionType)); + Finder->addMatcher( + cStyleCastExpr(isExpansionInMainFile(), CastDestinationIsFunction) + .bind(CStyleCastId), + this); + Finder->addMatcher( + cxxStaticCastExpr(isExpansionInMainFile(), CastDestinationIsFunction) + .bind(NamedCastId), + this); + Finder->addMatcher( + cxxReinterpretCastExpr(isExpansionInMainFile(), CastDestinationIsFunction) + .bind(NamedCastId), + this); + Finder->addMatcher(cxxConstCastExpr(isExpansionInMainFile(), + CastDestinationIsFunction).bind(NamedCastId), + this); + Finder->addMatcher(lambdaExpr(isExpansionInMainFile()).bind(LambdaId), this); +} + +void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) { + if (!Result.Context->getLangOpts().CPlusPlus) { + return; + } + + const BoundNodes &Nodes = Result.Nodes; + if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) { + processFunctionDecl(Result, Function); + } else if (const auto *Typedef = Nodes.getNodeAs<TypedefDecl>(TypedefId)) { + processTypedefDecl(Result, Typedef); + } else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) { + processFieldDecl(Result, Member); + } else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) { + processVarDecl(Result, Var); + } else if (const auto *NamedCast = + Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) { + processNamedCastExpr(Result, NamedCast); + } else if (const auto *CStyleCast = + Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) { + processExplicitCastExpr(Result, CStyleCast); + } else if (const auto *ExplicitCast = + Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) { + processExplicitCastExpr(Result, ExplicitCast); + } else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) { + processLambdaExpr(Result, Lambda); + } +} + +void RedundantVoidArgCheck::processFunctionDecl( + const MatchFinder::MatchResult &Result, const FunctionDecl *Function) { + SourceLocation Start = Function->getLocStart(); + if (Function->isThisDeclarationADefinition()) { + SourceLocation BeforeBody = + Function->getBody()->getLocStart().getLocWithOffset(-1); + removeVoidArgumentTokens(Result, SourceRange(Start, BeforeBody), + "function definition"); + } else { + removeVoidArgumentTokens(Result, Function->getSourceRange(), + "function declaration"); + } +} + +void RedundantVoidArgCheck::removeVoidArgumentTokens( + const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range, + StringRef GrammarLocation) { + std::string DeclText = + Lexer::getSourceText(CharSourceRange::getTokenRange(Range), + *Result.SourceManager, + Result.Context->getLangOpts()).str(); + Lexer PrototypeLexer(Range.getBegin(), Result.Context->getLangOpts(), + DeclText.data(), DeclText.data(), + DeclText.data() + DeclText.size()); + enum TokenState { + NothingYet, + SawLeftParen, + SawVoid, + }; + TokenState State = NothingYet; + Token VoidToken; + Token ProtoToken; + std::string Diagnostic = + ("redundant void argument list in " + GrammarLocation).str(); + + while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) { + switch (State) { + case NothingYet: + if (ProtoToken.is(tok::TokenKind::l_paren)) { + State = SawLeftParen; + } + break; + case SawLeftParen: + if (ProtoToken.is(tok::TokenKind::raw_identifier) && + ProtoToken.getRawIdentifier() == "void") { + State = SawVoid; + VoidToken = ProtoToken; + } else { + State = NothingYet; + } + break; + case SawVoid: + State = NothingYet; + if (ProtoToken.is(tok::TokenKind::r_paren)) { + removeVoidToken(VoidToken, Diagnostic); + } else if (ProtoToken.is(tok::TokenKind::l_paren)) { + State = SawLeftParen; + } + break; + } + } + + if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) { + removeVoidToken(VoidToken, Diagnostic); + } +} + +void RedundantVoidArgCheck::removeVoidToken(Token VoidToken, + StringRef Diagnostic) { + SourceLocation VoidLoc(VoidToken.getLocation()); + auto VoidRange = + CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3)); + diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange); +} + +void RedundantVoidArgCheck::processTypedefDecl( + const MatchFinder::MatchResult &Result, const TypedefDecl *Typedef) { + if (protoTypeHasNoParms(Typedef->getUnderlyingType())) { + removeVoidArgumentTokens(Result, Typedef->getSourceRange(), "typedef"); + } +} + +void RedundantVoidArgCheck::processFieldDecl( + const MatchFinder::MatchResult &Result, const FieldDecl *Member) { + if (protoTypeHasNoParms(Member->getType())) { + removeVoidArgumentTokens(Result, Member->getSourceRange(), + "field declaration"); + } +} + +void RedundantVoidArgCheck::processVarDecl( + const MatchFinder::MatchResult &Result, const VarDecl *Var) { + if (protoTypeHasNoParms(Var->getType())) { + SourceLocation Begin = Var->getLocStart(); + if (Var->hasInit()) { + SourceLocation InitStart = + Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart()) + .getLocWithOffset(-1); + removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart), + "variable declaration with initializer"); + } else { + removeVoidArgumentTokens(Result, Var->getSourceRange(), + "variable declaration"); + } + } +} + +void RedundantVoidArgCheck::processNamedCastExpr( + const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) { + if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) { + removeVoidArgumentTokens( + Result, + NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(), + "named cast"); + } +} + +void RedundantVoidArgCheck::processExplicitCastExpr( + const MatchFinder::MatchResult &Result, + const ExplicitCastExpr *ExplicitCast) { + if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) { + removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(), + "cast expression"); + } +} + +void RedundantVoidArgCheck::processLambdaExpr( + const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) { + if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 && + Lambda->hasExplicitParameters()) { + SourceLocation Begin = + Lambda->getIntroducerRange().getEnd().getLocWithOffset(1); + SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1); + removeVoidArgumentTokens(Result, SourceRange(Begin, End), + "lambda expression"); + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h new file mode 100644 index 00000000000..8feb6e5075d --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h @@ -0,0 +1,76 @@ +//===--- RedundantVoidArgCheck.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_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H + +#include "../ClangTidy.h" +#include "clang/Lex/Token.h" + +#include <string> + +namespace clang { +namespace tidy { +namespace modernize { + +/// \brief Find and remove redundant void argument lists. +/// +/// Examples: +/// `int f(void);` becomes `int f();` +/// `int (*f(void))(void);` becomes `int (*f())();` +/// `typedef int (*f_t(void))(void);` becomes `typedef int (*f_t())();` +/// `void (C::*p)(void);` becomes `void (C::*p)();` +/// `C::C(void) {}` becomes `C::C() {}` +/// `C::~C(void) {}` becomes `C::~C() {}` +/// +class RedundantVoidArgCheck : public ClangTidyCheck { +public: + RedundantVoidArgCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void processFunctionDecl(const ast_matchers::MatchFinder::MatchResult &Result, + const FunctionDecl *Function); + + void processTypedefDecl(const ast_matchers::MatchFinder::MatchResult &Result, + const TypedefDecl *Typedef); + + void processFieldDecl(const ast_matchers::MatchFinder::MatchResult &Result, + const FieldDecl *Member); + + void processVarDecl(const ast_matchers::MatchFinder::MatchResult &Result, + const VarDecl *Var); + + void + processNamedCastExpr(const ast_matchers::MatchFinder::MatchResult &Result, + const CXXNamedCastExpr *NamedCast); + + void + processExplicitCastExpr(const ast_matchers::MatchFinder::MatchResult &Result, + const ExplicitCastExpr *ExplicitCast); + + void processLambdaExpr(const ast_matchers::MatchFinder::MatchResult &Result, + const LambdaExpr *Lambda); + + void + removeVoidArgumentTokens(const ast_matchers::MatchFinder::MatchResult &Result, + SourceRange Range, StringRef GrammarLocation); + + void removeVoidToken(Token VoidToken, StringRef Diagnostic); +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H |