23AST_MATCHER(Decl, isFromStdNamespaceOrSystemHeader) {
24 if (
const auto *D = Node.getDeclContext()->getEnclosingNamespaceContext())
25 if (D->isStdNamespace())
27 if (Node.getLocation().isInvalid())
29 return Node.getASTContext().getSourceManager().isInSystemHeader(
37 StrictMode(Options.get(
"StrictMode", false)),
38 IgnoreSingleArgument(Options.get(
"IgnoreSingleArgument", false)),
39 CommentAnonymousInitLists(
40 Options.get(
"CommentAnonymousInitLists", false)),
41 CommentBoolLiterals(Options.get(
"CommentBoolLiterals", false)),
42 CommentCharacterLiterals(Options.get(
"CommentCharacterLiterals", false)),
43 CommentFloatLiterals(Options.get(
"CommentFloatLiterals", false)),
44 CommentIntegerLiterals(Options.get(
"CommentIntegerLiterals", false)),
45 CommentNullPtrs(Options.get(
"CommentNullPtrs", false)),
46 CommentParenthesizedTemporaries(
47 Options.get(
"CommentParenthesizedTemporaries", false)),
48 CommentStringLiterals(Options.get(
"CommentStringLiterals", false)),
49 CommentTypedInitLists(Options.get(
"CommentTypedInitLists", false)),
50 CommentUserDefinedLiterals(
51 Options.get(
"CommentUserDefinedLiterals", false)),
52 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
55 Options.store(Opts,
"StrictMode", StrictMode);
56 Options.store(Opts,
"IgnoreSingleArgument", IgnoreSingleArgument);
57 Options.store(Opts,
"CommentAnonymousInitLists", CommentAnonymousInitLists);
58 Options.store(Opts,
"CommentBoolLiterals", CommentBoolLiterals);
59 Options.store(Opts,
"CommentCharacterLiterals", CommentCharacterLiterals);
60 Options.store(Opts,
"CommentFloatLiterals", CommentFloatLiterals);
61 Options.store(Opts,
"CommentIntegerLiterals", CommentIntegerLiterals);
62 Options.store(Opts,
"CommentNullPtrs", CommentNullPtrs);
63 Options.store(Opts,
"CommentParenthesizedTemporaries",
64 CommentParenthesizedTemporaries);
65 Options.store(Opts,
"CommentStringLiterals", CommentStringLiterals);
66 Options.store(Opts,
"CommentTypedInitLists", CommentTypedInitLists);
67 Options.store(Opts,
"CommentUserDefinedLiterals", CommentUserDefinedLiterals);
72 callExpr(unless(cxxOperatorCallExpr()), unless(userDefinedLiteral()),
76 unless(hasDeclaration(functionDecl(
77 hasAnyName(
"NewCallback",
"NewPermanentCallback")))),
82 unless(hasDeclaration(isFromStdNamespaceOrSystemHeader())))
85 Finder->addMatcher(cxxConstructExpr(unless(hasDeclaration(
86 isFromStdNamespaceOrSystemHeader())))
93 std::vector<CommentToken> Comments;
94 while (Loc.isValid()) {
96 Loc, Ctx->getSourceManager(), Ctx->getLangOpts(),
98 if (!Tok || Tok->isNot(tok::comment))
100 Loc = Tok->getLocation();
103 Lexer::getSourceText(CharSourceRange::getCharRange(
104 Loc, Loc.getLocWithOffset(Tok->getLength())),
105 Ctx->getSourceManager(), Ctx->getLangOpts()),
111template <
typename NamedDeclRange>
113 StringRef TargetName) {
114 const std::string ArgNameLowerStr =
ArgName.lower();
115 const StringRef ArgNameLower = ArgNameLowerStr;
117 const unsigned UpperBound = ((
ArgName.size() + 2) / 3) + 1;
118 const unsigned ThisED =
119 ArgNameLower.edit_distance(TargetName.lower(),
121 if (ThisED >= UpperBound)
124 return llvm::all_of(Candidates, [&](
const auto &Candidate) {
125 const IdentifierInfo *II = Candidate->getIdentifier();
130 if (II->getName() == TargetName)
133 const unsigned Threshold = 2;
137 const unsigned OtherED = ArgNameLower.edit_distance(
138 II->getName().lower(),
139 true, ThisED + Threshold);
140 return OtherED >= ThisED + Threshold;
144static bool sameName(StringRef InComment, StringRef InDecl,
bool StrictMode) {
146 return InComment == InDecl;
147 InComment = InComment.trim(
'_');
148 InDecl = InDecl.trim(
'_');
150 return InComment.compare_insensitive(InDecl) == 0;
154 return Expect !=
nullptr && Expect->getLocation().isMacroID() &&
155 Expect->getNameInfo().getName().isIdentifier() &&
156 Expect->getName().starts_with(
"gmock_");
159 const CXXMethodDecl *Expect) {
161 return Mock !=
nullptr && Mock->getNextDeclInContext() == Expect &&
162 Mock->getNumParams() == Expect->getNumParams() &&
163 Mock->getLocation().isMacroID() &&
164 Mock->getNameInfo().getName().isIdentifier() &&
165 Mock->getName() == Expect->getName().substr(strlen(
"gmock_"));
175 const DeclContext *Ctx = Method->getDeclContext();
176 if (Ctx ==
nullptr || !Ctx->isRecord())
178 for (
const auto *D : Ctx->decls()) {
179 if (D->getNextDeclInContext() == Method) {
180 const auto *Previous = dyn_cast<CXXMethodDecl>(D);
186 if (
const auto *Next =
187 dyn_cast_or_null<CXXMethodDecl>(Method->getNextDeclInContext())) {
199 if (
const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
203 if (MockedMethod->size_overridden_methods() > 0)
204 return *MockedMethod->begin_overridden_methods();
213enum class InitListKind {
222 Arg = Arg->IgnoreUnlessSpelledInSource();
224 if (
const auto *StdInit = dyn_cast<CXXStdInitializerListExpr>(Arg))
225 Arg = StdInit->getSubExpr()->IgnoreUnlessSpelledInSource();
227 if (isa<InitListExpr>(Arg))
228 return InitListKind::Anonymous;
230 if (
const auto *Ctor = dyn_cast<CXXConstructExpr>(Arg)) {
231 if (!Ctor->isListInitialization())
232 return InitListKind::None;
234 if (isa<CXXTemporaryObjectExpr>(Ctor))
235 return InitListKind::Typed;
238 return InitListKind::Anonymous;
243 if (
const auto *FuncCast = dyn_cast<CXXFunctionalCastExpr>(Arg)) {
244 const Expr *SubExpr = FuncCast->getSubExpr()->IgnoreImplicit();
245 if (FuncCast->isListInitialization() ||
246 isa<CXXStdInitializerListExpr>(SubExpr))
247 return InitListKind::Typed;
250 return InitListKind::None;
254 Arg = Arg->IgnoreUnlessSpelledInSource();
255 if (
const auto *TempObject = dyn_cast<CXXTemporaryObjectExpr>(Arg))
256 return !TempObject->isListInitialization();
259 const auto *FuncCast = dyn_cast<CXXFunctionalCastExpr>(Arg);
261 isa<CXXParenListInitExpr>(FuncCast->getSubExpr()->IgnoreImplicit());
266ArgumentCommentCheck::CommentKind
267ArgumentCommentCheck::shouldAddComment(
const Expr *Arg)
const {
273 Arg = Arg->IgnoreImplicit();
274 if (
const auto *UO = dyn_cast<UnaryOperator>(Arg))
275 Arg = UO->getSubExpr()->IgnoreImplicit();
276 if (Arg->getExprLoc().isMacroID())
277 return CommentKind::None;
279 if ((CommentAnonymousInitLists && Kind == InitListKind::Anonymous) ||
280 (CommentTypedInitLists && Kind == InitListKind::Typed) ||
281 (CommentParenthesizedTemporaries && IsParenthesizedTemporary)) {
282 return CommentKind::NonLiteral;
285 if ((CommentBoolLiterals && isa<CXXBoolLiteralExpr>(Arg)) ||
286 (CommentIntegerLiterals && isa<IntegerLiteral>(Arg)) ||
287 (CommentFloatLiterals && isa<FloatingLiteral>(Arg)) ||
288 (CommentUserDefinedLiterals && isa<UserDefinedLiteral>(Arg)) ||
289 (CommentCharacterLiterals && isa<CharacterLiteral>(Arg)) ||
290 (CommentStringLiterals && isa<StringLiteral>(Arg)) ||
291 (CommentNullPtrs && isa<CXXNullPtrLiteralExpr>(Arg))) {
292 return CommentKind::Literal;
295 return CommentKind::None;
298void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
299 const FunctionDecl *OriginalCallee,
300 SourceLocation ArgBeginLoc,
301 llvm::ArrayRef<const Expr *> Args) {
302 const FunctionDecl *Callee =
resolveMocks(OriginalCallee);
306 Callee = Callee->getFirstDecl();
307 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee);
308 Ctor && Ctor->isInheritingConstructor()) {
309 if (
const auto *BaseCtor = Ctor->getInheritedConstructor().getConstructor())
310 Callee = BaseCtor->getFirstDecl();
312 const unsigned NumArgs =
313 std::min<unsigned>(Args.size(), Callee->getNumParams());
314 if ((NumArgs == 0) || (IgnoreSingleArgument && NumArgs == 1))
317 auto MakeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
318 return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
319 Ctx->getSourceManager(),
323 for (
unsigned I = 0; I < NumArgs; ++I) {
324 const ParmVarDecl *PVD = Callee->getParamDecl(I);
325 const IdentifierInfo *II = PVD->getIdentifier();
328 if (FunctionDecl *Template = Callee->getTemplateInstantiationPattern()) {
333 if (Template->getNumParams() <= I ||
334 Template->getParamDecl(I)->isParameterPack()) {
339 const CharSourceRange BeforeArgument =
340 MakeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc());
341 ArgBeginLoc = Args[I]->getEndLoc();
343 std::vector<CommentToken> Comments;
344 if (BeforeArgument.isValid()) {
346 BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts());
349 const CharSourceRange ArgsRange =
350 MakeFileCharRange(Args[I]->getBeginLoc(), Args[I]->getEndLoc());
354 for (
const auto &Comment : Comments) {
355 SmallVector<StringRef, 2> Matches;
356 if (IdentRE.match(Comment.Text, &Matches) &&
357 !
sameName(Matches[2], II->getName(), StrictMode)) {
359 const DiagnosticBuilder Diag =
360 diag(Comment.Loc,
"argument name '%0' in comment does not "
361 "match parameter name %1")
363 if (
isLikelyTypo(Callee->parameters(), Matches[2], II->getName())) {
364 Diag << FixItHint::CreateReplacement(
366 llvm::Twine(Matches[1] + II->getName() + Matches[3]).str());
369 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note) << II;
370 if (OriginalCallee != Callee) {
371 diag(OriginalCallee->getLocation(),
372 "actual callee (%0) is declared here", DiagnosticIDs::Note)
380 const CommentKind Kind = shouldAddComment(Args[I]);
381 if (Comments.empty() && Kind != CommentKind::None) {
382 SmallString<32> ArgComment;
383 llvm::Twine(llvm::Twine(
"/*") + II->getName() +
"=*/")
384 .toStringRef(ArgComment);
385 const DiagnosticBuilder Diag =
386 diag(Args[I]->getBeginLoc(),
387 "argument comment missing for %select{literal argument|"
389 << (Kind == CommentKind::Literal ? 0 : 1) << II
390 << FixItHint::CreateInsertion(Args[I]->getBeginLoc(), ArgComment);
396 const auto *E = Result.Nodes.getNodeAs<Expr>(
"expr");
397 if (
const auto *Call = dyn_cast<CallExpr>(E)) {
398 const FunctionDecl *Callee = Call->getDirectCallee();
402 checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(),
403 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
405 const auto *Construct = cast<CXXConstructExpr>(E);
406 if (Construct->getNumArgs() > 0 &&
407 Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
412 Result.Context, Construct->getConstructor(),
413 Construct->getParenOrBraceRange().getBegin(),
414 llvm::ArrayRef(Construct->getArgs(), Construct->getNumArgs()));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.