11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Lex/Lexer.h"
14 using namespace clang::ast_matchers;
23 bool protoTypeHasNoParms(QualType QT) {
24 if (
auto PT = QT->getAs<PointerType>()) {
25 QT = PT->getPointeeType();
27 if (
auto *MPT = QT->getAs<MemberPointerType>()) {
28 QT = MPT->getPointeeType();
30 if (
auto FP = QT->getAs<FunctionProtoType>()) {
31 return FP->getNumParams() == 0;
36 const char FunctionId[] =
"function";
37 const char TypedefId[] =
"typedef";
38 const char FieldId[] =
"field";
39 const char VarId[] =
"var";
40 const char NamedCastId[] =
"named-cast";
41 const char CStyleCastId[] =
"c-style-cast";
42 const char ExplicitCastId[] =
"explicit-cast";
43 const char LambdaId[] =
"lambda";
47 void RedundantVoidArgCheck::registerMatchers(MatchFinder *
Finder) {
48 Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
52 Finder->addMatcher(typedefNameDecl().bind(TypedefId),
this);
53 auto ParenFunctionType = parenType(innerType(functionType()));
54 auto PointerToFunctionType = pointee(ParenFunctionType);
55 auto FunctionOrMemberPointer =
56 anyOf(hasType(pointerType(PointerToFunctionType)),
57 hasType(memberPointerType(PointerToFunctionType)));
58 Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId),
this);
59 Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId),
this);
60 auto CastDestinationIsFunction =
61 hasDestinationType(pointsTo(ParenFunctionType));
63 cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId),
this);
65 cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
67 cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
70 cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId),
this);
71 Finder->addMatcher(lambdaExpr().bind(LambdaId),
this);
74 void RedundantVoidArgCheck::check(
const MatchFinder::MatchResult &
Result) {
75 if (!Result.Context->getLangOpts().CPlusPlus) {
79 const BoundNodes &Nodes = Result.Nodes;
80 if (
const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
81 processFunctionDecl(Result, Function);
82 }
else if (
const auto *TypedefName =
83 Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) {
84 processTypedefNameDecl(Result, TypedefName);
85 }
else if (
const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) {
86 processFieldDecl(Result, Member);
87 }
else if (
const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) {
88 processVarDecl(Result, Var);
89 }
else if (
const auto *NamedCast =
90 Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) {
91 processNamedCastExpr(Result, NamedCast);
92 }
else if (
const auto *CStyleCast =
93 Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) {
94 processExplicitCastExpr(Result, CStyleCast);
95 }
else if (
const auto *ExplicitCast =
96 Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) {
97 processExplicitCastExpr(Result, ExplicitCast);
98 }
else if (
const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) {
99 processLambdaExpr(Result, Lambda);
103 void RedundantVoidArgCheck::processFunctionDecl(
104 const MatchFinder::MatchResult &
Result,
const FunctionDecl *Function) {
105 if (Function->isThisDeclarationADefinition()) {
106 const Stmt *Body = Function->getBody();
107 SourceLocation Start = Function->getLocStart();
109 Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd();
110 removeVoidArgumentTokens(Result, SourceRange(Start, End),
111 "function definition");
113 removeVoidArgumentTokens(Result, Function->getSourceRange(),
114 "function declaration");
118 void RedundantVoidArgCheck::removeVoidArgumentTokens(
119 const ast_matchers::MatchFinder::MatchResult &Result, SourceRange
Range,
120 StringRef GrammarLocation) {
121 CharSourceRange CharRange = Lexer::makeFileCharRange(
122 CharSourceRange::getTokenRange(Range), *Result.SourceManager,
123 Result.Context->getLangOpts());
125 std::string DeclText = Lexer::getSourceText(CharRange, *Result.SourceManager,
126 Result.Context->getLangOpts())
128 Lexer PrototypeLexer(CharRange.getBegin(), Result.Context->getLangOpts(),
129 DeclText.data(), DeclText.data(),
130 DeclText.data() + DeclText.size());
136 TokenState State = NothingYet;
139 std::string Diagnostic =
140 (
"redundant void argument list in " + GrammarLocation).str();
142 while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
145 if (ProtoToken.is(tok::TokenKind::l_paren)) {
146 State = SawLeftParen;
150 if (ProtoToken.is(tok::TokenKind::raw_identifier) &&
151 ProtoToken.getRawIdentifier() ==
"void") {
153 VoidToken = ProtoToken;
160 if (ProtoToken.is(tok::TokenKind::r_paren)) {
161 removeVoidToken(VoidToken, Diagnostic);
162 }
else if (ProtoToken.is(tok::TokenKind::l_paren)) {
163 State = SawLeftParen;
169 if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) {
170 removeVoidToken(VoidToken, Diagnostic);
174 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
175 StringRef Diagnostic) {
176 SourceLocation VoidLoc(VoidToken.getLocation());
178 CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3));
179 diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange);
182 void RedundantVoidArgCheck::processTypedefNameDecl(
183 const MatchFinder::MatchResult &Result,
184 const TypedefNameDecl *TypedefName) {
185 if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) {
186 removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
187 isa<TypedefDecl>(TypedefName) ?
"typedef"
192 void RedundantVoidArgCheck::processFieldDecl(
193 const MatchFinder::MatchResult &Result,
const FieldDecl *Member) {
194 if (protoTypeHasNoParms(Member->getType())) {
195 removeVoidArgumentTokens(Result, Member->getSourceRange(),
196 "field declaration");
200 void RedundantVoidArgCheck::processVarDecl(
201 const MatchFinder::MatchResult &Result,
const VarDecl *Var) {
202 if (protoTypeHasNoParms(Var->getType())) {
203 SourceLocation Begin = Var->getLocStart();
204 if (Var->hasInit()) {
205 SourceLocation InitStart =
206 Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart())
207 .getLocWithOffset(-1);
208 removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
209 "variable declaration with initializer");
211 removeVoidArgumentTokens(Result, Var->getSourceRange(),
212 "variable declaration");
217 void RedundantVoidArgCheck::processNamedCastExpr(
218 const MatchFinder::MatchResult &Result,
const CXXNamedCastExpr *NamedCast) {
219 if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) {
220 removeVoidArgumentTokens(
222 NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
227 void RedundantVoidArgCheck::processExplicitCastExpr(
228 const MatchFinder::MatchResult &Result,
229 const ExplicitCastExpr *ExplicitCast) {
230 if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) {
231 removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
236 void RedundantVoidArgCheck::processLambdaExpr(
237 const MatchFinder::MatchResult &Result,
const LambdaExpr *Lambda) {
238 if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
239 Lambda->hasExplicitParameters()) {
240 SourceLocation Begin =
241 Lambda->getIntroducerRange().getEnd().getLocWithOffset(1);
242 SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1);
243 removeVoidArgumentTokens(Result, SourceRange(Begin, End),
244 "lambda expression");
std::unique_ptr< ast_matchers::MatchFinder > Finder
CharSourceRange Range
SourceRange for the file name.