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 StrictMode(Options.getLocalOrGlobal(
"StrictMode", 0) != 0),
26 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
34 callExpr(unless(cxxOperatorCallExpr()),
38 unless(hasDeclaration(functionDecl(
39 hasAnyName(
"NewCallback",
"NewPermanentCallback")))))
42 Finder->addMatcher(cxxConstructExpr().bind(
"expr"),
this);
45 static std::vector<std::pair<SourceLocation, StringRef>>
47 std::vector<std::pair<SourceLocation, StringRef>> Comments;
48 auto &
SM = Ctx->getSourceManager();
49 std::pair<FileID, unsigned> BeginLoc =
SM.getDecomposedLoc(Range.getBegin()),
50 EndLoc =
SM.getDecomposedLoc(Range.getEnd());
52 if (BeginLoc.first != EndLoc.first)
56 StringRef Buffer =
SM.getBufferData(BeginLoc.first, &Invalid);
60 const char *StrData = Buffer.data() + BeginLoc.second;
62 Lexer TheLexer(
SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
63 Buffer.begin(), StrData, Buffer.end());
64 TheLexer.SetCommentRetentionState(
true);
68 if (TheLexer.LexFromRawLexer(Tok))
70 if (Tok.getLocation() == Range.getEnd() || Tok.getKind() == tok::eof)
73 if (Tok.getKind() == tok::comment) {
74 std::pair<FileID, unsigned> CommentLoc =
75 SM.getDecomposedLoc(Tok.getLocation());
76 assert(CommentLoc.first == BeginLoc.first);
77 Comments.emplace_back(
79 StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
86 bool ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
87 StringRef ArgName,
unsigned ArgIndex) {
88 std::string ArgNameLowerStr = ArgName.lower();
89 StringRef ArgNameLower = ArgNameLowerStr;
91 unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
92 unsigned ThisED = ArgNameLower.edit_distance(
93 Params[ArgIndex]->getIdentifier()->getName().lower(),
95 if (ThisED >= UpperBound)
98 for (
unsigned I = 0, E = Params.size(); I != E; ++I) {
101 IdentifierInfo *II = Params[I]->getIdentifier();
105 const unsigned Threshold = 2;
109 unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
112 if (OtherED < ThisED + Threshold)
119 static bool sameName(StringRef InComment, StringRef InDecl,
bool StrictMode) {
121 return InComment == InDecl;
122 InComment = InComment.trim(
'_');
123 InDecl = InDecl.trim(
'_');
125 return InComment.compare_lower(InDecl) == 0;
128 void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
129 const FunctionDecl *Callee,
130 SourceLocation ArgBeginLoc,
131 llvm::ArrayRef<const Expr *> Args) {
132 Callee = Callee->getFirstDecl();
134 E = std::min<unsigned>(Args.size(), Callee->getNumParams());
136 const ParmVarDecl *PVD = Callee->getParamDecl(I);
137 IdentifierInfo *II = PVD->getIdentifier();
140 if (
auto Template = Callee->getTemplateInstantiationPattern()) {
144 if (Template->getNumParams() <= I ||
145 Template->getParamDecl(I)->isParameterPack()) {
150 CharSourceRange BeforeArgument = CharSourceRange::getCharRange(
151 I == 0 ? ArgBeginLoc : Args[I - 1]->getLocEnd(),
152 Args[I]->getLocStart());
153 BeforeArgument = Lexer::makeFileCharRange(
154 BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts());
157 llvm::SmallVector<StringRef, 2> Matches;
158 if (IdentRE.match(Comment.second, &Matches)) {
159 if (!
sameName(Matches[2], II->getName(), StrictMode)) {
161 DiagnosticBuilder Diag =
162 diag(Comment.first,
"argument name '%0' in comment does not "
163 "match parameter name %1")
165 if (isLikelyTypo(Callee->parameters(), Matches[2], I)) {
166 Diag << FixItHint::CreateReplacement(
168 (Matches[1] + II->getName() + Matches[3]).str());
171 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note)
180 const auto *E = Result.Nodes.getNodeAs<Expr>(
"expr");
181 if (
const auto *Call = dyn_cast<CallExpr>(E)) {
182 const FunctionDecl *Callee = Call->getDirectCallee();
186 checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
187 llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
189 const auto *Construct = cast<CXXConstructExpr>(E);
191 Result.Context, Construct->getConstructor(),
192 Construct->getParenOrBraceRange().getBegin(),
193 llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode)
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.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
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.