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 return D->isStdNamespace();
30 ArgumentCommentCheck::ArgumentCommentCheck(StringRef
Name,
33 StrictMode(Options.getLocalOrGlobal(
"StrictMode", false)),
34 IgnoreSingleArgument(Options.get(
"IgnoreSingleArgument", false)),
35 CommentBoolLiterals(Options.get(
"CommentBoolLiterals", false)),
36 CommentIntegerLiterals(Options.get(
"CommentIntegerLiterals", false)),
37 CommentFloatLiterals(Options.get(
"CommentFloatLiterals", false)),
38 CommentStringLiterals(Options.get(
"CommentStringLiterals", false)),
39 CommentUserDefinedLiterals(
40 Options.get(
"CommentUserDefinedLiterals", false)),
41 CommentCharacterLiterals(Options.get(
"CommentCharacterLiterals", false)),
42 CommentNullPtrs(Options.get(
"CommentNullPtrs", false)),
43 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
47 Options.
store(Opts,
"IgnoreSingleArgument", IgnoreSingleArgument);
48 Options.
store(Opts,
"CommentBoolLiterals", CommentBoolLiterals);
49 Options.
store(Opts,
"CommentIntegerLiterals", CommentIntegerLiterals);
50 Options.
store(Opts,
"CommentFloatLiterals", CommentFloatLiterals);
51 Options.
store(Opts,
"CommentStringLiterals", CommentStringLiterals);
52 Options.
store(Opts,
"CommentUserDefinedLiterals", CommentUserDefinedLiterals);
53 Options.
store(Opts,
"CommentCharacterLiterals", CommentCharacterLiterals);
59 callExpr(unless(cxxOperatorCallExpr()),
63 unless(hasDeclaration(functionDecl(
64 hasAnyName(
"NewCallback",
"NewPermanentCallback")))),
69 unless(hasDeclaration(isFromStdNamespace())))
73 cxxConstructExpr(unless(hasDeclaration(isFromStdNamespace())))
78 static std::vector<std::pair<SourceLocation, StringRef>>
80 std::vector<std::pair<SourceLocation, StringRef>> Comments;
81 auto &SM =
Ctx->getSourceManager();
82 std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(
Range.getBegin()),
83 EndLoc = SM.getDecomposedLoc(
Range.getEnd());
85 if (BeginLoc.first != EndLoc.first)
89 StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
93 const char *StrData = Buffer.data() + BeginLoc.second;
95 Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first),
Ctx->getLangOpts(),
96 Buffer.begin(), StrData, Buffer.end());
97 TheLexer.SetCommentRetentionState(
true);
101 if (TheLexer.LexFromRawLexer(Tok))
103 if (Tok.getLocation() ==
Range.getEnd() || Tok.is(tok::eof))
106 if (Tok.is(tok::comment)) {
107 std::pair<FileID, unsigned> CommentLoc =
108 SM.getDecomposedLoc(Tok.getLocation());
109 assert(CommentLoc.first == BeginLoc.first);
110 Comments.emplace_back(
112 StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
122 static std::vector<std::pair<SourceLocation, StringRef>>
124 std::vector<std::pair<SourceLocation, StringRef>> Comments;
125 while (
Loc.isValid()) {
127 Loc,
Ctx->getSourceManager(),
Ctx->getLangOpts(),
129 if (Tok.isNot(tok::comment))
131 Loc = Tok.getLocation();
132 Comments.emplace_back(
134 Lexer::getSourceText(CharSourceRange::getCharRange(
135 Loc,
Loc.getLocWithOffset(Tok.getLength())),
136 Ctx->getSourceManager(),
Ctx->getLangOpts()));
142 StringRef ArgName,
unsigned ArgIndex) {
143 std::string ArgNameLowerStr = ArgName.lower();
144 StringRef ArgNameLower = ArgNameLowerStr;
146 unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
147 unsigned ThisED = ArgNameLower.edit_distance(
148 Params[ArgIndex]->getIdentifier()->getName().
lower(),
150 if (ThisED >= UpperBound)
153 for (
unsigned I = 0,
E = Params.size(); I !=
E; ++I) {
156 IdentifierInfo *II = Params[I]->getIdentifier();
160 const unsigned Threshold = 2;
164 unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
167 if (OtherED < ThisED + Threshold)
174 static bool sameName(StringRef InComment, StringRef InDecl,
bool StrictMode) {
176 return InComment == InDecl;
177 InComment = InComment.trim(
'_');
178 InDecl = InDecl.trim(
'_');
180 return InComment.compare_lower(InDecl) == 0;
184 return Expect !=
nullptr && Expect->getLocation().isMacroID() &&
185 Expect->getNameInfo().getName().isIdentifier() &&
186 Expect->getName().startswith(
"gmock_");
189 const CXXMethodDecl *Expect) {
191 return Mock !=
nullptr && Mock->getNextDeclInContext() == Expect &&
192 Mock->getNumParams() == Expect->getNumParams() &&
193 Mock->getLocation().isMacroID() &&
194 Mock->getNameInfo().getName().isIdentifier() &&
195 Mock->getName() == Expect->getName().substr(strlen(
"gmock_"));
205 const DeclContext *
Ctx = Method->getDeclContext();
206 if (
Ctx ==
nullptr || !
Ctx->isRecord())
208 for (
const auto *D :
Ctx->decls()) {
209 if (D->getNextDeclInContext() == Method) {
210 const auto *Previous = dyn_cast<CXXMethodDecl>(D);
216 if (
const auto *Next =
217 dyn_cast_or_null<CXXMethodDecl>(Method->getNextDeclInContext())) {
229 if (
const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
233 if (MockedMethod->size_overridden_methods() > 0) {
234 return *MockedMethod->begin_overridden_methods();
244 bool ArgumentCommentCheck::shouldAddComment(
const Expr *Arg)
const {
245 Arg = Arg->IgnoreImpCasts();
246 if (isa<UnaryOperator>(Arg))
247 Arg = cast<UnaryOperator>(Arg)->getSubExpr();
248 if (Arg->getExprLoc().isMacroID())
250 return (CommentBoolLiterals && isa<CXXBoolLiteralExpr>(Arg)) ||
251 (CommentIntegerLiterals && isa<IntegerLiteral>(Arg)) ||
252 (CommentFloatLiterals && isa<FloatingLiteral>(Arg)) ||
253 (CommentUserDefinedLiterals && isa<UserDefinedLiteral>(Arg)) ||
254 (CommentCharacterLiterals && isa<CharacterLiteral>(Arg)) ||
255 (CommentStringLiterals && isa<StringLiteral>(Arg)) ||
256 (CommentNullPtrs && isa<CXXNullPtrLiteralExpr>(Arg));
259 void ArgumentCommentCheck::checkCallArgs(ASTContext *
Ctx,
260 const FunctionDecl *OriginalCallee,
261 SourceLocation ArgBeginLoc,
262 llvm::ArrayRef<const Expr *> Args) {
263 const FunctionDecl *Callee =
resolveMocks(OriginalCallee);
267 Callee = Callee->getFirstDecl();
268 unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
269 if ((NumArgs == 0) || (IgnoreSingleArgument && NumArgs == 1))
272 auto MakeFileCharRange = [
Ctx](SourceLocation Begin, SourceLocation End) {
273 return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
274 Ctx->getSourceManager(),
278 for (
unsigned I = 0; I < NumArgs; ++I) {
279 const ParmVarDecl *PVD = Callee->getParamDecl(I);
280 IdentifierInfo *II = PVD->getIdentifier();
283 if (
auto Template = Callee->getTemplateInstantiationPattern()) {
288 if (Template->getNumParams() <= I ||
289 Template->getParamDecl(I)->isParameterPack()) {
294 CharSourceRange BeforeArgument =
295 MakeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc());
296 ArgBeginLoc = Args[I]->getEndLoc();
298 std::vector<std::pair<SourceLocation, StringRef>> Comments;
299 if (BeforeArgument.isValid()) {
303 CharSourceRange ArgsRange =
304 MakeFileCharRange(Args[I]->getBeginLoc(), Args[I]->getEndLoc());
308 for (
auto Comment : Comments) {
309 llvm::SmallVector<StringRef, 2> Matches;
310 if (IdentRE.match(Comment.second, &Matches) &&
311 !
sameName(Matches[2], II->getName(), StrictMode)) {
313 DiagnosticBuilder Diag =
314 diag(Comment.first,
"argument name '%0' in comment does not "
315 "match parameter name %1")
317 if (
isLikelyTypo(Callee->parameters(), Matches[2], I)) {
318 Diag << FixItHint::CreateReplacement(
319 Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
322 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note) << II;
323 if (OriginalCallee != Callee) {
324 diag(OriginalCallee->getLocation(),
325 "actual callee (%0) is declared here", DiagnosticIDs::Note)
332 if (Comments.empty() && shouldAddComment(Args[I])) {
333 std::string ArgComment =
334 (llvm::Twine(
"/*") + II->getName() +
"=*/").str();
335 DiagnosticBuilder Diag =
336 diag(Args[I]->getBeginLoc(),
337 "argument comment missing for literal argument %0")
339 << FixItHint::CreateInsertion(Args[I]->getBeginLoc(), ArgComment);
345 const auto *
E = Result.Nodes.getNodeAs<Expr>(
"expr");
346 if (
const auto *Call = dyn_cast<CallExpr>(
E)) {
347 const FunctionDecl *Callee = Call->getDirectCallee();
351 checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(),
352 llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
354 const auto *Construct = cast<CXXConstructExpr>(
E);
355 if (Construct->getNumArgs() > 0 &&
356 Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
361 Result.Context, Construct->getConstructor(),
362 Construct->getParenOrBraceRange().getBegin(),
363 llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));