clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ArgumentCommentCheck.cpp
Go to the documentation of this file.
1 //===--- ArgumentCommentCheck.cpp - clang-tidy ----------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "ArgumentCommentCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Lex/Token.h"
15
16 using namespace clang::ast_matchers;
17
18 namespace clang {
19 namespace tidy {
20 namespace misc {
21
22 ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
23 ClangTidyContext *Context)
24 : ClangTidyCheck(Name, Context),
25 IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
26
27 void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
28 Finder->addMatcher(
29 callExpr(unless(cxxOperatorCallExpr()),
30 // NewCallback's arguments relate to the pointed function, don't
31 // check them against NewCallback's parameter names.
32 // FIXME: Make this configurable.
33 unless(hasDeclaration(functionDecl(anyOf(
34 hasName("NewCallback"), hasName("NewPermanentCallback"))))))
35 .bind("expr"),
36 this);
37 Finder->addMatcher(cxxConstructExpr().bind("expr"), this);
38 }
39
40 std::vector<std::pair<SourceLocation, StringRef>>
41 ArgumentCommentCheck::getCommentsInRange(ASTContext *Ctx, SourceRange Range) {
42 std::vector<std::pair<SourceLocation, StringRef>> Comments;
43 auto &SM = Ctx->getSourceManager();
44 std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
45 EndLoc = SM.getDecomposedLoc(Range.getEnd());
46
47 if (BeginLoc.first != EndLoc.first)
48 return Comments;
49
50 bool Invalid = false;
51 StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
52 if (Invalid)
53 return Comments;
54
55 const char *StrData = Buffer.data() + BeginLoc.second;
56
57 Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
58 Buffer.begin(), StrData, Buffer.end());
59 TheLexer.SetCommentRetentionState(true);
60
61 while (true) {
62 Token Tok;
63 if (TheLexer.LexFromRawLexer(Tok))
64 break;
65 if (Tok.getLocation() == Range.getEnd() || Tok.getKind() == tok::eof)
66 break;
67
68 if (Tok.getKind() == tok::comment) {
69 std::pair<FileID, unsigned> CommentLoc =
70 SM.getDecomposedLoc(Tok.getLocation());
71 assert(CommentLoc.first == BeginLoc.first);
72 Comments.emplace_back(
73 Tok.getLocation(),
74 StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
75 }
76 }
77
78 return Comments;
79 }
80
81 bool
82 ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
83 StringRef ArgName, unsigned ArgIndex) {
84 std::string ArgNameLower = ArgName.lower();
85 unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
86 unsigned ThisED = StringRef(ArgNameLower).edit_distance(
87 Params[ArgIndex]->getIdentifier()->getName().lower(),
88 /*AllowReplacements=*/true, UpperBound);
89 if (ThisED >= UpperBound)
90 return false;
91
92 for (unsigned I = 0, E = Params.size(); I != E; ++I) {
93 if (I == ArgIndex)
94 continue;
95 IdentifierInfo *II = Params[I]->getIdentifier();
96 if (!II)
97 continue;
98
99 const unsigned Threshold = 2;
100 // Other parameters must be an edit distance at least Threshold more away
101 // from this parameter. This gives us greater confidence that this is a typo
102 // of this parameter and not one with a similar name.
103 unsigned OtherED = StringRef(ArgNameLower).edit_distance(
104 II->getName().lower(),
105 /*AllowReplacements=*/true, ThisED + Threshold);
106 if (OtherED < ThisED + Threshold)
107 return false;
108 }
109
110 return true;
111 }
112
113 void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
114 const FunctionDecl *Callee,
115 SourceLocation ArgBeginLoc,
116 llvm::ArrayRef<const Expr *> Args) {
117 Callee = Callee->getFirstDecl();
118 for (unsigned i = 0,
119 e = std::min<unsigned>(Args.size(), Callee->getNumParams());
120 i != e; ++i) {
121 const ParmVarDecl *PVD = Callee->getParamDecl(i);
122 IdentifierInfo *II = PVD->getIdentifier();
123 if (!II)
124 continue;
125 if (auto Template = Callee->getTemplateInstantiationPattern()) {
126 // Don't warn on arguments for parameters instantiated from template
127 // parameter packs. If we find more arguments than the template definition
128 // has, it also means that they correspond to a parameter pack.
129 if (Template->getNumParams() <= i ||
130 Template->getParamDecl(i)->isParameterPack()) {
131 continue;
132 }
133 }
134
135 SourceLocation BeginSLoc, EndSLoc = Args[i]->getLocStart();
136 if (i == 0)
137 BeginSLoc = ArgBeginLoc;
138 else
139 BeginSLoc = Args[i - 1]->getLocEnd();
140 if (BeginSLoc.isMacroID() || EndSLoc.isMacroID())
141 continue;
142
143 for (auto Comment :
144 getCommentsInRange(Ctx, SourceRange(BeginSLoc, EndSLoc))) {
145 llvm::SmallVector<StringRef, 2> Matches;
146 if (IdentRE.match(Comment.second, &Matches)) {
147 if (Matches[2] != II->getName()) {
148 {
149 DiagnosticBuilder Diag =
150 diag(Comment.first, "argument name '%0' in comment does not "
151 "match parameter name %1")
152 << Matches[2] << II;
153 if (isLikelyTypo(Callee->parameters(), Matches[2], i)) {
154 Diag << FixItHint::CreateReplacement(
155 Comment.first,
156 (Matches[1] + II->getName() + Matches[3]).str());
157 }
158 }
159 diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note)
160 << II;
161 }
162 }
163 }
164 }
165 }
166
167 void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
168 const Expr *E = Result.Nodes.getNodeAs<Expr>("expr");
169 if (auto Call = dyn_cast<CallExpr>(E)) {
170 const FunctionDecl *Callee = Call->getDirectCallee();
171 if (!Callee)
172 return;
173
174 checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
175 llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
176 } else {
177 auto Construct = cast<CXXConstructExpr>(E);
178 checkCallArgs(
179 Result.Context, Construct->getConstructor(),
180 Construct->getParenOrBraceRange().getBegin(),
181 llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
182 }
183 }
184
185 } // namespace misc
186 } // namespace tidy
187 } // namespace clang
ArgumentCommentCheck.h
clang::tidy::misc::ArgumentCommentCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
Definition: ArgumentCommentCheck.cpp:27
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
SM
SourceManager & SM
Definition: IncludeOrderCheck.cpp:44
clang::tidy::misc::ArgumentCommentCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ArgumentCommentCheck.cpp:167
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:35
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:124
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121