10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/Lexer.h"
20bool protoTypeHasNoParms(QualType QT) {
21 if (
const auto *PT = QT->getAs<PointerType>())
22 QT = PT->getPointeeType();
23 if (
auto *MPT = QT->getAs<MemberPointerType>())
24 QT = MPT->getPointeeType();
25 if (
const auto *FP = QT->getAs<FunctionProtoType>())
26 return FP->getNumParams() == 0;
30const char FunctionId[] =
"function";
31const char TypedefId[] =
"typedef";
33const char VarId[] =
"var";
34const char NamedCastId[] =
"named-cast";
35const char CStyleCastId[] =
"c-style-cast";
36const char ExplicitCastId[] =
"explicit-cast";
37const char LambdaId[] =
"lambda";
42 Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
43 unless(isInstantiated()), unless(isExternC()))
46 Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId),
48 auto ParenFunctionType = parenType(innerType(functionType()));
49 auto PointerToFunctionType = pointee(ParenFunctionType);
50 auto FunctionOrMemberPointer =
51 anyOf(hasType(pointerType(PointerToFunctionType)),
52 hasType(memberPointerType(PointerToFunctionType)));
53 Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId),
this);
54 Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId),
this);
55 auto CastDestinationIsFunction =
56 hasDestinationType(pointsTo(ParenFunctionType));
58 cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId),
this);
60 cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
62 cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
65 cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
66 Finder->addMatcher(lambdaExpr().bind(LambdaId),
this);
70 const BoundNodes &Nodes = Result.Nodes;
71 if (
const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId))
72 processFunctionDecl(Result, Function);
74 Nodes.getNodeAs<TypedefNameDecl>(TypedefId))
76 else if (
const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId))
77 processFieldDecl(Result, Member);
78 else if (
const auto *Var = Nodes.getNodeAs<VarDecl>(VarId))
79 processVarDecl(Result, Var);
80 else if (
const auto *NamedCast =
81 Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId))
82 processNamedCastExpr(Result, NamedCast);
83 else if (
const auto *CStyleCast =
84 Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId))
85 processExplicitCastExpr(Result, CStyleCast);
86 else if (
const auto *ExplicitCast =
87 Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId))
88 processExplicitCastExpr(Result, ExplicitCast);
89 else if (
const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId))
90 processLambdaExpr(Result, Lambda);
93void RedundantVoidArgCheck::processFunctionDecl(
94 const MatchFinder::MatchResult &Result,
const FunctionDecl *Function) {
95 const auto *Method = dyn_cast<CXXMethodDecl>(Function);
96 SourceLocation Start = Method && Method->getParent()->isLambda()
97 ? Method->getBeginLoc()
98 : Function->getLocation();
99 SourceLocation End = Function->getEndLoc();
100 if (Function->isThisDeclarationADefinition()) {
101 if (
const Stmt *Body = Function->getBody()) {
102 End = Body->getBeginLoc();
103 if (End.isMacroID() &&
104 Result.SourceManager->isAtStartOfImmediateMacroExpansion(End))
105 End = Result.SourceManager->getExpansionLoc(End);
106 End = End.getLocWithOffset(-1);
108 removeVoidArgumentTokens(Result, SourceRange(Start, End),
109 "function definition");
111 removeVoidArgumentTokens(Result, SourceRange(Start, End),
112 "function declaration");
116 if (!ProtoToken.is(tok::TokenKind::raw_identifier))
119 IdentifierTable::iterator It = Idents.find(ProtoToken.getRawIdentifier());
120 if (It == Idents.end())
123 return It->second->hadMacroDefinition();
126void RedundantVoidArgCheck::removeVoidArgumentTokens(
127 const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
128 StringRef GrammarLocation) {
129 CharSourceRange CharRange =
130 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
133 std::string DeclText =
134 Lexer::getSourceText(CharRange, *Result.SourceManager,
getLangOpts())
136 Lexer PrototypeLexer(CharRange.getBegin(),
getLangOpts(), DeclText.data(),
137 DeclText.data(), DeclText.data() + DeclText.size());
138 enum class TokenState {
146 TokenState State = TokenState::Start;
149 const IdentifierTable &Idents = Result.Context->Idents;
152 (
"redundant void argument list in " + GrammarLocation).str();
154 while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
156 case TokenState::Start:
157 if (ProtoToken.is(tok::TokenKind::l_paren))
158 State = TokenState::LeftParen;
160 State = TokenState::MacroId;
162 case TokenState::MacroId:
163 if (ProtoToken.is(tok::TokenKind::l_paren))
164 State = TokenState::MacroLeftParen;
166 State = TokenState::Start;
168 case TokenState::MacroLeftParen:
170 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
172 State = TokenState::MacroId;
174 State = TokenState::MacroArguments;
175 }
else if (ProtoToken.is(tok::TokenKind::r_paren)) {
178 State = TokenState::Start;
180 State = TokenState::MacroId;
182 State = TokenState::MacroArguments;
184 case TokenState::MacroArguments:
186 State = TokenState::MacroLeftParen;
187 else if (ProtoToken.is(tok::TokenKind::r_paren)) {
190 State = TokenState::Start;
193 case TokenState::LeftParen:
194 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
196 State = TokenState::MacroId;
197 else if (ProtoToken.getRawIdentifier() ==
"void") {
198 State = TokenState::Void;
199 VoidToken = ProtoToken;
201 }
else if (ProtoToken.is(tok::TokenKind::l_paren))
202 State = TokenState::LeftParen;
204 State = TokenState::Start;
206 case TokenState::Void:
207 State = TokenState::Start;
208 if (ProtoToken.is(tok::TokenKind::r_paren))
210 else if (ProtoToken.is(tok::TokenKind::l_paren))
211 State = TokenState::LeftParen;
216 if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
220void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
222 SourceLocation VoidLoc = VoidToken.getLocation();
226void RedundantVoidArgCheck::processTypedefNameDecl(
227 const MatchFinder::MatchResult &Result,
229 if (protoTypeHasNoParms(
TypedefName->getUnderlyingType()))
230 removeVoidArgumentTokens(Result,
TypedefName->getSourceRange(),
235void RedundantVoidArgCheck::processFieldDecl(
236 const MatchFinder::MatchResult &Result,
const FieldDecl *Member) {
237 if (protoTypeHasNoParms(Member->getType()))
238 removeVoidArgumentTokens(Result, Member->getSourceRange(),
239 "field declaration");
242void RedundantVoidArgCheck::processVarDecl(
243 const MatchFinder::MatchResult &Result,
const VarDecl *Var) {
244 if (protoTypeHasNoParms(Var->getType())) {
245 SourceLocation Begin = Var->getBeginLoc();
246 if (Var->hasInit()) {
247 SourceLocation InitStart =
248 Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
249 .getLocWithOffset(-1);
250 removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
251 "variable declaration with initializer");
253 removeVoidArgumentTokens(Result, Var->getSourceRange(),
254 "variable declaration");
258void RedundantVoidArgCheck::processNamedCastExpr(
259 const MatchFinder::MatchResult &Result,
const CXXNamedCastExpr *NamedCast) {
260 if (protoTypeHasNoParms(NamedCast->getTypeAsWritten()))
261 removeVoidArgumentTokens(
263 NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
267void RedundantVoidArgCheck::processExplicitCastExpr(
268 const MatchFinder::MatchResult &Result,
269 const ExplicitCastExpr *ExplicitCast) {
270 if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten()))
271 removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
275void RedundantVoidArgCheck::processLambdaExpr(
276 const MatchFinder::MatchResult &Result,
const LambdaExpr *Lambda) {
277 if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
278 Lambda->hasExplicitParameters()) {
279 SourceManager *SM = Result.SourceManager;
280 TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
281 removeVoidArgumentTokens(Result,
282 {SM->getSpellingLoc(TL.getBeginLoc()),
283 SM->getSpellingLoc(TL.getEndLoc())},
284 "lambda expression");
DiagnosticCallback Diagnostic
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
bool isMacroIdentifier(const IdentifierTable &Idents, const Token &ProtoToken)
static constexpr llvm::StringLiteral TypedefName