10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Lex/Lexer.h"
22 bool protoTypeHasNoParms(QualType QT) {
23 if (
const auto *PT = QT->getAs<PointerType>())
24 QT = PT->getPointeeType();
25 if (
auto *MPT = QT->getAs<MemberPointerType>())
26 QT = MPT->getPointeeType();
27 if (
const auto *FP = QT->getAs<FunctionProtoType>())
28 return FP->getNumParams() == 0;
32 const char FunctionId[] =
"function";
33 const char TypedefId[] =
"typedef";
35 const char VarId[] =
"var";
36 const char NamedCastId[] =
"named-cast";
37 const char CStyleCastId[] =
"c-style-cast";
38 const char ExplicitCastId[] =
"explicit-cast";
39 const char LambdaId[] =
"lambda";
43 void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
44 Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
45 unless(isInstantiated()), unless(isExternC()))
48 Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId),
50 auto ParenFunctionType = parenType(innerType(functionType()));
51 auto PointerToFunctionType = pointee(ParenFunctionType);
52 auto FunctionOrMemberPointer =
53 anyOf(hasType(pointerType(PointerToFunctionType)),
54 hasType(memberPointerType(PointerToFunctionType)));
55 Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(
FieldId),
this);
56 Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId),
this);
57 auto CastDestinationIsFunction =
58 hasDestinationType(pointsTo(ParenFunctionType));
60 cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId),
this);
62 cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
64 cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
67 cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
68 Finder->addMatcher(lambdaExpr().bind(LambdaId),
this);
72 const BoundNodes &Nodes = Result.Nodes;
73 if (
const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId))
74 processFunctionDecl(Result, Function);
76 Nodes.getNodeAs<TypedefNameDecl>(TypedefId))
78 else if (
const auto *Member = Nodes.getNodeAs<FieldDecl>(
FieldId))
79 processFieldDecl(Result, Member);
80 else if (
const auto *Var = Nodes.getNodeAs<VarDecl>(VarId))
81 processVarDecl(Result, Var);
82 else if (
const auto *NamedCast =
83 Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId))
84 processNamedCastExpr(Result, NamedCast);
85 else if (
const auto *CStyleCast =
86 Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId))
87 processExplicitCastExpr(Result, CStyleCast);
88 else if (
const auto *ExplicitCast =
89 Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId))
90 processExplicitCastExpr(Result, ExplicitCast);
91 else if (
const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId))
92 processLambdaExpr(Result, Lambda);
95 void RedundantVoidArgCheck::processFunctionDecl(
96 const MatchFinder::MatchResult &Result,
const FunctionDecl *Function) {
97 const auto *Method = dyn_cast<CXXMethodDecl>(Function);
98 SourceLocation Start = Method && Method->getParent()->isLambda()
99 ? Method->getBeginLoc()
100 : Function->getLocation();
101 SourceLocation End = Function->getEndLoc();
102 if (Function->isThisDeclarationADefinition()) {
103 if (
const Stmt *Body = Function->getBody()) {
104 End = Body->getBeginLoc();
105 if (End.isMacroID() &&
106 Result.SourceManager->isAtStartOfImmediateMacroExpansion(End))
107 End = Result.SourceManager->getExpansionLoc(End);
108 End = End.getLocWithOffset(-1);
110 removeVoidArgumentTokens(Result, SourceRange(Start, End),
111 "function definition");
113 removeVoidArgumentTokens(Result, SourceRange(Start, End),
114 "function declaration");
118 if (!ProtoToken.is(tok::TokenKind::raw_identifier))
121 IdentifierTable::iterator It = Idents.find(ProtoToken.getRawIdentifier());
122 if (It == Idents.end())
125 return It->second->hadMacroDefinition();
128 void RedundantVoidArgCheck::removeVoidArgumentTokens(
129 const ast_matchers::MatchFinder::MatchResult &Result, SourceRange
Range,
130 StringRef GrammarLocation) {
131 CharSourceRange CharRange =
132 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
Range),
133 *Result.SourceManager, getLangOpts());
135 std::string DeclText =
138 Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
139 DeclText.data(), DeclText.data() + DeclText.size());
140 enum class TokenState {
148 TokenState State = TokenState::Start;
151 const IdentifierTable &Idents = Result.Context->Idents;
154 (
"redundant void argument list in " + GrammarLocation).str();
156 while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
158 case TokenState::Start:
159 if (ProtoToken.is(tok::TokenKind::l_paren))
160 State = TokenState::LeftParen;
162 State = TokenState::MacroId;
164 case TokenState::MacroId:
165 if (ProtoToken.is(tok::TokenKind::l_paren))
166 State = TokenState::MacroLeftParen;
168 State = TokenState::Start;
170 case TokenState::MacroLeftParen:
172 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
174 State = TokenState::MacroId;
176 State = TokenState::MacroArguments;
177 }
else if (ProtoToken.is(tok::TokenKind::r_paren)) {
180 State = TokenState::Start;
182 State = TokenState::MacroId;
184 State = TokenState::MacroArguments;
186 case TokenState::MacroArguments:
188 State = TokenState::MacroLeftParen;
189 else if (ProtoToken.is(tok::TokenKind::r_paren)) {
192 State = TokenState::Start;
195 case TokenState::LeftParen:
196 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
198 State = TokenState::MacroId;
199 else if (ProtoToken.getRawIdentifier() ==
"void") {
200 State = TokenState::Void;
201 VoidToken = ProtoToken;
203 }
else if (ProtoToken.is(tok::TokenKind::l_paren))
204 State = TokenState::LeftParen;
206 State = TokenState::Start;
208 case TokenState::Void:
209 State = TokenState::Start;
210 if (ProtoToken.is(tok::TokenKind::r_paren))
212 else if (ProtoToken.is(tok::TokenKind::l_paren))
213 State = TokenState::LeftParen;
218 if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
222 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
224 SourceLocation VoidLoc = VoidToken.getLocation();
225 diag(VoidLoc,
Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
228 void RedundantVoidArgCheck::processTypedefNameDecl(
229 const MatchFinder::MatchResult &Result,
231 if (protoTypeHasNoParms(
TypedefName->getUnderlyingType()))
232 removeVoidArgumentTokens(Result,
TypedefName->getSourceRange(),
237 void RedundantVoidArgCheck::processFieldDecl(
238 const MatchFinder::MatchResult &Result,
const FieldDecl *Member) {
239 if (protoTypeHasNoParms(Member->getType()))
240 removeVoidArgumentTokens(Result, Member->getSourceRange(),
241 "field declaration");
244 void RedundantVoidArgCheck::processVarDecl(
245 const MatchFinder::MatchResult &Result,
const VarDecl *Var) {
246 if (protoTypeHasNoParms(Var->getType())) {
247 SourceLocation Begin = Var->getBeginLoc();
248 if (Var->hasInit()) {
249 SourceLocation InitStart =
250 Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
251 .getLocWithOffset(-1);
252 removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
253 "variable declaration with initializer");
255 removeVoidArgumentTokens(Result, Var->getSourceRange(),
256 "variable declaration");
260 void RedundantVoidArgCheck::processNamedCastExpr(
261 const MatchFinder::MatchResult &Result,
const CXXNamedCastExpr *NamedCast) {
262 if (protoTypeHasNoParms(NamedCast->getTypeAsWritten()))
263 removeVoidArgumentTokens(
265 NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
269 void RedundantVoidArgCheck::processExplicitCastExpr(
270 const MatchFinder::MatchResult &Result,
271 const ExplicitCastExpr *ExplicitCast) {
272 if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten()))
273 removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
277 void RedundantVoidArgCheck::processLambdaExpr(
278 const MatchFinder::MatchResult &Result,
const LambdaExpr *Lambda) {
279 if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
280 Lambda->hasExplicitParameters()) {
281 SourceManager *SM = Result.SourceManager;
282 TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
283 removeVoidArgumentTokens(Result,
284 {SM->getSpellingLoc(TL.getBeginLoc()),
285 SM->getSpellingLoc(TL.getEndLoc())},
286 "lambda expression");