blob: 923081dda6e0093fd774ac1785eec1223f4724f2 [file] [log] [blame]
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +00001//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
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 "clang/AST/CommentSema.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000011#include "clang/AST/CommentDiagnostic.h"
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000012#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000013#include "clang/AST/Decl.h"
Dmitri Gribenko96b09862012-07-31 22:37:06 +000014#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000015#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000016#include "llvm/ADT/StringSwitch.h"
17
18namespace clang {
19namespace comments {
20
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000021Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000022 DiagnosticsEngine &Diags, const CommandTraits &Traits) :
23 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Dmitri Gribenko9443c572012-08-06 17:08:27 +000024 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000025}
26
27void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000028 if (!D)
29 return;
30
31 ThisDeclInfo = new (Allocator) DeclInfo;
32 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenko651f8ce2012-08-01 23:21:57 +000033 ThisDeclInfo->IsFilled = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000034}
35
36ParagraphComment *Sema::actOnParagraphComment(
37 ArrayRef<InlineContentComment *> Content) {
38 return new (Allocator) ParagraphComment(Content);
39}
40
41BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
42 SourceLocation LocEnd,
43 StringRef Name) {
44 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
45}
46
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000047void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
48 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000049 Command->setArgs(Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000050}
51
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000052void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
53 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000054 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000055 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000056 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000057 checkReturnsCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000058}
59
60ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
61 SourceLocation LocEnd,
62 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000063 ParamCommandComment *Command =
64 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
65
Dmitri Gribenko8487c522012-07-23 17:40:30 +000066 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000067 Diag(Command->getLocation(),
68 diag::warn_doc_param_not_attached_to_a_function_decl)
69 << Command->getCommandNameRange();
70
71 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000072}
73
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000074void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
75 SourceLocation ArgLocBegin,
76 SourceLocation ArgLocEnd,
77 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000078 ParamCommandComment::PassDirection Direction;
79 std::string ArgLower = Arg.lower();
80 // TODO: optimize: lower Name first (need an API in SmallString for that),
81 // after that StringSwitch.
82 if (ArgLower == "[in]")
83 Direction = ParamCommandComment::In;
84 else if (ArgLower == "[out]")
85 Direction = ParamCommandComment::Out;
86 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
87 Direction = ParamCommandComment::InOut;
88 else {
89 // Remove spaces.
90 std::string::iterator O = ArgLower.begin();
91 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
92 I != E; ++I) {
93 const char C = *I;
94 if (C != ' ' && C != '\n' && C != '\r' &&
95 C != '\t' && C != '\v' && C != '\f')
96 *O++ = C;
97 }
98 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000099
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000100 bool RemovingWhitespaceHelped = false;
101 if (ArgLower == "[in]") {
102 Direction = ParamCommandComment::In;
103 RemovingWhitespaceHelped = true;
104 } else if (ArgLower == "[out]") {
105 Direction = ParamCommandComment::Out;
106 RemovingWhitespaceHelped = true;
107 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
108 Direction = ParamCommandComment::InOut;
109 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000110 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000111 Direction = ParamCommandComment::In;
112 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000113 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000114
115 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
116 if (RemovingWhitespaceHelped)
117 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
118 << ArgRange
119 << FixItHint::CreateReplacement(
120 ArgRange,
121 ParamCommandComment::getDirectionAsString(Direction));
122 else
123 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
124 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000125 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000126 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000127}
128
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000129void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
130 SourceLocation ArgLocBegin,
131 SourceLocation ArgLocEnd,
132 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000133 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000134 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000135
136 if (!Command->isDirectionExplicit()) {
137 // User didn't provide a direction argument.
138 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
139 }
140 typedef BlockCommandComment::Argument Argument;
141 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
142 ArgLocEnd),
143 Arg);
144 Command->setArgs(llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000145}
146
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000147void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
148 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000149 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000150 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000151}
152
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000153TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
154 SourceLocation LocEnd,
155 StringRef Name) {
156 TParamCommandComment *Command =
157 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
158
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000159 if (!isTemplateOrSpecialization())
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000160 Diag(Command->getLocation(),
161 diag::warn_doc_tparam_not_attached_to_a_template_decl)
162 << Command->getCommandNameRange();
163
164 return Command;
165}
166
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000167void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
168 SourceLocation ArgLocBegin,
169 SourceLocation ArgLocEnd,
170 StringRef Arg) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000171 // Parser will not feed us more arguments than needed.
172 assert(Command->getNumArgs() == 0);
173
174 typedef BlockCommandComment::Argument Argument;
175 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
176 ArgLocEnd),
177 Arg);
178 Command->setArgs(llvm::makeArrayRef(A, 1));
179
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000180 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000181 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000182 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000183 }
184
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000185 const TemplateParameterList *TemplateParameters =
186 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000187 SmallVector<unsigned, 2> Position;
188 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
189 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
190 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
191 TemplateParameterDocs.find(Arg);
192 if (PrevCommandIt != TemplateParameterDocs.end()) {
193 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
194 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
195 << Arg << ArgRange;
196 TParamCommandComment *PrevCommand = PrevCommandIt->second;
197 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
198 << PrevCommand->getParamNameRange();
199 }
200 TemplateParameterDocs[Arg] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000201 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000202 }
203
204 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
205 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
206 << Arg << ArgRange;
207
208 if (!TemplateParameters || TemplateParameters->size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000209 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000210
211 StringRef CorrectedName;
212 if (TemplateParameters->size() == 1) {
213 const NamedDecl *Param = TemplateParameters->getParam(0);
214 const IdentifierInfo *II = Param->getIdentifier();
215 if (II)
216 CorrectedName = II->getName();
217 } else {
218 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
219 }
220
221 if (!CorrectedName.empty()) {
222 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
223 << CorrectedName
224 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
225 }
226
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000227 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000228}
229
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000230void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
231 ParagraphComment *Paragraph) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000232 Command->setParagraph(Paragraph);
233 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000234}
235
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000236InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
237 SourceLocation CommandLocEnd,
238 StringRef CommandName) {
239 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000240 return new (Allocator) InlineCommandComment(
241 CommandLocBegin,
242 CommandLocEnd,
243 CommandName,
244 getInlineCommandRenderKind(CommandName),
245 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000246}
247
248InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
249 SourceLocation CommandLocEnd,
250 StringRef CommandName,
251 SourceLocation ArgLocBegin,
252 SourceLocation ArgLocEnd,
253 StringRef Arg) {
254 typedef InlineCommandComment::Argument Argument;
255 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
256 ArgLocEnd),
257 Arg);
258
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000259 return new (Allocator) InlineCommandComment(
260 CommandLocBegin,
261 CommandLocEnd,
262 CommandName,
263 getInlineCommandRenderKind(CommandName),
264 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000265}
266
267InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
268 SourceLocation LocEnd,
269 StringRef Name) {
270 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000271 return new (Allocator) InlineCommandComment(
272 LocBegin, LocEnd, Name,
273 InlineCommandComment::RenderNormal,
274 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000275}
276
277TextComment *Sema::actOnText(SourceLocation LocBegin,
278 SourceLocation LocEnd,
279 StringRef Text) {
280 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
281}
282
283VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
284 StringRef Name) {
285 return new (Allocator) VerbatimBlockComment(
286 Loc,
287 Loc.getLocWithOffset(1 + Name.size()),
288 Name);
289}
290
291VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
292 StringRef Text) {
293 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
294}
295
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000296void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000297 VerbatimBlockComment *Block,
298 SourceLocation CloseNameLocBegin,
299 StringRef CloseName,
300 ArrayRef<VerbatimBlockLineComment *> Lines) {
301 Block->setCloseName(CloseName, CloseNameLocBegin);
302 Block->setLines(Lines);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000303}
304
305VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
306 StringRef Name,
307 SourceLocation TextBegin,
308 StringRef Text) {
309 return new (Allocator) VerbatimLineComment(
310 LocBegin,
311 TextBegin.getLocWithOffset(Text.size()),
312 Name,
313 TextBegin,
314 Text);
315}
316
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000317HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
318 StringRef TagName) {
319 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000320}
321
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000322void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000323 HTMLStartTagComment *Tag,
324 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000325 SourceLocation GreaterLoc,
326 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000327 Tag->setAttrs(Attrs);
328 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000329 if (IsSelfClosing)
330 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000331 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000332 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000333}
334
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000335HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
336 SourceLocation LocEnd,
337 StringRef TagName) {
338 HTMLEndTagComment *HET =
339 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
340 if (isHTMLEndTagForbidden(TagName)) {
341 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
342 << TagName << HET->getSourceRange();
343 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000344 }
345
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000346 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000347 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000348 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
349 I != E; ++I) {
350 if ((*I)->getTagName() == TagName) {
351 FoundOpen = true;
352 break;
353 }
354 }
355 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000356 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
357 << HET->getSourceRange();
358 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000359 }
360
361 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000362 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000363 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000364 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000365 if (LastNotClosedTagName == TagName)
366 break;
367
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000368 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000369 continue;
370
371 bool OpenLineInvalid;
372 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000373 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000374 &OpenLineInvalid);
375 bool CloseLineInvalid;
376 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000377 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000378 &CloseLineInvalid);
379
380 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000381 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
382 << HST->getTagName() << HET->getTagName()
383 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000384 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000385 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
386 << HST->getTagName() << HET->getTagName()
387 << HST->getSourceRange();
388 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
389 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000390 }
391 }
392
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000393 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000394}
395
396FullComment *Sema::actOnFullComment(
397 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000398 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
399 resolveParamCommandIndexes(FC);
400 return FC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000401}
402
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000403void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
404 ParagraphComment *Paragraph = Command->getParagraph();
405 if (Paragraph->isWhitespace()) {
406 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000407 if (Command->getNumArgs() > 0)
408 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000409 if (!DiagLoc.isValid())
410 DiagLoc = Command->getCommandNameRange().getEnd();
411 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
412 << Command->getCommandName()
413 << Command->getSourceRange();
414 }
415}
416
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000417void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000418 if (!Traits.isReturnsCommand(Command->getCommandName()))
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000419 return;
420 if (isFunctionDecl()) {
421 if (ThisDeclInfo->ResultType->isVoidType()) {
422 unsigned DiagKind;
423 switch (ThisDeclInfo->ThisDecl->getKind()) {
424 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000425 if (ThisDeclInfo->IsObjCMethod)
426 DiagKind = 3;
427 else
428 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000429 break;
430 case Decl::CXXConstructor:
431 DiagKind = 1;
432 break;
433 case Decl::CXXDestructor:
434 DiagKind = 2;
435 break;
436 }
437 Diag(Command->getLocation(),
438 diag::warn_doc_returns_attached_to_a_void_function)
439 << Command->getCommandName()
440 << DiagKind
441 << Command->getSourceRange();
442 }
443 return;
444 }
445 Diag(Command->getLocation(),
446 diag::warn_doc_returns_not_attached_to_a_function_decl)
447 << Command->getCommandName()
448 << Command->getSourceRange();
449}
450
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000451void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
452 StringRef Name = Command->getCommandName();
453 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000454 if (Traits.isBriefCommand(Name)) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000455 if (!BriefCommand) {
456 BriefCommand = Command;
457 return;
458 }
459 PrevCommand = BriefCommand;
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000460 } else if (Traits.isReturnsCommand(Name)) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000461 if (!ReturnsCommand) {
462 ReturnsCommand = Command;
463 return;
464 }
465 PrevCommand = ReturnsCommand;
466 } else {
467 // We don't want to check this command for duplicates.
468 return;
469 }
470 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
471 << Name
472 << Command->getSourceRange();
473 if (Name == PrevCommand->getCommandName())
474 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
475 << PrevCommand->getCommandName()
476 << Command->getSourceRange();
477 else
478 Diag(PrevCommand->getLocation(),
479 diag::note_doc_block_command_previous_alias)
480 << PrevCommand->getCommandName()
481 << Name;
482}
483
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000484void Sema::resolveParamCommandIndexes(const FullComment *FC) {
485 if (!isFunctionDecl()) {
486 // We already warned that \\param commands are not attached to a function
487 // decl.
488 return;
489 }
490
491 llvm::SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
492
493 // Comment AST nodes that correspond to \c ParamVars for which we have
494 // found a \\param command or NULL if no documentation was found so far.
495 llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
496
497 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
498 ParamVarDocs.resize(ParamVars.size(), NULL);
499
500 // First pass over all \\param commands: resolve all parameter names.
501 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
502 I != E; ++I) {
503 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
504 if (!PCC || !PCC->hasParamName())
505 continue;
506 StringRef ParamName = PCC->getParamName();
507
508 // Check that referenced parameter name is in the function decl.
509 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
510 ParamVars);
511 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
512 UnresolvedParamCommands.push_back(PCC);
513 continue;
514 }
515 PCC->setParamIndex(ResolvedParamIndex);
516 if (ParamVarDocs[ResolvedParamIndex]) {
517 SourceRange ArgRange = PCC->getParamNameRange();
518 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
519 << ParamName << ArgRange;
520 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
521 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
522 << PrevCommand->getParamNameRange();
523 }
524 ParamVarDocs[ResolvedParamIndex] = PCC;
525 }
526
527 // Find parameter declarations that have no corresponding \\param.
528 llvm::SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
529 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
530 if (!ParamVarDocs[i])
531 OrphanedParamDecls.push_back(ParamVars[i]);
532 }
533
534 // Second pass over unresolved \\param commands: do typo correction.
535 // Suggest corrections from a set of parameter declarations that have no
536 // corresponding \\param.
537 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
538 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
539
540 SourceRange ArgRange = PCC->getParamNameRange();
541 StringRef ParamName = PCC->getParamName();
542 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
543 << ParamName << ArgRange;
544
545 // All parameters documented -- can't suggest a correction.
546 if (OrphanedParamDecls.size() == 0)
547 continue;
548
549 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
550 if (OrphanedParamDecls.size() == 1) {
551 // If one parameter is not documented then that parameter is the only
552 // possible suggestion.
553 CorrectedParamIndex = 0;
554 } else {
555 // Do typo correction.
556 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
557 OrphanedParamDecls);
558 }
559 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
560 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
561 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
562 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
563 << CorrectedII->getName()
564 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
565 }
566 }
567}
568
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000569bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000570 if (!ThisDeclInfo)
571 return false;
572 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000573 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000574 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000575}
576
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000577bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000578 if (!ThisDeclInfo)
579 return false;
580 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000581 inspectThisDecl();
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000582 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000583}
584
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000585ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000586 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000587 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000588 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000589}
590
591void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000592 ThisDeclInfo->fill();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000593}
594
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000595unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000596 ArrayRef<const ParmVarDecl *> ParamVars) {
597 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000598 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
599 if (II && II->getName() == Name)
600 return i;
601 }
602 return ParamCommandComment::InvalidParamIndex;
603}
604
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000605namespace {
606class SimpleTypoCorrector {
607 StringRef Typo;
608 const unsigned MaxEditDistance;
609
610 const NamedDecl *BestDecl;
611 unsigned BestEditDistance;
612 unsigned BestIndex;
613 unsigned NextIndex;
614
615public:
616 SimpleTypoCorrector(StringRef Typo) :
617 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
618 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
619 BestIndex(0), NextIndex(0)
620 { }
621
622 void addDecl(const NamedDecl *ND);
623
624 const NamedDecl *getBestDecl() const {
625 if (BestEditDistance > MaxEditDistance)
626 return NULL;
627
628 return BestDecl;
629 }
630
631 unsigned getBestDeclIndex() const {
632 assert(getBestDecl());
633 return BestIndex;
634 }
635};
636
637void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
638 unsigned CurrIndex = NextIndex++;
639
640 const IdentifierInfo *II = ND->getIdentifier();
641 if (!II)
642 return;
643
644 StringRef Name = II->getName();
645 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
646 if (MinPossibleEditDistance > 0 &&
647 Typo.size() / MinPossibleEditDistance < 3)
648 return;
649
650 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
651 if (EditDistance < BestEditDistance) {
652 BestEditDistance = EditDistance;
653 BestDecl = ND;
654 BestIndex = CurrIndex;
655 }
656}
657} // unnamed namespace
658
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000659unsigned Sema::correctTypoInParmVarReference(
660 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000661 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000662 SimpleTypoCorrector Corrector(Typo);
663 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
664 Corrector.addDecl(ParamVars[i]);
665 if (Corrector.getBestDecl())
666 return Corrector.getBestDeclIndex();
667 else
668 return ParamCommandComment::InvalidParamIndex;;
669}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000670
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000671namespace {
672bool ResolveTParamReferenceHelper(
673 StringRef Name,
674 const TemplateParameterList *TemplateParameters,
675 SmallVectorImpl<unsigned> *Position) {
676 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
677 const NamedDecl *Param = TemplateParameters->getParam(i);
678 const IdentifierInfo *II = Param->getIdentifier();
679 if (II && II->getName() == Name) {
680 Position->push_back(i);
681 return true;
682 }
683
684 if (const TemplateTemplateParmDecl *TTP =
685 dyn_cast<TemplateTemplateParmDecl>(Param)) {
686 Position->push_back(i);
687 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
688 Position))
689 return true;
690 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000691 }
692 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000693 return false;
694}
695} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000696
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000697bool Sema::resolveTParamReference(
698 StringRef Name,
699 const TemplateParameterList *TemplateParameters,
700 SmallVectorImpl<unsigned> *Position) {
701 Position->clear();
702 if (!TemplateParameters)
703 return false;
704
705 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
706}
707
708namespace {
709void CorrectTypoInTParamReferenceHelper(
710 const TemplateParameterList *TemplateParameters,
711 SimpleTypoCorrector &Corrector) {
712 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
713 const NamedDecl *Param = TemplateParameters->getParam(i);
714 Corrector.addDecl(Param);
715
716 if (const TemplateTemplateParmDecl *TTP =
717 dyn_cast<TemplateTemplateParmDecl>(Param))
718 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
719 Corrector);
720 }
721}
722} // unnamed namespace
723
724StringRef Sema::correctTypoInTParamReference(
725 StringRef Typo,
726 const TemplateParameterList *TemplateParameters) {
727 SimpleTypoCorrector Corrector(Typo);
728 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
729 if (const NamedDecl *ND = Corrector.getBestDecl()) {
730 const IdentifierInfo *II = ND->getIdentifier();
731 assert(II && "SimpleTypoCorrector should not return this decl");
732 return II->getName();
733 }
734 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000735}
736
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000737InlineCommandComment::RenderKind
738Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000739 assert(Traits.isInlineCommand(Name));
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000740
741 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
742 .Case("b", InlineCommandComment::RenderBold)
743 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
744 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
745 .Default(InlineCommandComment::RenderNormal);
746}
747
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000748bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000749 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000750 .Case("p", true)
751 .Case("li", true)
752 .Case("dt", true)
753 .Case("dd", true)
754 .Case("tr", true)
755 .Case("th", true)
756 .Case("td", true)
757 .Case("thead", true)
758 .Case("tfoot", true)
759 .Case("tbody", true)
760 .Case("colgroup", true)
761 .Default(false);
762}
763
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000764bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000765 return llvm::StringSwitch<bool>(Name)
766 .Case("br", true)
767 .Case("hr", true)
768 .Case("img", true)
769 .Case("col", true)
770 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000771}
772
773} // end namespace comments
774} // end namespace clang
775