Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 27 additions & 263 deletions clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,281 +7,45 @@
//===----------------------------------------------------------------------===//

#include "RedundantVoidArgCheck.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "../utils/LexerUtils.h"
#include "clang/ASTMatchers/ASTMatchers.h"

using namespace clang::ast_matchers;

namespace clang::tidy::modernize {

// Determine if the given QualType is a nullary function or pointer to same.
static bool protoTypeHasNoParms(QualType QT) {
if (const auto *PT = QT->getAs<PointerType>())
QT = PT->getPointeeType();
if (auto *MPT = QT->getAs<MemberPointerType>())
QT = MPT->getPointeeType();
if (const auto *FP = QT->getAs<FunctionProtoType>())
return FP->getNumParams() == 0;
return false;
}

static const char FunctionId[] = "function";
static const char TypedefId[] = "typedef";
static const char FieldId[] = "field";
static const char VarId[] = "var";
static const char NamedCastId[] = "named-cast";
static const char CStyleCastId[] = "c-style-cast";
static const char ExplicitCastId[] = "explicit-cast";
static const char LambdaId[] = "lambda";

void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
unless(isInstantiated()), unless(isExternC()))
.bind(FunctionId),
this);
Finder->addMatcher(typedefNameDecl(unless(isImplicit())).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),
traverse(TK_IgnoreUnlessSpelledInSource,
functionTypeLoc(unless(hasParent(functionDecl(isExternC()))))
.bind("fn")),
this);
Finder->addMatcher(
cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
traverse(TK_IgnoreUnlessSpelledInSource, lambdaExpr().bind("fn")), 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) {
const auto *Method = dyn_cast<CXXMethodDecl>(Function);
const SourceLocation Start = Method && Method->getParent()->isLambda()
? Method->getBeginLoc()
: Function->getLocation();
SourceLocation End = Function->getEndLoc();
if (Function->isThisDeclarationADefinition()) {
if (const Stmt *Body = Function->getBody()) {
End = Body->getBeginLoc();
if (End.isMacroID() &&
Result.SourceManager->isAtStartOfImmediateMacroExpansion(End))
End = Result.SourceManager->getExpansionLoc(End);
End = End.getLocWithOffset(-1);
}
removeVoidArgumentTokens(Result, SourceRange(Start, End),
"function definition");
} else
removeVoidArgumentTokens(Result, SourceRange(Start, End),
"function declaration");
}

static bool isMacroIdentifier(const IdentifierTable &Idents,
const Token &ProtoToken) {
if (!ProtoToken.is(tok::TokenKind::raw_identifier))
return false;

const IdentifierTable::iterator It =
Idents.find(ProtoToken.getRawIdentifier());
if (It == Idents.end())
return false;

return It->second->hadMacroDefinition();
}

