blob: 6701fa45cedc5b9f1fd69b63148765d42384d14a [file] [log] [blame]
//===- 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 tidy {
namespace modernize {
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
void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
unless(isExternC()))
.bind(FunctionId),
this);
Finder->addMatcher(typedefNameDecl().bind(TypedefId), this);
auto ParenFunctionType = parenType(innerType(functionType()));
auto PointerToFunctionType = pointee(ParenFunctionType);
auto FunctionOrMemberPointer =
anyOf(hasType(pointerType(PointerToFunctionType)),
hasType(memberPointerType(PointerToFunctionType)));
Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this);
Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this);
auto CastDestinationIsFunction =
hasDestinationType(pointsTo(ParenFunctionType));
Finder->addMatcher(
cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
Finder->addMatcher(
cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
Finder->addMatcher(
cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
Finder->addMatcher(
cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
}
void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
const BoundNodes &Nodes = Result.Nodes;
if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
processFunctionDecl(Result, Function);
} else if (const auto *TypedefName =
Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) {
processTypedefNameDecl(Result, TypedefName);
} 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) {
if (Function->isThisDeclarationADefinition()) {
const Stmt *Body = Function->getBody();
SourceLocation Start = Function->getLocStart();
SourceLocation End =
Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd();
removeVoidArgumentTokens(Result, SourceRange(Start, End),
"function definition");
} else {
removeVoidArgumentTokens(Result, Function->getSourceRange(),
"function declaration");
}
}
void RedundantVoidArgCheck::removeVoidArgumentTokens(
const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
StringRef GrammarLocation) {
CharSourceRange CharRange =
Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
*Result.SourceManager, getLangOpts());
std::string DeclText =
Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
.str();
Lexer PrototypeLexer(CharRange.getBegin(), 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::processTypedefNameDecl(
const MatchFinder::MatchResult &Result,
const TypedefNameDecl *TypedefName) {
if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) {
removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
isa<TypedefDecl>(TypedefName) ? "typedef"
: "type alias");
}
}
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