21AST_MATCHER(Decl, isFromStdNamespaceOrSystemHeader) {
22 if (
const auto *D = Node.getDeclContext()->getEnclosingNamespaceContext())
23 if (D->isStdNamespace())
25 if (Node.getLocation().isInvalid())
27 return Node.getASTContext().getSourceManager().isInSystemHeader(
35 StrictMode(Options.get(
"StrictMode", false)),
36 IgnoreSingleArgument(Options.get(
"IgnoreSingleArgument", false)),
37 CommentBoolLiterals(Options.get(
"CommentBoolLiterals", false)),
38 CommentIntegerLiterals(Options.get(
"CommentIntegerLiterals", false)),
39 CommentFloatLiterals(Options.get(
"CommentFloatLiterals", false)),
40 CommentStringLiterals(Options.get(
"CommentStringLiterals", false)),
41 CommentUserDefinedLiterals(
42 Options.get(
"CommentUserDefinedLiterals", false)),
43 CommentCharacterLiterals(Options.get(
"CommentCharacterLiterals", false)),
44 CommentNullPtrs(Options.get(
"CommentNullPtrs", false)),
45 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
48 Options.store(Opts,
"StrictMode", StrictMode);
49 Options.store(Opts,
"IgnoreSingleArgument", IgnoreSingleArgument);
50 Options.store(Opts,
"CommentBoolLiterals", CommentBoolLiterals);
51 Options.store(Opts,
"CommentIntegerLiterals", CommentIntegerLiterals);
52 Options.store(Opts,
"CommentFloatLiterals", CommentFloatLiterals);
53 Options.store(Opts,
"CommentStringLiterals", CommentStringLiterals);
54 Options.store(Opts,
"CommentUserDefinedLiterals", CommentUserDefinedLiterals);
55 Options.store(Opts,
"CommentCharacterLiterals", CommentCharacterLiterals);
56 Options.store(Opts,
"CommentNullPtrs", CommentNullPtrs);
61 callExpr(unless(cxxOperatorCallExpr()), unless(userDefinedLiteral()),
65 unless(hasDeclaration(functionDecl(
66 hasAnyName(
"NewCallback",
"NewPermanentCallback")))),
71 unless(hasDeclaration(isFromStdNamespaceOrSystemHeader())))
74 Finder->addMatcher(cxxConstructExpr(unless(hasDeclaration(
75 isFromStdNamespaceOrSystemHeader())))
80static std::vector<std::pair<SourceLocation, StringRef>>
82 std::vector<std::pair<SourceLocation, StringRef>> Comments;
83 auto &SM = Ctx->getSourceManager();
84 const std::pair<FileID, unsigned> BeginLoc =
85 SM.getDecomposedLoc(Range.getBegin()),
87 SM.getDecomposedLoc(Range.getEnd());
89 if (BeginLoc.first != EndLoc.first)
93 const StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
97 const char *StrData = Buffer.data() + BeginLoc.second;
99 Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
100 Buffer.begin(), StrData, Buffer.end());
101 TheLexer.SetCommentRetentionState(
true);
105 if (TheLexer.LexFromRawLexer(Tok))
107 if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
110 if (Tok.is(tok::comment)) {
111 const std::pair<FileID, unsigned> CommentLoc =
112 SM.getDecomposedLoc(Tok.getLocation());
113 assert(CommentLoc.first == BeginLoc.first);
114 Comments.emplace_back(
116 StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
126static std::vector<std::pair<SourceLocation, StringRef>>
128 std::vector<std::pair<SourceLocation, StringRef>> Comments;
129 while (Loc.isValid()) {
131 Loc, Ctx->getSourceManager(), Ctx->getLangOpts(),
133 if (Tok.isNot(tok::comment))
135 Loc = Tok.getLocation();
136 Comments.emplace_back(
138 Lexer::getSourceText(CharSourceRange::getCharRange(
139 Loc, Loc.getLocWithOffset(Tok.getLength())),
140 Ctx->getSourceManager(), Ctx->getLangOpts()));
146 StringRef
ArgName,
unsigned ArgIndex) {
147 const std::string ArgNameLowerStr =
ArgName.lower();
148 const StringRef ArgNameLower = ArgNameLowerStr;
150 const unsigned UpperBound = ((
ArgName.size() + 2) / 3) + 1;
151 const unsigned ThisED = ArgNameLower.edit_distance(
152 Params[ArgIndex]->getIdentifier()->
getName().lower(),
154 if (ThisED >= UpperBound)
157 for (
unsigned I = 0, E = Params.size(); I != E; ++I) {
160 const IdentifierInfo *II = Params[I]->getIdentifier();
164 const unsigned Threshold = 2;
168 const unsigned OtherED = ArgNameLower.edit_distance(
169 II->getName().lower(),
170 true, ThisED + Threshold);
171 if (OtherED < ThisED + Threshold)
178static bool sameName(StringRef InComment, StringRef InDecl,
bool StrictMode) {
180 return InComment == InDecl;
181 InComment = InComment.trim(
'_');
182 InDecl = InDecl.trim(
'_');
184 return InComment.compare_insensitive(InDecl) == 0;
188 return Expect !=
nullptr && Expect->getLocation().isMacroID() &&
189 Expect->getNameInfo().getName().isIdentifier() &&
190 Expect->getName().starts_with(
"gmock_");
193 const CXXMethodDecl *Expect) {
195 return Mock !=
nullptr && Mock->getNextDeclInContext() == Expect &&
196 Mock->getNumParams() == Expect->getNumParams() &&
197 Mock->getLocation().isMacroID() &&
198 Mock->getNameInfo().getName().isIdentifier() &&
199 Mock->getName() == Expect->getName().substr(strlen(
"gmock_"));
209 const DeclContext *Ctx = Method->getDeclContext();
210 if (Ctx ==
nullptr || !Ctx->isRecord())
212 for (
const auto *D : Ctx->decls()) {
213 if (D->getNextDeclInContext() == Method) {
214 const auto *Previous = dyn_cast<CXXMethodDecl>(D);
220 if (
const auto *Next =
221 dyn_cast_or_null<CXXMethodDecl>(Method->getNextDeclInContext())) {
233 if (
const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
237 if (MockedMethod->size_overridden_methods() > 0) {
238 return *MockedMethod->begin_overridden_methods();
248bool ArgumentCommentCheck::shouldAddComment(
const Expr *Arg)
const {
249 Arg = Arg->IgnoreImpCasts();
250 if (isa<UnaryOperator>(Arg))
251 Arg = cast<UnaryOperator>(Arg)->getSubExpr();
252 if (Arg->getExprLoc().isMacroID())
254 return (CommentBoolLiterals && isa<CXXBoolLiteralExpr>(Arg)) ||
255 (CommentIntegerLiterals && isa<IntegerLiteral>(Arg)) ||
256 (CommentFloatLiterals && isa<FloatingLiteral>(Arg)) ||
257 (CommentUserDefinedLiterals && isa<UserDefinedLiteral>(Arg)) ||
258 (CommentCharacterLiterals && isa<CharacterLiteral>(Arg)) ||
259 (CommentStringLiterals && isa<StringLiteral>(Arg)) ||
260 (CommentNullPtrs && isa<CXXNullPtrLiteralExpr>(Arg));
263void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
264 const FunctionDecl *OriginalCallee,
265 SourceLocation ArgBeginLoc,
266 llvm::ArrayRef<const Expr *> Args) {
267 const FunctionDecl *Callee =
resolveMocks(OriginalCallee);
271 Callee = Callee->getFirstDecl();
272 const unsigned NumArgs =
273 std::min<unsigned>(Args.size(), Callee->getNumParams());
274 if ((NumArgs == 0) || (IgnoreSingleArgument && NumArgs == 1))
277 auto MakeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
278 return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
279 Ctx->getSourceManager(),
283 for (
unsigned I = 0; I < NumArgs; ++I) {
284 const ParmVarDecl *PVD = Callee->getParamDecl(I);
285 const IdentifierInfo *II = PVD->getIdentifier();
288 if (FunctionDecl *Template = Callee->getTemplateInstantiationPattern()) {
293 if (Template->getNumParams() <= I ||
294 Template->getParamDecl(I)->isParameterPack()) {
299 const CharSourceRange BeforeArgument =
300 MakeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc());
301 ArgBeginLoc = Args[I]->getEndLoc();
303 std::vector<std::pair<SourceLocation, StringRef>> Comments;
304 if (BeforeArgument.isValid()) {
308 const CharSourceRange ArgsRange =
309 MakeFileCharRange(Args[I]->getBeginLoc(), Args[I]->getEndLoc());
313 for (
auto Comment : Comments) {
314 llvm::SmallVector<StringRef, 2> Matches;
315 if (IdentRE.match(Comment.second, &Matches) &&
316 !
sameName(Matches[2], II->getName(), StrictMode)) {
318 const DiagnosticBuilder Diag =
319 diag(Comment.first,
"argument name '%0' in comment does not "
320 "match parameter name %1")
322 if (
isLikelyTypo(Callee->parameters(), Matches[2], I)) {
323 Diag << FixItHint::CreateReplacement(
324 Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
327 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note) << II;
328 if (OriginalCallee != Callee) {
329 diag(OriginalCallee->getLocation(),
330 "actual callee (%0) is declared here", DiagnosticIDs::Note)
337 if (Comments.empty() && shouldAddComment(Args[I])) {
338 const std::string ArgComment =
339 (llvm::Twine(
"/*") + II->getName() +
"=*/").str();
340 const DiagnosticBuilder Diag =
341 diag(Args[I]->getBeginLoc(),
342 "argument comment missing for literal argument %0")
344 << FixItHint::CreateInsertion(Args[I]->getBeginLoc(), ArgComment);
350 const auto *E = Result.Nodes.getNodeAs<Expr>(
"expr");
351 if (
const auto *Call = dyn_cast<CallExpr>(E)) {
352 const FunctionDecl *Callee = Call->getDirectCallee();
356 checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(),
357 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
359 const auto *Construct = cast<CXXConstructExpr>(E);
360 if (Construct->getNumArgs() > 0 &&
361 Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
366 Result.Context, Construct->getConstructor(),
367 Construct->getParenOrBraceRange().getBegin(),
368 llvm::ArrayRef(Construct->getArgs(), Construct->getNumArgs()));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.