blob: 923081dda6e0093fd774ac1785eec1223f4724f2 [file] [log] [blame]
Dmitri Gribenkoec925312012-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 Gribenkof26054f2012-07-11 21:38:39 +000011#include "clang/AST/CommentDiagnostic.h"
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +000012#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000013#include "clang/AST/Decl.h"
Dmitri Gribenko34df2202012-07-31 22:37:06 +000014#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000015#include "clang/Basic/SourceManager.h"
Dmitri Gribenkoec925312012-07-06 00:28:32 +000016#include "llvm/ADT/StringSwitch.h"
17
18namespace clang {
19namespace comments {
20
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000021Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +000022 DiagnosticsEngine &Diags, const CommandTraits &Traits) :
23 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +000024 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000025}
26
27void Sema::setDecl(const Decl *D) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +000028 if (!D)
29 return;
30
31 ThisDeclInfo = new (Allocator) DeclInfo;
32 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenkoe6213dd2012-08-01 23:21:57 +000033 ThisDeclInfo->IsFilled = false;
Dmitri Gribenkoec925312012-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 Gribenkoa9770ad2012-08-06 19:03:12 +000047void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
48 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +000049 Command->setArgs(Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000050}
51
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +000052void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
53 ParagraphComment *Paragraph) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +000054 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000055 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +000056 checkBlockCommandDuplicate(Command);
Dmitri Gribenko64305832012-08-03 21:15:32 +000057 checkReturnsCommand(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000058}
59
60ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
61 SourceLocation LocEnd,
62 StringRef Name) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000063 ParamCommandComment *Command =
64 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
65
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +000066 if (!isFunctionDecl())
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +000072}
73
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +000074void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
75 SourceLocation ArgLocBegin,
76 SourceLocation ArgLocEnd,
77 StringRef Arg) {
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +000099
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000110 } else {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000111 Direction = ParamCommandComment::In;
112 RemovingWhitespaceHelped = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000113 }
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000125 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000126 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000127}
128
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000129void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
130 SourceLocation ArgLocBegin,
131 SourceLocation ArgLocEnd,
132 StringRef Arg) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000133 // Parser will not feed us more arguments than needed.
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000134 assert(Command->getNumArgs() == 0);
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000145}
146
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000147void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
148 ParagraphComment *Paragraph) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000149 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000150 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000151}
152
Dmitri Gribenko34df2202012-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 Gribenko8e5d5f12012-08-06 21:31:15 +0000159 if (!isTemplateOrSpecialization())
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000167void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
168 SourceLocation ArgLocBegin,
169 SourceLocation ArgLocEnd,
170 StringRef Arg) {
Dmitri Gribenko34df2202012-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 Gribenko8e5d5f12012-08-06 21:31:15 +0000180 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000181 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000182 return;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000183 }
184
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000185 const TemplateParameterList *TemplateParameters =
186 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000201 return;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000209 return;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000227 return;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000228}
229
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000230void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
231 ParagraphComment *Paragraph) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000232 Command->setParagraph(Paragraph);
233 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000234}
235
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000236InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
237 SourceLocation CommandLocEnd,
238 StringRef CommandName) {
239 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000240 return new (Allocator) InlineCommandComment(
241 CommandLocBegin,
242 CommandLocEnd,
243 CommandName,
244 getInlineCommandRenderKind(CommandName),
245 Args);
Dmitri Gribenkoec925312012-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 Gribenkod73e4ce2012-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 Gribenkoec925312012-07-06 00:28:32 +0000265}
266
267InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
268 SourceLocation LocEnd,
269 StringRef Name) {
270 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000271 return new (Allocator) InlineCommandComment(
272 LocBegin, LocEnd, Name,
273 InlineCommandComment::RenderNormal,
274 Args);
Dmitri Gribenkoec925312012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000296void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenkoec925312012-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 Gribenkoec925312012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000317HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
318 StringRef TagName) {
319 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000320}
321
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000322void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000323 HTMLStartTagComment *Tag,
324 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000325 SourceLocation GreaterLoc,
326 bool IsSelfClosing) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000327 Tag->setAttrs(Attrs);
328 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000329 if (IsSelfClosing)
330 Tag->setSelfClosing();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000331 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000332 HTMLOpenTags.push_back(Tag);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000333}
334
Dmitri Gribenkoe00ffc72012-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 Gribenko9460fbf2012-07-12 23:37:09 +0000344 }
345
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000346 bool FoundOpen = false;
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000347 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkof26054f2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000356 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
357 << HET->getSourceRange();
358 return HET;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000359 }
360
361 while (!HTMLOpenTags.empty()) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000362 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000363 HTMLOpenTags.pop_back();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000364 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000365 if (LastNotClosedTagName == TagName)
366 break;
367
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000368 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000369 continue;
370
371 bool OpenLineInvalid;
372 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000373 HST->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000374 &OpenLineInvalid);
375 bool CloseLineInvalid;
376 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000377 HET->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000378 &CloseLineInvalid);
379
380 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenkoe00ffc72012-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 Gribenkof26054f2012-07-11 21:38:39 +0000384 else {
Dmitri Gribenkoe00ffc72012-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 Gribenkof26054f2012-07-11 21:38:39 +0000390 }
391 }
392
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000393 return HET;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000394}
395
396FullComment *Sema::actOnFullComment(
397 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko219bd152012-08-24 17:45:39 +0000398 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
399 resolveParamCommandIndexes(FC);
400 return FC;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000401}
402
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000403void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
404 ParagraphComment *Paragraph = Command->getParagraph();
405 if (Paragraph->isWhitespace()) {
406 SourceLocation DiagLoc;
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000407 if (Command->getNumArgs() > 0)
408 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkof26054f2012-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 Gribenko64305832012-08-03 21:15:32 +0000417void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000418 if (!Traits.isReturnsCommand(Command->getCommandName()))
Dmitri Gribenko64305832012-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 Gribenko558babc2012-08-06 16:29:26 +0000425 if (ThisDeclInfo->IsObjCMethod)
426 DiagKind = 3;
427 else
428 DiagKind = 0;
Dmitri Gribenko64305832012-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 Gribenko5ec0c752012-08-06 17:08:27 +0000451void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
452 StringRef Name = Command->getCommandName();
453 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000454 if (Traits.isBriefCommand(Name)) {
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +0000455 if (!BriefCommand) {
456 BriefCommand = Command;
457 return;
458 }
459 PrevCommand = BriefCommand;
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000460 } else if (Traits.isReturnsCommand(Name)) {
Dmitri Gribenko5ec0c752012-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 Gribenko219bd152012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000569bool Sema::isFunctionDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000570 if (!ThisDeclInfo)
571 return false;
572 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000573 inspectThisDecl();
Dmitri Gribenko37a7faf2012-08-02 21:45:39 +0000574 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000575}
576
Dmitri Gribenko8e5d5f12012-08-06 21:31:15 +0000577bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000578 if (!ThisDeclInfo)
579 return false;
580 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000581 inspectThisDecl();
Dmitri Gribenko8e5d5f12012-08-06 21:31:15 +0000582 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000583}
584
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000585ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000586 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000587 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000588 return ThisDeclInfo->ParamVars;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000589}
590
591void Sema::inspectThisDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000592 ThisDeclInfo->fill();
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000593}
594
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000595unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000596 ArrayRef<const ParmVarDecl *> ParamVars) {
597 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkof26054f2012-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 Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000659unsigned Sema::correctTypoInParmVarReference(
660 StringRef Typo,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000661 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000670
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000691 }
692 }
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000693 return false;
694}
695} // unnamed namespace
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000696
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000735}
736
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000737InlineCommandComment::RenderKind
738Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000739 assert(Traits.isInlineCommand(Name));
Dmitri Gribenkod73e4ce2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000748bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000749 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko9460fbf2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000764bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko9460fbf2012-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 Gribenkoec925312012-07-06 00:28:32 +0000771}
772
773} // end namespace comments
774} // end namespace clang
775