|
7 | 7 | //===----------------------------------------------------------------------===// |
8 | 8 |
|
9 | 9 | #include "RedundantVoidArgCheck.h" |
10 | | -#include "clang/Frontend/CompilerInstance.h" |
11 | | -#include "clang/Lex/Lexer.h" |
| 10 | +#include "../utils/LexerUtils.h" |
| 11 | +#include "clang/ASTMatchers/ASTMatchers.h" |
12 | 12 |
|
13 | 13 | using namespace clang::ast_matchers; |
| 14 | +using namespace clang::ast_matchers::internal; |
14 | 15 |
|
15 | 16 | namespace clang::tidy::modernize { |
16 | 17 |
|
17 | | -// Determine if the given QualType is a nullary function or pointer to same. |
18 | | -static bool protoTypeHasNoParms(QualType QT) { |
19 | | - if (const auto *PT = QT->getAs<PointerType>()) |
20 | | - QT = PT->getPointeeType(); |
21 | | - if (auto *MPT = QT->getAs<MemberPointerType>()) |
22 | | - QT = MPT->getPointeeType(); |
23 | | - if (const auto *FP = QT->getAs<FunctionProtoType>()) |
24 | | - return FP->getNumParams() == 0; |
25 | | - return false; |
26 | | -} |
27 | | - |
28 | | -static const char FunctionId[] = "function"; |
29 | | -static const char TypedefId[] = "typedef"; |
30 | | -static const char FieldId[] = "field"; |
31 | | -static const char VarId[] = "var"; |
32 | | -static const char NamedCastId[] = "named-cast"; |
33 | | -static const char CStyleCastId[] = "c-style-cast"; |
34 | | -static const char ExplicitCastId[] = "explicit-cast"; |
35 | | -static const char LambdaId[] = "lambda"; |
36 | | - |
37 | 18 | void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) { |
38 | | - Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()), |
39 | | - unless(isInstantiated()), unless(isExternC())) |
40 | | - .bind(FunctionId), |
41 | | - this); |
42 | | - Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId), |
| 19 | + const VariadicDynCastAllOfMatcher<TypeLoc, FunctionProtoTypeLoc> |
| 20 | + functionProtoTypeLoc; // NOLINT(readability-identifier-naming) |
| 21 | + Finder->addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, |
| 22 | + functionProtoTypeLoc( |
| 23 | + unless(hasParent(functionDecl(isExternC())))) |
| 24 | + .bind("fn")), |
43 | 25 | this); |
44 | | - auto ParenFunctionType = parenType(innerType(functionType())); |
45 | | - auto PointerToFunctionType = pointee(ParenFunctionType); |
46 | | - auto FunctionOrMemberPointer = |
47 | | - anyOf(hasType(pointerType(PointerToFunctionType)), |
48 | | - hasType(memberPointerType(PointerToFunctionType))); |
49 | | - Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this); |
50 | | - Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this); |
51 | | - auto CastDestinationIsFunction = |
52 | | - hasDestinationType(pointsTo(ParenFunctionType)); |
53 | 26 | Finder->addMatcher( |
54 | | - cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this); |
55 | | - Finder->addMatcher( |
56 | | - cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); |
57 | | - Finder->addMatcher( |
58 | | - cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId), |
59 | | - this); |
60 | | - Finder->addMatcher( |
61 | | - cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); |
62 | | - Finder->addMatcher(lambdaExpr().bind(LambdaId), this); |
| 27 | + traverse(TK_IgnoreUnlessSpelledInSource, lambdaExpr().bind("fn")), this); |
63 | 28 | } |
64 | 29 |
|
65 | 30 | void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) { |
66 | | - const BoundNodes &Nodes = Result.Nodes; |
67 | | - if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) |
68 | | - processFunctionDecl(Result, Function); |
69 | | - else if (const auto *TypedefName = |
70 | | - Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) |
71 | | - processTypedefNameDecl(Result, TypedefName); |
72 | | - else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) |
73 | | - processFieldDecl(Result, Member); |
74 | | - else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) |
75 | | - processVarDecl(Result, Var); |
76 | | - else if (const auto *NamedCast = |
77 | | - Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) |
78 | | - processNamedCastExpr(Result, NamedCast); |
79 | | - else if (const auto *CStyleCast = |
80 | | - Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) |
81 | | - processExplicitCastExpr(Result, CStyleCast); |
82 | | - else if (const auto *ExplicitCast = |
83 | | - Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) |
84 | | - processExplicitCastExpr(Result, ExplicitCast); |
85 | | - else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) |
86 | | - processLambdaExpr(Result, Lambda); |
87 | | -} |
88 | | - |
89 | | -void RedundantVoidArgCheck::processFunctionDecl( |
90 | | - const MatchFinder::MatchResult &Result, const FunctionDecl *Function) { |
91 | | - const auto *Method = dyn_cast<CXXMethodDecl>(Function); |
92 | | - const SourceLocation Start = Method && Method->getParent()->isLambda() |
93 | | - ? Method->getBeginLoc() |
94 | | - : Function->getLocation(); |
95 | | - SourceLocation End = Function->getEndLoc(); |
96 | | - if (Function->isThisDeclarationADefinition()) { |
97 | | - if (const Stmt *Body = Function->getBody()) { |
98 | | - End = Body->getBeginLoc(); |
99 | | - if (End.isMacroID() && |
100 | | - Result.SourceManager->isAtStartOfImmediateMacroExpansion(End)) |
101 | | - End = Result.SourceManager->getExpansionLoc(End); |
102 | | - End = End.getLocWithOffset(-1); |
103 | | - } |
104 | | - removeVoidArgumentTokens(Result, SourceRange(Start, End), |
105 | | - "function definition"); |
106 | | - } else |
107 | | - removeVoidArgumentTokens(Result, SourceRange(Start, End), |
108 | | - "function declaration"); |
109 | | -} |
110 | | - |
111 | | -static bool isMacroIdentifier(const IdentifierTable &Idents, |
112 | | - const Token &ProtoToken) { |
113 | | - if (!ProtoToken.is(tok::TokenKind::raw_identifier)) |
114 | | - return false; |
115 | | - |
116 | | - const IdentifierTable::iterator It = |
117 | | - Idents.find(ProtoToken.getRawIdentifier()); |
118 | | - if (It == Idents.end()) |
119 | | - return false; |
120 | | - |
121 | | - return It->second->hadMacroDefinition(); |
122 | | -} |
123 | | - |
124 | | -void RedundantVoidArgCheck::removeVoidArgumentTokens( |
125 | | - const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range, |
126 | | - StringRef GrammarLocation) { |
127 | | - const CharSourceRange CharRange = |
128 | | - Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range), |
129 | | - *Result.SourceManager, getLangOpts()); |
130 | | - |
131 | | - std::string DeclText = |
132 | | - Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts()) |
133 | | - .str(); |
134 | | - Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(), |
135 | | - DeclText.data(), DeclText.data() + DeclText.size()); |
136 | | - enum class TokenState { |
137 | | - Start, |
138 | | - MacroId, |
139 | | - MacroLeftParen, |
140 | | - MacroArguments, |
141 | | - LeftParen, |
142 | | - Void, |
143 | | - }; |
144 | | - TokenState State = TokenState::Start; |
145 | | - Token VoidToken; |
146 | | - Token ProtoToken; |
147 | | - const IdentifierTable &Idents = Result.Context->Idents; |
148 | | - int MacroLevel = 0; |
149 | | - const std::string Diagnostic = |
150 | | - ("redundant void argument list in " + GrammarLocation).str(); |
151 | | - |
152 | | - while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) { |
153 | | - switch (State) { |
154 | | - case TokenState::Start: |
155 | | - if (ProtoToken.is(tok::TokenKind::l_paren)) |
156 | | - State = TokenState::LeftParen; |
157 | | - else if (isMacroIdentifier(Idents, ProtoToken)) |
158 | | - State = TokenState::MacroId; |
159 | | - break; |
160 | | - case TokenState::MacroId: |
161 | | - if (ProtoToken.is(tok::TokenKind::l_paren)) |
162 | | - State = TokenState::MacroLeftParen; |
163 | | - else |
164 | | - State = TokenState::Start; |
165 | | - break; |
166 | | - case TokenState::MacroLeftParen: |
167 | | - ++MacroLevel; |
168 | | - if (ProtoToken.is(tok::TokenKind::raw_identifier)) { |
169 | | - if (isMacroIdentifier(Idents, ProtoToken)) |
170 | | - State = TokenState::MacroId; |
171 | | - else |
172 | | - State = TokenState::MacroArguments; |
173 | | - } else if (ProtoToken.is(tok::TokenKind::r_paren)) { |
174 | | - --MacroLevel; |
175 | | - if (MacroLevel == 0) |
176 | | - State = TokenState::Start; |
177 | | - else |
178 | | - State = TokenState::MacroId; |
179 | | - } else |
180 | | - State = TokenState::MacroArguments; |
181 | | - break; |
182 | | - case TokenState::MacroArguments: |
183 | | - if (isMacroIdentifier(Idents, ProtoToken)) |
184 | | - State = TokenState::MacroLeftParen; |
185 | | - else if (ProtoToken.is(tok::TokenKind::r_paren)) { |
186 | | - --MacroLevel; |
187 | | - if (MacroLevel == 0) |
188 | | - State = TokenState::Start; |
189 | | - } |
190 | | - break; |
191 | | - case TokenState::LeftParen: |
192 | | - if (ProtoToken.is(tok::TokenKind::raw_identifier)) { |
193 | | - if (isMacroIdentifier(Idents, ProtoToken)) |
194 | | - State = TokenState::MacroId; |
195 | | - else if (ProtoToken.getRawIdentifier() == "void") { |
196 | | - State = TokenState::Void; |
197 | | - VoidToken = ProtoToken; |
198 | | - } |
199 | | - } else if (ProtoToken.is(tok::TokenKind::l_paren)) |
200 | | - State = TokenState::LeftParen; |
201 | | - else |
202 | | - State = TokenState::Start; |
203 | | - break; |
204 | | - case TokenState::Void: |
205 | | - State = TokenState::Start; |
206 | | - if (ProtoToken.is(tok::TokenKind::r_paren)) |
207 | | - removeVoidToken(VoidToken, Diagnostic); |
208 | | - else if (ProtoToken.is(tok::TokenKind::l_paren)) |
209 | | - State = TokenState::LeftParen; |
210 | | - break; |
211 | | - } |
212 | | - } |
213 | | - |
214 | | - if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren)) |
215 | | - removeVoidToken(VoidToken, Diagnostic); |
216 | | -} |
217 | | - |
218 | | -void RedundantVoidArgCheck::removeVoidToken(Token VoidToken, |
219 | | - StringRef Diagnostic) { |
220 | | - const SourceLocation VoidLoc = VoidToken.getLocation(); |
221 | | - diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc); |
222 | | -} |
223 | | - |
224 | | -void RedundantVoidArgCheck::processTypedefNameDecl( |
225 | | - const MatchFinder::MatchResult &Result, |
226 | | - const TypedefNameDecl *TypedefName) { |
227 | | - if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) |
228 | | - removeVoidArgumentTokens(Result, TypedefName->getSourceRange(), |
229 | | - isa<TypedefDecl>(TypedefName) ? "typedef" |
230 | | - : "type alias"); |
231 | | -} |
232 | | - |
233 | | -void RedundantVoidArgCheck::processFieldDecl( |
234 | | - const MatchFinder::MatchResult &Result, const FieldDecl *Member) { |
235 | | - if (protoTypeHasNoParms(Member->getType())) |
236 | | - removeVoidArgumentTokens(Result, Member->getSourceRange(), |
237 | | - "field declaration"); |
238 | | -} |
239 | | - |
240 | | -void RedundantVoidArgCheck::processVarDecl( |
241 | | - const MatchFinder::MatchResult &Result, const VarDecl *Var) { |
242 | | - if (protoTypeHasNoParms(Var->getType())) { |
243 | | - const SourceLocation Begin = Var->getBeginLoc(); |
244 | | - if (Var->hasInit()) { |
245 | | - const SourceLocation InitStart = |
246 | | - Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc()) |
247 | | - .getLocWithOffset(-1); |
248 | | - removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart), |
249 | | - "variable declaration with initializer"); |
250 | | - } else |
251 | | - removeVoidArgumentTokens(Result, Var->getSourceRange(), |
252 | | - "variable declaration"); |
253 | | - } |
254 | | -} |
255 | | - |
256 | | -void RedundantVoidArgCheck::processNamedCastExpr( |
257 | | - const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) { |
258 | | - if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) |
259 | | - removeVoidArgumentTokens( |
260 | | - Result, |
261 | | - NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(), |
262 | | - "named cast"); |
263 | | -} |
264 | | - |
265 | | -void RedundantVoidArgCheck::processExplicitCastExpr( |
266 | | - const MatchFinder::MatchResult &Result, |
267 | | - const ExplicitCastExpr *ExplicitCast) { |
268 | | - if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) |
269 | | - removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(), |
270 | | - "cast expression"); |
271 | | -} |
272 | | - |
273 | | -void RedundantVoidArgCheck::processLambdaExpr( |
274 | | - const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) { |
275 | | - if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 && |
276 | | - Lambda->hasExplicitParameters()) { |
277 | | - const SourceManager *SM = Result.SourceManager; |
278 | | - const TypeLoc TL = |
279 | | - Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc(); |
280 | | - removeVoidArgumentTokens(Result, |
281 | | - {SM->getSpellingLoc(TL.getBeginLoc()), |
282 | | - SM->getSpellingLoc(TL.getEndLoc())}, |
283 | | - "lambda expression"); |
284 | | - } |
| 31 | + const FunctionProtoTypeLoc TL = [&] { |
| 32 | + if (const auto *TL = Result.Nodes.getNodeAs<FunctionProtoTypeLoc>("fn")) |
| 33 | + return *TL; |
| 34 | + return Result.Nodes.getNodeAs<LambdaExpr>("fn") |
| 35 | + ->getCallOperator() |
| 36 | + ->getTypeSourceInfo() |
| 37 | + ->getTypeLoc() |
| 38 | + .getAs<FunctionProtoTypeLoc>(); |
| 39 | + }(); |
| 40 | + |
| 41 | + if (TL.getNumParams() != 0) |
| 42 | + return; |
| 43 | + |
| 44 | + const std::optional<Token> Tok = utils::lexer::findNextTokenSkippingComments( |
| 45 | + Result.SourceManager->getSpellingLoc(TL.getLParenLoc()), |
| 46 | + *Result.SourceManager, getLangOpts()); |
| 47 | + |
| 48 | + if (!Tok || Tok->isNot(tok::raw_identifier) || |
| 49 | + Tok->getRawIdentifier() != "void") |
| 50 | + return; |
| 51 | + |
| 52 | + diag(Tok->getLocation(), "redundant void argument list") |
| 53 | + << FixItHint::CreateRemoval(Tok->getLocation()); |
285 | 54 | } |
286 | 55 |
|
287 | 56 | } // namespace clang::tidy::modernize |
0 commit comments