11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Lex/Token.h"
16 using namespace clang::ast_matchers;
22 ArgumentCommentCheck::ArgumentCommentCheck(StringRef
Name,
25 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
29 callExpr(unless(cxxOperatorCallExpr()),
33 unless(hasDeclaration(functionDecl(anyOf(
34 hasName(
"NewCallback"), hasName(
"NewPermanentCallback"))))))
37 Finder->addMatcher(cxxConstructExpr().bind(
"expr"),
this);
40 static std::vector<std::pair<SourceLocation, StringRef>>
42 std::vector<std::pair<SourceLocation, StringRef>> Comments;
43 auto &
SM = Ctx->getSourceManager();
44 std::pair<FileID, unsigned> BeginLoc =
SM.getDecomposedLoc(Range.getBegin()),
45 EndLoc =
SM.getDecomposedLoc(Range.getEnd());
47 if (BeginLoc.first != EndLoc.first)
51 StringRef Buffer =
SM.getBufferData(BeginLoc.first, &Invalid);
55 const char *StrData = Buffer.data() + BeginLoc.second;
57 Lexer TheLexer(
SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
58 Buffer.begin(), StrData, Buffer.end());
59 TheLexer.SetCommentRetentionState(
true);
63 if (TheLexer.LexFromRawLexer(Tok))
65 if (Tok.getLocation() == Range.getEnd() || Tok.getKind() == tok::eof)
68 if (Tok.getKind() == tok::comment) {
69 std::pair<FileID, unsigned> CommentLoc =
70 SM.getDecomposedLoc(Tok.getLocation());
71 assert(CommentLoc.first == BeginLoc.first);
72 Comments.emplace_back(
74 StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
82 ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
83 StringRef ArgName,
unsigned ArgIndex) {
84 std::string ArgNameLower = ArgName.lower();
85 unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
86 unsigned ThisED = StringRef(ArgNameLower).edit_distance(
87 Params[ArgIndex]->getIdentifier()->getName().lower(),
89 if (ThisED >= UpperBound)
92 for (
unsigned I = 0, E = Params.size(); I != E; ++I) {
95 IdentifierInfo *II = Params[I]->getIdentifier();
99 const unsigned Threshold = 2;
103 unsigned OtherED = StringRef(ArgNameLower).edit_distance(
104 II->getName().lower(),
105 true, ThisED + Threshold);
106 if (OtherED < ThisED + Threshold)
113 void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
114 const FunctionDecl *Callee,
115 SourceLocation ArgBeginLoc,
116 llvm::ArrayRef<const Expr *> Args) {
117 Callee = Callee->getFirstDecl();
119 e = std::min<unsigned>(Args.size(), Callee->getNumParams());
121 const ParmVarDecl *PVD = Callee->getParamDecl(i);
122 IdentifierInfo *II = PVD->getIdentifier();
125 if (
auto Template = Callee->getTemplateInstantiationPattern()) {
129 if (Template->getNumParams() <= i ||
130 Template->getParamDecl(i)->isParameterPack()) {
135 CharSourceRange BeforeArgument = CharSourceRange::getCharRange(
136 i == 0 ? ArgBeginLoc : Args[i - 1]->getLocEnd(),
137 Args[i]->getLocStart());
138 BeforeArgument = Lexer::makeFileCharRange(
139 BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts());
142 llvm::SmallVector<StringRef, 2> Matches;
143 if (IdentRE.match(Comment.second, &Matches)) {
144 if (Matches[2] != II->getName()) {
146 DiagnosticBuilder Diag =
147 diag(Comment.first,
"argument name '%0' in comment does not "
148 "match parameter name %1")
150 if (isLikelyTypo(Callee->parameters(), Matches[2], i)) {
151 Diag << FixItHint::CreateReplacement(
153 (Matches[1] + II->getName() + Matches[3]).str());
156 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note)
165 const Expr *E = Result.Nodes.getNodeAs<Expr>(
"expr");
166 if (
auto Call = dyn_cast<CallExpr>(E)) {
167 const FunctionDecl *Callee = Call->getDirectCallee();
171 checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
172 llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
174 auto Construct = cast<CXXConstructExpr>(E);
176 Result.Context, Construct->getConstructor(),
177 Construct->getParenOrBraceRange().getBegin(),
178 llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
std::unique_ptr< ast_matchers::MatchFinder > Finder
static std::vector< std::pair< SourceLocation, StringRef > > getCommentsInRange(ASTContext *Ctx, CharSourceRange Range)
Base class for all clang-tidy checks.
CharSourceRange Range
SourceRange for the file name.
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.