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 CommentBoolLiterals(Options.get(
"CommentBoolLiterals", false)),
40 CommentIntegerLiterals(Options.get(
"CommentIntegerLiterals", false)),
41 CommentFloatLiterals(Options.get(
"CommentFloatLiterals", false)),
42 CommentStringLiterals(Options.get(
"CommentStringLiterals", false)),
43 CommentUserDefinedLiterals(
44 Options.get(
"CommentUserDefinedLiterals", false)),
45 CommentCharacterLiterals(Options.get(
"CommentCharacterLiterals", false)),
46 CommentNullPtrs(Options.get(
"CommentNullPtrs", false)),
47 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
50 Options.store(Opts,
"StrictMode", StrictMode);
51 Options.store(Opts,
"IgnoreSingleArgument", IgnoreSingleArgument);
52 Options.store(Opts,
"CommentBoolLiterals", CommentBoolLiterals);
53 Options.store(Opts,
"CommentIntegerLiterals", CommentIntegerLiterals);
54 Options.store(Opts,
"CommentFloatLiterals", CommentFloatLiterals);
55 Options.store(Opts,
"CommentStringLiterals", CommentStringLiterals);
56 Options.store(Opts,
"CommentUserDefinedLiterals", CommentUserDefinedLiterals);
57 Options.store(Opts,
"CommentCharacterLiterals", CommentCharacterLiterals);
58 Options.store(Opts,
"CommentNullPtrs", CommentNullPtrs);
63 callExpr(unless(cxxOperatorCallExpr()), unless(userDefinedLiteral()),
67 unless(hasDeclaration(functionDecl(
68 hasAnyName(
"NewCallback",
"NewPermanentCallback")))),
73 unless(hasDeclaration(isFromStdNamespaceOrSystemHeader())))
76 Finder->addMatcher(cxxConstructExpr(unless(hasDeclaration(
77 isFromStdNamespaceOrSystemHeader())))
84 std::vector<CommentToken> Comments;
85 while (Loc.isValid()) {
87 Loc, Ctx->getSourceManager(), Ctx->getLangOpts(),
89 if (!Tok || Tok->isNot(tok::comment))
91 Loc = Tok->getLocation();
94 Lexer::getSourceText(CharSourceRange::getCharRange(
95 Loc, Loc.getLocWithOffset(Tok->getLength())),
96 Ctx->getSourceManager(), Ctx->getLangOpts()),
102template <
typename NamedDeclRange>
104 StringRef TargetName) {
105 const std::string ArgNameLowerStr =
ArgName.lower();
106 const StringRef ArgNameLower = ArgNameLowerStr;
108 const unsigned UpperBound = ((
ArgName.size() + 2) / 3) + 1;
109 const unsigned ThisED =
110 ArgNameLower.edit_distance(TargetName.lower(),
112 if (ThisED >= UpperBound)
115 return llvm::all_of(Candidates, [&](
const auto &Candidate) {
116 const IdentifierInfo *II = Candidate->getIdentifier();
121 if (II->getName() == TargetName)
124 const unsigned Threshold = 2;
128 const unsigned OtherED = ArgNameLower.edit_distance(
129 II->getName().lower(),
130 true, ThisED + Threshold);
131 return OtherED >= ThisED + Threshold;
135static bool sameName(StringRef InComment, StringRef InDecl,
bool StrictMode) {
137 return InComment == InDecl;
138 InComment = InComment.trim(
'_');
139 InDecl = InDecl.trim(
'_');
141 return InComment.compare_insensitive(InDecl) == 0;
145 return Expect !=
nullptr && Expect->getLocation().isMacroID() &&
146 Expect->getNameInfo().getName().isIdentifier() &&
147 Expect->getName().starts_with(
"gmock_");
150 const CXXMethodDecl *Expect) {
152 return Mock !=
nullptr && Mock->getNextDeclInContext() == Expect &&
153 Mock->getNumParams() == Expect->getNumParams() &&
154 Mock->getLocation().isMacroID() &&
155 Mock->getNameInfo().getName().isIdentifier() &&
156 Mock->getName() == Expect->getName().substr(strlen(
"gmock_"));
166 const DeclContext *Ctx = Method->getDeclContext();
167 if (Ctx ==
nullptr || !Ctx->isRecord())
169 for (
const auto *D : Ctx->decls()) {
170 if (D->getNextDeclInContext() == Method) {
171 const auto *Previous = dyn_cast<CXXMethodDecl>(D);
177 if (
const auto *Next =
178 dyn_cast_or_null<CXXMethodDecl>(Method->getNextDeclInContext())) {
190 if (
const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
194 if (MockedMethod->size_overridden_methods() > 0)
195 return *MockedMethod->begin_overridden_methods();
204bool ArgumentCommentCheck::shouldAddComment(
const Expr *Arg)
const {
205 Arg = Arg->IgnoreImpCasts();
206 if (isa<UnaryOperator>(Arg))
207 Arg = cast<UnaryOperator>(Arg)->getSubExpr();
208 if (Arg->getExprLoc().isMacroID())
210 return (CommentBoolLiterals && isa<CXXBoolLiteralExpr>(Arg)) ||
211 (CommentIntegerLiterals && isa<IntegerLiteral>(Arg)) ||
212 (CommentFloatLiterals && isa<FloatingLiteral>(Arg)) ||
213 (CommentUserDefinedLiterals && isa<UserDefinedLiteral>(Arg)) ||
214 (CommentCharacterLiterals && isa<CharacterLiteral>(Arg)) ||
215 (CommentStringLiterals && isa<StringLiteral>(Arg)) ||
216 (CommentNullPtrs && isa<CXXNullPtrLiteralExpr>(Arg));
219void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
220 const FunctionDecl *OriginalCallee,
221 SourceLocation ArgBeginLoc,
222 llvm::ArrayRef<const Expr *> Args) {
223 const FunctionDecl *Callee =
resolveMocks(OriginalCallee);
227 Callee = Callee->getFirstDecl();
228 if (
const auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee);
229 Ctor && Ctor->isInheritingConstructor()) {
230 if (
const auto *BaseCtor = Ctor->getInheritedConstructor().getConstructor())
231 Callee = BaseCtor->getFirstDecl();
233 const unsigned NumArgs =
234 std::min<unsigned>(Args.size(), Callee->getNumParams());
235 if ((NumArgs == 0) || (IgnoreSingleArgument && NumArgs == 1))
238 auto MakeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
239 return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
240 Ctx->getSourceManager(),
244 for (
unsigned I = 0; I < NumArgs; ++I) {
245 const ParmVarDecl *PVD = Callee->getParamDecl(I);
246 const IdentifierInfo *II = PVD->getIdentifier();
249 if (FunctionDecl *Template = Callee->getTemplateInstantiationPattern()) {
254 if (Template->getNumParams() <= I ||
255 Template->getParamDecl(I)->isParameterPack()) {
260 const CharSourceRange BeforeArgument =
261 MakeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc());
262 ArgBeginLoc = Args[I]->getEndLoc();
264 std::vector<CommentToken> Comments;
265 if (BeforeArgument.isValid()) {
267 BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts());
270 const CharSourceRange ArgsRange =
271 MakeFileCharRange(Args[I]->getBeginLoc(), Args[I]->getEndLoc());
275 for (
const auto &Comment : Comments) {
276 llvm::SmallVector<StringRef, 2> Matches;
277 if (IdentRE.match(Comment.Text, &Matches) &&
278 !
sameName(Matches[2], II->getName(), StrictMode)) {
280 const DiagnosticBuilder Diag =
281 diag(Comment.Loc,
"argument name '%0' in comment does not "
282 "match parameter name %1")
284 if (
isLikelyTypo(Callee->parameters(), Matches[2], II->getName())) {
285 Diag << FixItHint::CreateReplacement(
286 Comment.Loc, (Matches[1] + II->getName() + Matches[3]).str());
289 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note) << II;
290 if (OriginalCallee != Callee) {
291 diag(OriginalCallee->getLocation(),
292 "actual callee (%0) is declared here", DiagnosticIDs::Note)
299 if (Comments.empty() && shouldAddComment(Args[I])) {
300 llvm::SmallString<32> ArgComment;
301 (llvm::Twine(
"/*") + II->getName() +
"=*/").toStringRef(ArgComment);
302 const DiagnosticBuilder Diag =
303 diag(Args[I]->getBeginLoc(),
304 "argument comment missing for literal argument %0")
306 << FixItHint::CreateInsertion(Args[I]->getBeginLoc(), ArgComment);
312 const auto *E = Result.Nodes.getNodeAs<Expr>(
"expr");
313 if (
const auto *Call = dyn_cast<CallExpr>(E)) {
314 const FunctionDecl *Callee = Call->getDirectCallee();
318 checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(),
319 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
321 const auto *Construct = cast<CXXConstructExpr>(E);
322 if (Construct->getNumArgs() > 0 &&
323 Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
328 Result.Context, Construct->getConstructor(),
329 Construct->getParenOrBraceRange().getBegin(),
330 llvm::ArrayRef(Construct->getArgs(), Construct->getNumArgs()));
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.