blob: 2c5611b705fbde9d1a8226c921465759d2b9fdda [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 {
// 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 End;
if (Function->hasBody())
End = Function->getBody()->getLocStart().getLocWithOffset(-1);
else
End = 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,
Result.Context->getLangOpts());
std::string DeclText = Lexer::getSourceText(CharRange, *Result.SourceManager,
Result.Context->getLangOpts())
.str();
Lexer PrototypeLexer(CharRange.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