10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 #include "clang/Lex/Token.h"
15 #include "../utils/LexerUtils.h"
24 if (
const auto *
D = Node.getDeclContext()->getEnclosingNamespaceContext())
25 if (
D->isStdNamespace())
27 if (Node.getLocation().isInvalid())
29 return Node.getASTContext().getSourceManager().isInSystemHeader(
34 ArgumentCommentCheck::ArgumentCommentCheck(StringRef
Name,
37 StrictMode(Options.getLocalOrGlobal(
"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]*)( *= *\\*/)$") {}
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);
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())))
82 static std::vector<std::pair<SourceLocation, StringRef>>
84 std::vector<std::pair<SourceLocation, StringRef>> Comments;
85 auto &SM =
Ctx->getSourceManager();
86 std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(
Range.getBegin()),
87 EndLoc = SM.getDecomposedLoc(
Range.getEnd());
89 if (BeginLoc.first != EndLoc.first)
93 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 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()));
126 static 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(
139 Loc,
Loc.getLocWithOffset(Tok.getLength())),
140 Ctx->getSourceManager(),
Ctx->getLangOpts()));
146 StringRef ArgName,
unsigned ArgIndex) {
147 std::string ArgNameLowerStr = ArgName.lower();
148 StringRef ArgNameLower = ArgNameLowerStr;
150 unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
151 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 IdentifierInfo *II = Params[I]->getIdentifier();
164 const unsigned Threshold = 2;
168 unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
171 if (OtherED < ThisED + Threshold)
178 static 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().startswith(
"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();
248 bool 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));
263 void 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 unsigned NumArgs = std::min<unsigned>(
Args.size(), Callee->getNumParams());
273 if ((NumArgs == 0) || (IgnoreSingleArgument && NumArgs == 1))
276 auto MakeFileCharRange = [
Ctx](SourceLocation Begin, SourceLocation End) {
277 return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
278 Ctx->getSourceManager(),
282 for (
unsigned I = 0; I < NumArgs; ++I) {
283 const ParmVarDecl *PVD = Callee->getParamDecl(I);
284 IdentifierInfo *II = PVD->getIdentifier();
287 if (FunctionDecl *Template = Callee->getTemplateInstantiationPattern()) {
292 if (Template->getNumParams() <= I ||
293 Template->getParamDecl(I)->isParameterPack()) {
298 CharSourceRange BeforeArgument =
299 MakeFileCharRange(ArgBeginLoc,
Args[I]->getBeginLoc());
300 ArgBeginLoc =
Args[I]->getEndLoc();
302 std::vector<std::pair<SourceLocation, StringRef>> Comments;
303 if (BeforeArgument.isValid()) {
307 CharSourceRange ArgsRange =
308 MakeFileCharRange(
Args[I]->getBeginLoc(),
Args[I]->getEndLoc());
312 for (
auto Comment : Comments) {
313 llvm::SmallVector<StringRef, 2> Matches;
314 if (IdentRE.match(Comment.second, &Matches) &&
315 !
sameName(Matches[2], II->getName(), StrictMode)) {
317 DiagnosticBuilder Diag =
318 diag(Comment.first,
"argument name '%0' in comment does not "
319 "match parameter name %1")
321 if (
isLikelyTypo(Callee->parameters(), Matches[2], I)) {
322 Diag << FixItHint::CreateReplacement(
323 Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
326 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note) << II;
327 if (OriginalCallee != Callee) {
328 diag(OriginalCallee->getLocation(),
329 "actual callee (%0) is declared here", DiagnosticIDs::Note)
336 if (Comments.empty() && shouldAddComment(
Args[I])) {
337 std::string ArgComment =
338 (llvm::Twine(
"/*") + II->getName() +
"=*/").str();
339 DiagnosticBuilder Diag =
341 "argument comment missing for literal argument %0")
343 << FixItHint::CreateInsertion(
Args[I]->getBeginLoc(), ArgComment);
349 const auto *
E = Result.Nodes.getNodeAs<Expr>(
"expr");
350 if (
const auto *Call = dyn_cast<CallExpr>(
E)) {
351 const FunctionDecl *Callee = Call->getDirectCallee();
355 checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(),
356 llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
358 const auto *Construct = cast<CXXConstructExpr>(
E);
359 if (Construct->getNumArgs() > 0 &&
360 Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
365 Result.Context, Construct->getConstructor(),
366 Construct->getParenOrBraceRange().getBegin(),
367 llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));