forked from OSchip/llvm-project
251 lines
9.1 KiB
C++
251 lines
9.1 KiB
C++
//===- RedundantVoidArgCheck.cpp - clang-tidy -----------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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(isInstantiated()), 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->getBeginLoc();
|
|
SourceLocation End =
|
|
Body ? Body->getBeginLoc().getLocWithOffset(-1) : Function->getEndLoc();
|
|
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 if (ProtoToken.is(tok::TokenKind::l_paren)) {
|
|
State = SawLeftParen;
|
|
} 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->getBeginLoc();
|
|
if (Var->hasInit()) {
|
|
SourceLocation InitStart =
|
|
Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
|
|
.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()) {
|
|
SourceManager *SM = Result.SourceManager;
|
|
TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
|
|
removeVoidArgumentTokens(Result,
|
|
{SM->getSpellingLoc(TL.getBeginLoc()),
|
|
SM->getSpellingLoc(TL.getEndLoc())},
|
|
"lambda expression");
|
|
}
|
|
}
|
|
|
|
} // namespace modernize
|
|
} // namespace tidy
|
|
} // namespace clang
|