void RedundantVoidArgCheck::removeVoidArgumentTokens(
const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
StringRef GrammarLocation) {
const 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 class TokenState {
Start,
MacroId,
MacroLeftParen,
MacroArguments,
LeftParen,
Void,
};
TokenState State = TokenState::Start;
Token VoidToken;
Token ProtoToken;
const IdentifierTable &Idents = Result.Context->Idents;
int MacroLevel = 0;
const std::string Diagnostic =
("redundant void argument list in " + GrammarLocation).str();

while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
switch (State) {
case TokenState::Start:
if (ProtoToken.is(tok::TokenKind::l_paren))
State = TokenState::LeftParen;
else if (isMacroIdentifier(Idents, ProtoToken))
State = TokenState::MacroId;
break;
case TokenState::MacroId:
if (ProtoToken.is(tok::TokenKind::l_paren))
State = TokenState::MacroLeftParen;
else
State = TokenState::Start;
break;
case TokenState::MacroLeftParen:
++MacroLevel;
if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
if (isMacroIdentifier(Idents, ProtoToken))
State = TokenState::MacroId;
else
State = TokenState::MacroArguments;
} else if (ProtoToken.is(tok::TokenKind::r_paren)) {
--MacroLevel;
if (MacroLevel == 0)
State = TokenState::Start;
else
State = TokenState::MacroId;
} else
State = TokenState::MacroArguments;
break;
case TokenState::MacroArguments:
if (isMacroIdentifier(Idents, ProtoToken))
State = TokenState::MacroLeftParen;
else if (ProtoToken.is(tok::TokenKind::r_paren)) {
--MacroLevel;
if (MacroLevel == 0)
State = TokenState::Start;
}
break;
case TokenState::LeftParen:
if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
if (isMacroIdentifier(Idents, ProtoToken))
State = TokenState::MacroId;
else if (ProtoToken.getRawIdentifier() == "void") {
State = TokenState::Void;
VoidToken = ProtoToken;
}
} else if (ProtoToken.is(tok::TokenKind::l_paren))
State = TokenState::LeftParen;
else
State = TokenState::Start;
break;
case TokenState::Void:
State = TokenState::Start;
if (ProtoToken.is(tok::TokenKind::r_paren))
removeVoidToken(VoidToken, Diagnostic);
else if (ProtoToken.is(tok::TokenKind::l_paren))
State = TokenState::LeftParen;
break;
}
}

if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
removeVoidToken(VoidToken, Diagnostic);
}

void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
StringRef Diagnostic) {
const SourceLocation VoidLoc = VoidToken.getLocation();
diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
}

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())) {
const SourceLocation Begin = Var->getBeginLoc();
if (Var->hasInit()) {
const 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()) {
const SourceManager *SM = Result.SourceManager;
const TypeLoc TL =
Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
removeVoidArgumentTokens(Result,
{SM->getSpellingLoc(TL.getBeginLoc()),
SM->getSpellingLoc(TL.getEndLoc())},
"lambda expression");
}
const FunctionTypeLoc TL = [&] {
if (const auto *TL = Result.Nodes.getNodeAs<FunctionTypeLoc>("fn"))
return *TL;
return Result.Nodes.getNodeAs<LambdaExpr>("fn")
->getCallOperator()
->getFunctionTypeLoc();
}();

if (TL.getNumParams() != 0)
return;

const std::optional<Token> Tok = utils::lexer::findNextTokenSkippingComments(
Result.SourceManager->getSpellingLoc(TL.getLParenLoc()),
*Result.SourceManager, getLangOpts());

if (!Tok || Tok->isNot(tok::raw_identifier) ||
Tok->getRawIdentifier() != "void")
return;

diag(Tok->getLocation(), "redundant void argument list")
<< FixItHint::CreateRemoval(Tok->getLocation());
}

} // namespace clang::tidy::modernize
34 changes: 0 additions & 34 deletions clang-tools-extra/clang-tidy/modernize/RedundantVoidArgCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANTVOIDARGCHECK_H

#include "../ClangTidyCheck.h"
#include "clang/Lex/Token.h"

#include <string>

namespace clang::tidy::modernize {

Expand All @@ -38,37 +35,6 @@ class RedundantVoidArgCheck : public ClangTidyCheck {
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
processTypedefNameDecl(const ast_matchers::MatchFinder::MatchResult &Result,
const TypedefNameDecl *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 clang::tidy::modernize
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// RUN: %check_clang_tidy %s modernize-redundant-void-arg %t -- -- -fdelayed-template-parsing

int foo(void) {
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list [modernize-redundant-void-arg]
// CHECK-FIXES: int foo() {
return 0;
}

template <class T>
struct MyFoo {
int foo(void) {
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list [modernize-redundant-void-arg]
// CHECK-FIXES: int foo() {
return 0;
}
Expand All @@ -21,7 +21,7 @@ template <class T>
struct MyBar {
// This declaration isn't instantiated and won't be parsed 'delayed-template-parsing'.
int foo(void) {
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list [modernize-redundant-void-arg]
// CHECK-FIXES: int foo() {
return 0;
}
Expand Down
Loading