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" 23 ArgumentCommentCheck::ArgumentCommentCheck(StringRef
Name,
26 StrictMode(Options.getLocalOrGlobal(
"StrictMode", 0) != 0),
27 CommentBoolLiterals(Options.getLocalOrGlobal(
"CommentBoolLiterals", 0) !=
29 CommentIntegerLiterals(
30 Options.getLocalOrGlobal(
"CommentIntegerLiterals", 0) != 0),
32 Options.getLocalOrGlobal(
"CommentFloatLiterals", 0) != 0),
33 CommentStringLiterals(
34 Options.getLocalOrGlobal(
"CommentStringLiterals", 0) != 0),
35 CommentUserDefinedLiterals(
36 Options.getLocalOrGlobal(
"CommentUserDefinedLiterals", 0) != 0),
37 CommentCharacterLiterals(
38 Options.getLocalOrGlobal(
"CommentCharacterLiterals", 0) != 0),
39 CommentNullPtrs(Options.getLocalOrGlobal(
"CommentNullPtrs", 0) != 0),
40 IdentRE(
"^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
44 Options.
store(Opts,
"CommentBoolLiterals", CommentBoolLiterals);
45 Options.
store(Opts,
"CommentIntegerLiterals", CommentIntegerLiterals);
46 Options.
store(Opts,
"CommentFloatLiterals", CommentFloatLiterals);
47 Options.
store(Opts,
"CommentStringLiterals", CommentStringLiterals);
48 Options.
store(Opts,
"CommentUserDefinedLiterals", CommentUserDefinedLiterals);
49 Options.
store(Opts,
"CommentCharacterLiterals", CommentCharacterLiterals);
55 callExpr(unless(cxxOperatorCallExpr()),
59 unless(hasDeclaration(functionDecl(
60 hasAnyName(
"NewCallback",
"NewPermanentCallback")))))
63 Finder->addMatcher(cxxConstructExpr().bind(
"expr"),
this);
66 static std::vector<std::pair<SourceLocation, StringRef>>
68 std::vector<std::pair<SourceLocation, StringRef>> Comments;
69 auto &SM = Ctx->getSourceManager();
70 std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
71 EndLoc = SM.getDecomposedLoc(Range.getEnd());
73 if (BeginLoc.first != EndLoc.first)
77 StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
81 const char *StrData = Buffer.data() + BeginLoc.second;
83 Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
84 Buffer.begin(), StrData, Buffer.end());
85 TheLexer.SetCommentRetentionState(
true);
89 if (TheLexer.LexFromRawLexer(Tok))
91 if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
94 if (Tok.is(tok::comment)) {
95 std::pair<FileID, unsigned> CommentLoc =
96 SM.getDecomposedLoc(Tok.getLocation());
97 assert(CommentLoc.first == BeginLoc.first);
98 Comments.emplace_back(
100 StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
110 static std::vector<std::pair<SourceLocation, StringRef>>
112 std::vector<std::pair<SourceLocation, StringRef>> Comments;
113 while (Loc.isValid()) {
115 Loc, Ctx->getSourceManager(), Ctx->getLangOpts(),
117 if (Tok.isNot(tok::comment))
119 Loc = Tok.getLocation();
120 Comments.emplace_back(
122 Lexer::getSourceText(CharSourceRange::getCharRange(
123 Loc, Loc.getLocWithOffset(Tok.getLength())),
124 Ctx->getSourceManager(), Ctx->getLangOpts()));
130 StringRef ArgName,
unsigned ArgIndex) {
131 std::string ArgNameLowerStr = ArgName.lower();
132 StringRef ArgNameLower = ArgNameLowerStr;
134 unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
135 unsigned ThisED = ArgNameLower.edit_distance(
136 Params[ArgIndex]->getIdentifier()->getName().
lower(),
138 if (ThisED >= UpperBound)
141 for (
unsigned I = 0, E = Params.size(); I != E; ++I) {
144 IdentifierInfo *II = Params[I]->getIdentifier();
148 const unsigned Threshold = 2;
152 unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
155 if (OtherED < ThisED + Threshold)
162 static bool sameName(StringRef InComment, StringRef InDecl,
bool StrictMode) {
164 return InComment == InDecl;
165 InComment = InComment.trim(
'_');
166 InDecl = InDecl.trim(
'_');
168 return InComment.compare_lower(InDecl) == 0;
172 return Expect !=
nullptr && Expect->getLocation().isMacroID() &&
173 Expect->getNameInfo().getName().isIdentifier() &&
174 Expect->getName().startswith(
"gmock_");
177 const CXXMethodDecl *Expect) {
179 return Mock !=
nullptr && Mock->getNextDeclInContext() == Expect &&
180 Mock->getNumParams() == Expect->getNumParams() &&
181 Mock->getLocation().isMacroID() &&
182 Mock->getNameInfo().getName().isIdentifier() &&
183 Mock->getName() == Expect->getName().substr(strlen(
"gmock_"));
193 const DeclContext *
Ctx = Method->getDeclContext();
194 if (Ctx ==
nullptr || !Ctx->isRecord())
196 for (
const auto *
D : Ctx->decls()) {
197 if (
D->getNextDeclInContext() == Method) {
198 const auto *Previous = dyn_cast<CXXMethodDecl>(
D);
204 if (
const auto *Next =
205 dyn_cast_or_null<CXXMethodDecl>(Method->getNextDeclInContext())) {
217 if (
const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
221 if (MockedMethod->size_overridden_methods() > 0) {
222 return *MockedMethod->begin_overridden_methods();
232 bool ArgumentCommentCheck::shouldAddComment(
const Expr *Arg)
const {
233 if (Arg->getExprLoc().isMacroID())
235 Arg = Arg->IgnoreImpCasts();
236 return (CommentBoolLiterals && isa<CXXBoolLiteralExpr>(Arg)) ||
237 (CommentIntegerLiterals && isa<IntegerLiteral>(Arg)) ||
238 (CommentFloatLiterals && isa<FloatingLiteral>(Arg)) ||
239 (CommentUserDefinedLiterals && isa<UserDefinedLiteral>(Arg)) ||
240 (CommentCharacterLiterals && isa<CharacterLiteral>(Arg)) ||
241 (CommentStringLiterals && isa<StringLiteral>(Arg)) ||
242 (CommentNullPtrs && isa<CXXNullPtrLiteralExpr>(Arg));
245 void ArgumentCommentCheck::checkCallArgs(ASTContext *
Ctx,
246 const FunctionDecl *OriginalCallee,
247 SourceLocation ArgBeginLoc,
248 llvm::ArrayRef<const Expr *> Args) {
249 const FunctionDecl *Callee =
resolveMocks(OriginalCallee);
253 Callee = Callee->getFirstDecl();
254 unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
258 auto MakeFileCharRange = [
Ctx](SourceLocation Begin, SourceLocation End) {
259 return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
260 Ctx->getSourceManager(),
264 for (
unsigned I = 0; I < NumArgs; ++I) {
265 const ParmVarDecl *PVD = Callee->getParamDecl(I);
266 IdentifierInfo *II = PVD->getIdentifier();
269 if (
auto Template = Callee->getTemplateInstantiationPattern()) {
274 if (Template->getNumParams() <= I ||
275 Template->getParamDecl(I)->isParameterPack()) {
280 CharSourceRange BeforeArgument =
281 MakeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc());
282 ArgBeginLoc = Args[I]->getEndLoc();
284 std::vector<std::pair<SourceLocation, StringRef>> Comments;
285 if (BeforeArgument.isValid()) {
289 CharSourceRange ArgsRange = MakeFileCharRange(
290 Args[I]->getBeginLoc(), Args[NumArgs - 1]->getEndLoc());
294 for (
auto Comment : Comments) {
295 llvm::SmallVector<StringRef, 2> Matches;
296 if (IdentRE.match(Comment.second, &Matches) &&
297 !
sameName(Matches[2], II->getName(), StrictMode)) {
299 DiagnosticBuilder Diag =
300 diag(Comment.first,
"argument name '%0' in comment does not " 301 "match parameter name %1")
303 if (
isLikelyTypo(Callee->parameters(), Matches[2], I)) {
304 Diag << FixItHint::CreateReplacement(
305 Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
308 diag(PVD->getLocation(),
"%0 declared here", DiagnosticIDs::Note) << II;
309 if (OriginalCallee != Callee) {
310 diag(OriginalCallee->getLocation(),
311 "actual callee (%0) is declared here", DiagnosticIDs::Note)
318 if (Comments.empty() && shouldAddComment(Args[I])) {
319 std::string ArgComment =
320 (llvm::Twine(
"/*") + II->getName() +
"=*/").str();
321 DiagnosticBuilder Diag =
322 diag(Args[I]->getBeginLoc(),
323 "argument comment missing for literal argument %0")
325 << FixItHint::CreateInsertion(Args[I]->getBeginLoc(), ArgComment);
331 const auto *E = Result.Nodes.getNodeAs<Expr>(
"expr");
332 if (
const auto *Call = dyn_cast<CallExpr>(E)) {
333 const FunctionDecl *Callee = Call->getDirectCallee();
337 checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(),
338 llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
340 const auto *Construct = cast<CXXConstructExpr>(E);
341 if (Construct->getNumArgs() == 1 &&
342 Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
347 Result.Context, Construct->getConstructor(),
348 Construct->getParenOrBraceRange().getBegin(),
349 llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
SourceLocation Loc
'#' location in the include directive
static bool looksLikeExpectMethod(const CXXMethodDecl *Expect)
static const FunctionDecl * resolveMocks(const FunctionDecl *Func)
Token getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or tok::unknown if not found.
static std::vector< std::pair< SourceLocation, StringRef > > getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc)
Base class for all clang-tidy checks.
static bool areMockAndExpectMethods(const CXXMethodDecl *Mock, const CXXMethodDecl *Expect)
static bool isLikelyTypo(llvm::ArrayRef< ParmVarDecl *> Params, StringRef ArgName, unsigned ArgIndex)
static std::vector< std::pair< SourceLocation, StringRef > > getCommentsInRange(ASTContext *Ctx, CharSourceRange Range)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
static char lower(char C)
static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static const CXXMethodDecl * findMockedMethod(const CXXMethodDecl *Method)
CharSourceRange Range
SourceRange for the file name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.