summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp
diff options
context:
space:
mode:
authorAlexander Kornienko <alexfh@google.com>2017-11-23 17:02:48 +0000
committerAlexander Kornienko <alexfh@google.com>2017-11-23 17:02:48 +0000
commit6f67bcbb93bda6cf0654a74721de97a3adc1605b (patch)
tree80e243436df76d35e1daa303ca15e9e644566173 /clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp
parentadccab64f21020d84fdc14715f4cb384456c9670 (diff)
downloadbcm5719-llvm-6f67bcbb93bda6cf0654a74721de97a3adc1605b.tar.gz
bcm5719-llvm-6f67bcbb93bda6cf0654a74721de97a3adc1605b.zip
[clang-tidy] rename_check.py misc-argument-comment bugprone-argument-comment
Summary: + manually convert the unit test to lit test. Reviewers: hokein Reviewed By: hokein Subscribers: mgorny, xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D40392 llvm-svn: 318926
Diffstat (limited to 'clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp308
1 files changed, 0 insertions, 308 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp b/clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp
deleted file mode 100644
index d7ac9f3a9b3..00000000000
--- a/clang-tools-extra/clang-tidy/misc/ArgumentCommentCheck.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-//===--- ArgumentCommentCheck.cpp - clang-tidy ----------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ArgumentCommentCheck.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/Lex/Lexer.h"
-#include "clang/Lex/Token.h"
-#include "../utils/LexerUtils.h"
-
-using namespace clang::ast_matchers;
-
-namespace clang {
-namespace tidy {
-namespace misc {
-
-ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
- ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- StrictMode(Options.getLocalOrGlobal("StrictMode", 0) != 0),
- IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
-
-void ArgumentCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "StrictMode", StrictMode);
-}
-
-void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(
- callExpr(unless(cxxOperatorCallExpr()),
- // NewCallback's arguments relate to the pointed function, don't
- // check them against NewCallback's parameter names.
- // FIXME: Make this configurable.
- unless(hasDeclaration(functionDecl(
- hasAnyName("NewCallback", "NewPermanentCallback")))))
- .bind("expr"),
- this);
- Finder->addMatcher(cxxConstructExpr().bind("expr"), this);
-}
-
-static std::vector<std::pair<SourceLocation, StringRef>>
-getCommentsInRange(ASTContext *Ctx, CharSourceRange Range) {
- std::vector<std::pair<SourceLocation, StringRef>> Comments;
- auto &SM = Ctx->getSourceManager();
- std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
- EndLoc = SM.getDecomposedLoc(Range.getEnd());
-
- if (BeginLoc.first != EndLoc.first)
- return Comments;
-
- bool Invalid = false;
- StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
- if (Invalid)
- return Comments;
-
- const char *StrData = Buffer.data() + BeginLoc.second;
-
- Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
- Buffer.begin(), StrData, Buffer.end());
- TheLexer.SetCommentRetentionState(true);
-
- while (true) {
- Token Tok;
- if (TheLexer.LexFromRawLexer(Tok))
- break;
- if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
- break;
-
- if (Tok.is(tok::comment)) {
- std::pair<FileID, unsigned> CommentLoc =
- SM.getDecomposedLoc(Tok.getLocation());
- assert(CommentLoc.first == BeginLoc.first);
- Comments.emplace_back(
- Tok.getLocation(),
- StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
- } else {
- // Clear comments found before the different token, e.g. comma.
- Comments.clear();
- }
- }
-
- return Comments;
-}
-
-static std::vector<std::pair<SourceLocation, StringRef>>
-getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) {
- std::vector<std::pair<SourceLocation, StringRef>> Comments;
- while (Loc.isValid()) {
- clang::Token Tok =
- utils::lexer::getPreviousToken(*Ctx, Loc, /*SkipComments=*/false);
- if (Tok.isNot(tok::comment))
- break;
- Loc = Tok.getLocation();
- Comments.emplace_back(
- Loc,
- Lexer::getSourceText(CharSourceRange::getCharRange(
- Loc, Loc.getLocWithOffset(Tok.getLength())),
- Ctx->getSourceManager(), Ctx->getLangOpts()));
- }
- return Comments;
-}
-
-static bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
- StringRef ArgName, unsigned ArgIndex) {
- std::string ArgNameLowerStr = ArgName.lower();
- StringRef ArgNameLower = ArgNameLowerStr;
- // The threshold is arbitrary.
- unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
- unsigned ThisED = ArgNameLower.edit_distance(
- Params[ArgIndex]->getIdentifier()->getName().lower(),
- /*AllowReplacements=*/true, UpperBound);
- if (ThisED >= UpperBound)
- return false;
-
- for (unsigned I = 0, E = Params.size(); I != E; ++I) {
- if (I == ArgIndex)
- continue;
- IdentifierInfo *II = Params[I]->getIdentifier();
- if (!II)
- continue;
-
- const unsigned Threshold = 2;
- // Other parameters must be an edit distance at least Threshold more away
- // from this parameter. This gives us greater confidence that this is a typo
- // of this parameter and not one with a similar name.
- unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
- /*AllowReplacements=*/true,
- ThisED + Threshold);
- if (OtherED < ThisED + Threshold)
- return false;
- }
-
- return true;
-}
-
-static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode) {
- if (StrictMode)
- return InComment == InDecl;
- InComment = InComment.trim('_');
- InDecl = InDecl.trim('_');
- // FIXME: compare_lower only works for ASCII.
- return InComment.compare_lower(InDecl) == 0;
-}
-
-static bool looksLikeExpectMethod(const CXXMethodDecl *Expect) {
- return Expect != nullptr && Expect->getLocation().isMacroID() &&
- Expect->getNameInfo().getName().isIdentifier() &&
- Expect->getName().startswith("gmock_");
-}
-static bool areMockAndExpectMethods(const CXXMethodDecl *Mock,
- const CXXMethodDecl *Expect) {
- assert(looksLikeExpectMethod(Expect));
- return Mock != nullptr && Mock->getNextDeclInContext() == Expect &&
- Mock->getNumParams() == Expect->getNumParams() &&
- Mock->getLocation().isMacroID() &&
- Mock->getNameInfo().getName().isIdentifier() &&
- Mock->getName() == Expect->getName().substr(strlen("gmock_"));
-}
-
-// This uses implementation details of MOCK_METHODx_ macros: for each mocked
-// method M it defines M() with appropriate signature and a method used to set
-// up expectations - gmock_M() - with each argument's type changed the
-// corresponding matcher. This function returns M when given either M or
-// gmock_M.
-static const CXXMethodDecl *findMockedMethod(const CXXMethodDecl *Method) {
- if (looksLikeExpectMethod(Method)) {
- const DeclContext *Ctx = Method->getDeclContext();
- if (Ctx == nullptr || !Ctx->isRecord())
- return nullptr;
- for (const auto *D : Ctx->decls()) {
- if (D->getNextDeclInContext() == Method) {
- const auto *Previous = dyn_cast<CXXMethodDecl>(D);
- return areMockAndExpectMethods(Previous, Method) ? Previous : nullptr;
- }
- }
- return nullptr;
- }
- if (const auto *Next = dyn_cast_or_null<CXXMethodDecl>(
- Method->getNextDeclInContext())) {
- if (looksLikeExpectMethod(Next) && areMockAndExpectMethods(Method, Next))
- return Method;
- }
- return nullptr;
-}
-
-// For gmock expectation builder method (the target of the call generated by
-// `EXPECT_CALL(obj, Method(...))`) tries to find the real method being mocked
-// (returns nullptr, if the mock method doesn't override anything). For other
-// functions returns the function itself.
-static const FunctionDecl *resolveMocks(const FunctionDecl *Func) {
- if (const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
- if (const auto *MockedMethod = findMockedMethod(Method)) {
- // If mocked method overrides the real one, we can use its parameter
- // names, otherwise we're out of luck.
- if (MockedMethod->size_overridden_methods() > 0) {
- return *MockedMethod->begin_overridden_methods();
- }
- return nullptr;
- }
- }
- return Func;
-}
-
-void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
- const FunctionDecl *OriginalCallee,
- SourceLocation ArgBeginLoc,
- llvm::ArrayRef<const Expr *> Args) {
- const FunctionDecl *Callee = resolveMocks(OriginalCallee);
- if (!Callee)
- return;
-
- Callee = Callee->getFirstDecl();
- unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
- if (NumArgs == 0)
- return;
-
- auto makeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
- return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
- Ctx->getSourceManager(),
- Ctx->getLangOpts());
- };
-
- for (unsigned I = 0; I < NumArgs; ++I) {
- const ParmVarDecl *PVD = Callee->getParamDecl(I);
- IdentifierInfo *II = PVD->getIdentifier();
- if (!II)
- continue;
- if (auto Template = Callee->getTemplateInstantiationPattern()) {
- // Don't warn on arguments for parameters instantiated from template
- // parameter packs. If we find more arguments than the template
- // definition has, it also means that they correspond to a parameter
- // pack.
- if (Template->getNumParams() <= I ||
- Template->getParamDecl(I)->isParameterPack()) {
- continue;
- }
- }
-
- CharSourceRange BeforeArgument =
- makeFileCharRange(ArgBeginLoc, Args[I]->getLocStart());
- ArgBeginLoc = Args[I]->getLocEnd();
-
- std::vector<std::pair<SourceLocation, StringRef>> Comments;
- if (BeforeArgument.isValid()) {
- Comments = getCommentsInRange(Ctx, BeforeArgument);
- } else {
- // Fall back to parsing back from the start of the argument.
- CharSourceRange ArgsRange = makeFileCharRange(
- Args[I]->getLocStart(), Args[NumArgs - 1]->getLocEnd());
- Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin());
- }
-
- for (auto Comment : Comments) {
- llvm::SmallVector<StringRef, 2> Matches;
- if (IdentRE.match(Comment.second, &Matches) &&
- !sameName(Matches[2], II->getName(), StrictMode)) {
- {
- DiagnosticBuilder Diag =
- diag(Comment.first, "argument name '%0' in comment does not "
- "match parameter name %1")
- << Matches[2] << II;
- if (isLikelyTypo(Callee->parameters(), Matches[2], I)) {
- Diag << FixItHint::CreateReplacement(
- Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
- }
- }
- diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note) << II;
- if (OriginalCallee != Callee) {
- diag(OriginalCallee->getLocation(),
- "actual callee (%0) is declared here", DiagnosticIDs::Note)
- << OriginalCallee;
- }
- }
- }
- }
-}
-
-void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
- const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
- if (const auto *Call = dyn_cast<CallExpr>(E)) {
- const FunctionDecl *Callee = Call->getDirectCallee();
- if (!Callee)
- return;
-
- checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
- llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
- } else {
- const auto *Construct = cast<CXXConstructExpr>(E);
- if (Construct->getNumArgs() == 1 &&
- Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
- // Ignore implicit construction.
- return;
- }
- checkCallArgs(
- Result.Context, Construct->getConstructor(),
- Construct->getParenOrBraceRange().getBegin(),
- llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
- }
-}
-
-} // namespace misc
-} // namespace tidy
-} // namespace clang
OpenPOWER on IntegriCloud