blob: cbfbc4eb24dcea419709a41a866e787746b861e8 [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"
12#include "clang/AST/Decl.h"
Dmitri Gribenko34df2202012-07-31 22:37:06 +000013#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000014#include "clang/Basic/SourceManager.h"
Dmitri Gribenkoec925312012-07-06 00:28:32 +000015#include "llvm/ADT/StringSwitch.h"
16
17namespace clang {
18namespace comments {
19
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000020Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
21 DiagnosticsEngine &Diags) :
Dmitri Gribenko527ab212012-08-01 23:08:09 +000022 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
23 ThisDeclInfo(NULL) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000024}
25
26void Sema::setDecl(const Decl *D) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +000027 if (!D)
28 return;
29
30 ThisDeclInfo = new (Allocator) DeclInfo;
31 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenkoe6213dd2012-08-01 23:21:57 +000032 ThisDeclInfo->IsFilled = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +000033}
34
35ParagraphComment *Sema::actOnParagraphComment(
36 ArrayRef<InlineContentComment *> Content) {
37 return new (Allocator) ParagraphComment(Content);
38}
39
40BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
41 SourceLocation LocEnd,
42 StringRef Name) {
43 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
44}
45
46BlockCommandComment *Sema::actOnBlockCommandArgs(
47 BlockCommandComment *Command,
48 ArrayRef<BlockCommandComment::Argument> Args) {
49 Command->setArgs(Args);
50 return Command;
51}
52
53BlockCommandComment *Sema::actOnBlockCommandFinish(
54 BlockCommandComment *Command,
55 ParagraphComment *Paragraph) {
56 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000057 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000058 return Command;
59}
60
61ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
62 SourceLocation LocEnd,
63 StringRef Name) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000064 ParamCommandComment *Command =
65 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
66
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +000067 if (!isFunctionDecl())
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000068 Diag(Command->getLocation(),
69 diag::warn_doc_param_not_attached_to_a_function_decl)
70 << Command->getCommandNameRange();
71
72 return Command;
Dmitri Gribenkoec925312012-07-06 00:28:32 +000073}
74
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000075ParamCommandComment *Sema::actOnParamCommandDirectionArg(
76 ParamCommandComment *Command,
Dmitri Gribenkoec925312012-07-06 00:28:32 +000077 SourceLocation ArgLocBegin,
78 SourceLocation ArgLocEnd,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000079 StringRef Arg) {
80 ParamCommandComment::PassDirection Direction;
81 std::string ArgLower = Arg.lower();
82 // TODO: optimize: lower Name first (need an API in SmallString for that),
83 // after that StringSwitch.
84 if (ArgLower == "[in]")
85 Direction = ParamCommandComment::In;
86 else if (ArgLower == "[out]")
87 Direction = ParamCommandComment::Out;
88 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
89 Direction = ParamCommandComment::InOut;
90 else {
91 // Remove spaces.
92 std::string::iterator O = ArgLower.begin();
93 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
94 I != E; ++I) {
95 const char C = *I;
96 if (C != ' ' && C != '\n' && C != '\r' &&
97 C != '\t' && C != '\v' && C != '\f')
98 *O++ = C;
99 }
100 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000101
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000102 bool RemovingWhitespaceHelped = false;
103 if (ArgLower == "[in]") {
104 Direction = ParamCommandComment::In;
105 RemovingWhitespaceHelped = true;
106 } else if (ArgLower == "[out]") {
107 Direction = ParamCommandComment::Out;
108 RemovingWhitespaceHelped = true;
109 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
110 Direction = ParamCommandComment::InOut;
111 RemovingWhitespaceHelped = true;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000112 } else {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000113 Direction = ParamCommandComment::In;
114 RemovingWhitespaceHelped = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000115 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000116
117 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
118 if (RemovingWhitespaceHelped)
119 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
120 << ArgRange
121 << FixItHint::CreateReplacement(
122 ArgRange,
123 ParamCommandComment::getDirectionAsString(Direction));
124 else
125 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
126 << ArgRange;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000127 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000128 Command->setDirection(Direction, /* Explicit = */ true);
129 return Command;
130}
131
132ParamCommandComment *Sema::actOnParamCommandParamNameArg(
133 ParamCommandComment *Command,
134 SourceLocation ArgLocBegin,
135 SourceLocation ArgLocEnd,
136 StringRef Arg) {
137 // Parser will not feed us more arguments than needed.
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000138 assert(Command->getNumArgs() == 0);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000139
140 if (!Command->isDirectionExplicit()) {
141 // User didn't provide a direction argument.
142 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
143 }
144 typedef BlockCommandComment::Argument Argument;
145 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
146 ArgLocEnd),
147 Arg);
148 Command->setArgs(llvm::makeArrayRef(A, 1));
149
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000150 if (!isFunctionDecl()) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000151 // We already warned that this \\param is not attached to a function decl.
152 return Command;
153 }
154
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000155 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
156
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000157 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000158 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000159 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
160 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko8f0f1b02012-07-24 21:44:16 +0000161 if (ParamVarDocs[ResolvedParamIndex]) {
162 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
163 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
164 << Arg << ArgRange;
165 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
166 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
167 << PrevCommand->getParamNameRange();
168 }
169 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000170 return Command;
171 }
172
173 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
174 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
175 << Arg << ArgRange;
176
Dmitri Gribenkof24990a2012-07-27 21:34:43 +0000177 // No parameters -- can't suggest a correction.
178 if (ParamVars.size() == 0)
179 return Command;
180
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000181 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000182 if (ParamVars.size() == 1) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000183 // If function has only one parameter then only that parameter
184 // can be documented.
185 CorrectedParamIndex = 0;
186 } else {
187 // Do typo correction.
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000188 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000189 }
190 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
191 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
192 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
193 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
194 << CorrectedII->getName()
195 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
196 }
197
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000198 return Command;
199}
200
201ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
202 ParagraphComment *Paragraph) {
203 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000204 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000205 return Command;
206}
207
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000208TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
209 SourceLocation LocEnd,
210 StringRef Name) {
211 TParamCommandComment *Command =
212 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
213
214 if (!isTemplateDecl())
215 Diag(Command->getLocation(),
216 diag::warn_doc_tparam_not_attached_to_a_template_decl)
217 << Command->getCommandNameRange();
218
219 return Command;
220}
221
222TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
223 TParamCommandComment *Command,
224 SourceLocation ArgLocBegin,
225 SourceLocation ArgLocEnd,
226 StringRef Arg) {
227 // Parser will not feed us more arguments than needed.
228 assert(Command->getNumArgs() == 0);
229
230 typedef BlockCommandComment::Argument Argument;
231 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
232 ArgLocEnd),
233 Arg);
234 Command->setArgs(llvm::makeArrayRef(A, 1));
235
236 if (!isTemplateDecl()) {
237 // We already warned that this \\tparam is not attached to a template decl.
238 return Command;
239 }
240
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000241 const TemplateParameterList *TemplateParameters =
242 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000243 SmallVector<unsigned, 2> Position;
244 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
245 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
246 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
247 TemplateParameterDocs.find(Arg);
248 if (PrevCommandIt != TemplateParameterDocs.end()) {
249 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
250 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
251 << Arg << ArgRange;
252 TParamCommandComment *PrevCommand = PrevCommandIt->second;
253 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
254 << PrevCommand->getParamNameRange();
255 }
256 TemplateParameterDocs[Arg] = Command;
257 return Command;
258 }
259
260 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
261 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
262 << Arg << ArgRange;
263
264 if (!TemplateParameters || TemplateParameters->size() == 0)
265 return Command;
266
267 StringRef CorrectedName;
268 if (TemplateParameters->size() == 1) {
269 const NamedDecl *Param = TemplateParameters->getParam(0);
270 const IdentifierInfo *II = Param->getIdentifier();
271 if (II)
272 CorrectedName = II->getName();
273 } else {
274 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
275 }
276
277 if (!CorrectedName.empty()) {
278 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
279 << CorrectedName
280 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
281 }
282
283 return Command;
284}
285
286TParamCommandComment *Sema::actOnTParamCommandFinish(
287 TParamCommandComment *Command,
288 ParagraphComment *Paragraph) {
289 Command->setParagraph(Paragraph);
290 checkBlockCommandEmptyParagraph(Command);
291 return Command;
292}
293
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000294InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
295 SourceLocation CommandLocEnd,
296 StringRef CommandName) {
297 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000298 return new (Allocator) InlineCommandComment(
299 CommandLocBegin,
300 CommandLocEnd,
301 CommandName,
302 getInlineCommandRenderKind(CommandName),
303 Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000304}
305
306InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
307 SourceLocation CommandLocEnd,
308 StringRef CommandName,
309 SourceLocation ArgLocBegin,
310 SourceLocation ArgLocEnd,
311 StringRef Arg) {
312 typedef InlineCommandComment::Argument Argument;
313 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
314 ArgLocEnd),
315 Arg);
316
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000317 return new (Allocator) InlineCommandComment(
318 CommandLocBegin,
319 CommandLocEnd,
320 CommandName,
321 getInlineCommandRenderKind(CommandName),
322 llvm::makeArrayRef(A, 1));
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000323}
324
325InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
326 SourceLocation LocEnd,
327 StringRef Name) {
328 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000329 return new (Allocator) InlineCommandComment(
330 LocBegin, LocEnd, Name,
331 InlineCommandComment::RenderNormal,
332 Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000333}
334
335TextComment *Sema::actOnText(SourceLocation LocBegin,
336 SourceLocation LocEnd,
337 StringRef Text) {
338 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
339}
340
341VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
342 StringRef Name) {
343 return new (Allocator) VerbatimBlockComment(
344 Loc,
345 Loc.getLocWithOffset(1 + Name.size()),
346 Name);
347}
348
349VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
350 StringRef Text) {
351 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
352}
353
354VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
355 VerbatimBlockComment *Block,
356 SourceLocation CloseNameLocBegin,
357 StringRef CloseName,
358 ArrayRef<VerbatimBlockLineComment *> Lines) {
359 Block->setCloseName(CloseName, CloseNameLocBegin);
360 Block->setLines(Lines);
361 return Block;
362}
363
364VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
365 StringRef Name,
366 SourceLocation TextBegin,
367 StringRef Text) {
368 return new (Allocator) VerbatimLineComment(
369 LocBegin,
370 TextBegin.getLocWithOffset(Text.size()),
371 Name,
372 TextBegin,
373 Text);
374}
375
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000376HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
377 StringRef TagName) {
378 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000379}
380
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000381HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
382 HTMLStartTagComment *Tag,
383 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000384 SourceLocation GreaterLoc,
385 bool IsSelfClosing) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000386 Tag->setAttrs(Attrs);
387 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000388 if (IsSelfClosing)
389 Tag->setSelfClosing();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000390 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000391 HTMLOpenTags.push_back(Tag);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000392 return Tag;
393}
394
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000395HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
396 SourceLocation LocEnd,
397 StringRef TagName) {
398 HTMLEndTagComment *HET =
399 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
400 if (isHTMLEndTagForbidden(TagName)) {
401 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
402 << TagName << HET->getSourceRange();
403 return HET;
Dmitri Gribenko9460fbf2012-07-12 23:37:09 +0000404 }
405
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000406 bool FoundOpen = false;
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000407 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000408 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
409 I != E; ++I) {
410 if ((*I)->getTagName() == TagName) {
411 FoundOpen = true;
412 break;
413 }
414 }
415 if (!FoundOpen) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000416 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
417 << HET->getSourceRange();
418 return HET;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000419 }
420
421 while (!HTMLOpenTags.empty()) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000422 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000423 HTMLOpenTags.pop_back();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000424 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000425 if (LastNotClosedTagName == TagName)
426 break;
427
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000428 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000429 continue;
430
431 bool OpenLineInvalid;
432 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000433 HST->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000434 &OpenLineInvalid);
435 bool CloseLineInvalid;
436 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000437 HET->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000438 &CloseLineInvalid);
439
440 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000441 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
442 << HST->getTagName() << HET->getTagName()
443 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000444 else {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000445 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
446 << HST->getTagName() << HET->getTagName()
447 << HST->getSourceRange();
448 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
449 << HET->getSourceRange();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000450 }
451 }
452
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000453 return HET;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000454}
455
456FullComment *Sema::actOnFullComment(
457 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000458 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000459}
460
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000461void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
462 ParagraphComment *Paragraph = Command->getParagraph();
463 if (Paragraph->isWhitespace()) {
464 SourceLocation DiagLoc;
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000465 if (Command->getNumArgs() > 0)
466 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000467 if (!DiagLoc.isValid())
468 DiagLoc = Command->getCommandNameRange().getEnd();
469 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
470 << Command->getCommandName()
471 << Command->getSourceRange();
472 }
473}
474
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000475bool Sema::isFunctionDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000476 if (!ThisDeclInfo)
477 return false;
478 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000479 inspectThisDecl();
Dmitri Gribenko37a7faf2012-08-02 21:45:39 +0000480 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000481}
482
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000483bool Sema::isTemplateDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000484 if (!ThisDeclInfo)
485 return false;
486 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000487 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000488 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000489}
490
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000491ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000492 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000493 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000494 return ThisDeclInfo->ParamVars;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000495}
496
497void Sema::inspectThisDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000498 ThisDeclInfo->fill();
499 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000500}
501
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000502unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000503 ArrayRef<const ParmVarDecl *> ParamVars) {
504 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000505 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
506 if (II && II->getName() == Name)
507 return i;
508 }
509 return ParamCommandComment::InvalidParamIndex;
510}
511
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000512namespace {
513class SimpleTypoCorrector {
514 StringRef Typo;
515 const unsigned MaxEditDistance;
516
517 const NamedDecl *BestDecl;
518 unsigned BestEditDistance;
519 unsigned BestIndex;
520 unsigned NextIndex;
521
522public:
523 SimpleTypoCorrector(StringRef Typo) :
524 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
525 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
526 BestIndex(0), NextIndex(0)
527 { }
528
529 void addDecl(const NamedDecl *ND);
530
531 const NamedDecl *getBestDecl() const {
532 if (BestEditDistance > MaxEditDistance)
533 return NULL;
534
535 return BestDecl;
536 }
537
538 unsigned getBestDeclIndex() const {
539 assert(getBestDecl());
540 return BestIndex;
541 }
542};
543
544void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
545 unsigned CurrIndex = NextIndex++;
546
547 const IdentifierInfo *II = ND->getIdentifier();
548 if (!II)
549 return;
550
551 StringRef Name = II->getName();
552 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
553 if (MinPossibleEditDistance > 0 &&
554 Typo.size() / MinPossibleEditDistance < 3)
555 return;
556
557 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
558 if (EditDistance < BestEditDistance) {
559 BestEditDistance = EditDistance;
560 BestDecl = ND;
561 BestIndex = CurrIndex;
562 }
563}
564} // unnamed namespace
565
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000566unsigned Sema::correctTypoInParmVarReference(
567 StringRef Typo,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000568 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000569 SimpleTypoCorrector Corrector(Typo);
570 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
571 Corrector.addDecl(ParamVars[i]);
572 if (Corrector.getBestDecl())
573 return Corrector.getBestDeclIndex();
574 else
575 return ParamCommandComment::InvalidParamIndex;;
576}
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000577
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000578namespace {
579bool ResolveTParamReferenceHelper(
580 StringRef Name,
581 const TemplateParameterList *TemplateParameters,
582 SmallVectorImpl<unsigned> *Position) {
583 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
584 const NamedDecl *Param = TemplateParameters->getParam(i);
585 const IdentifierInfo *II = Param->getIdentifier();
586 if (II && II->getName() == Name) {
587 Position->push_back(i);
588 return true;
589 }
590
591 if (const TemplateTemplateParmDecl *TTP =
592 dyn_cast<TemplateTemplateParmDecl>(Param)) {
593 Position->push_back(i);
594 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
595 Position))
596 return true;
597 Position->pop_back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000598 }
599 }
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000600 return false;
601}
602} // unnamed namespace
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000603
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000604bool Sema::resolveTParamReference(
605 StringRef Name,
606 const TemplateParameterList *TemplateParameters,
607 SmallVectorImpl<unsigned> *Position) {
608 Position->clear();
609 if (!TemplateParameters)
610 return false;
611
612 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
613}
614
615namespace {
616void CorrectTypoInTParamReferenceHelper(
617 const TemplateParameterList *TemplateParameters,
618 SimpleTypoCorrector &Corrector) {
619 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
620 const NamedDecl *Param = TemplateParameters->getParam(i);
621 Corrector.addDecl(Param);
622
623 if (const TemplateTemplateParmDecl *TTP =
624 dyn_cast<TemplateTemplateParmDecl>(Param))
625 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
626 Corrector);
627 }
628}
629} // unnamed namespace
630
631StringRef Sema::correctTypoInTParamReference(
632 StringRef Typo,
633 const TemplateParameterList *TemplateParameters) {
634 SimpleTypoCorrector Corrector(Typo);
635 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
636 if (const NamedDecl *ND = Corrector.getBestDecl()) {
637 const IdentifierInfo *II = ND->getIdentifier();
638 assert(II && "SimpleTypoCorrector should not return this decl");
639 return II->getName();
640 }
641 return StringRef();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000642}
643
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000644// TODO: tablegen
645bool Sema::isBlockCommand(StringRef Name) {
646 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko31005192012-07-18 00:44:55 +0000647 .Cases("brief", "short", true)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000648 .Case("result", true)
649 .Case("return", true)
650 .Case("returns", true)
651 .Case("author", true)
652 .Case("authors", true)
653 .Case("pre", true)
654 .Case("post", true)
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000655 .Default(false) || isParamCommand(Name) || isTParamCommand(Name);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000656}
657
658bool Sema::isParamCommand(StringRef Name) {
659 return llvm::StringSwitch<bool>(Name)
660 .Case("param", true)
661 .Case("arg", true)
662 .Default(false);
663}
664
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000665bool Sema::isTParamCommand(StringRef Name) {
666 return Name == "tparam";
667}
668
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000669unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
670 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko31005192012-07-18 00:44:55 +0000671 .Cases("brief", "short", 0)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000672 .Case("pre", 0)
673 .Case("post", 0)
674 .Case("author", 0)
675 .Case("authors", 0)
676 .Default(0);
677}
678
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000679bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000680 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoe82ae812012-07-19 00:21:03 +0000681 .Case("b", true)
682 .Cases("c", "p", true)
683 .Cases("a", "e", "em", true)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000684 .Default(false);
685}
686
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000687InlineCommandComment::RenderKind
688Sema::getInlineCommandRenderKind(StringRef Name) const {
689 assert(isInlineCommand(Name));
690
691 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
692 .Case("b", InlineCommandComment::RenderBold)
693 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
694 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
695 .Default(InlineCommandComment::RenderNormal);
696}
697
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000698bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000699 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko9460fbf2012-07-12 23:37:09 +0000700 .Case("p", true)
701 .Case("li", true)
702 .Case("dt", true)
703 .Case("dd", true)
704 .Case("tr", true)
705 .Case("th", true)
706 .Case("td", true)
707 .Case("thead", true)
708 .Case("tfoot", true)
709 .Case("tbody", true)
710 .Case("colgroup", true)
711 .Default(false);
712}
713
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000714bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko9460fbf2012-07-12 23:37:09 +0000715 return llvm::StringSwitch<bool>(Name)
716 .Case("br", true)
717 .Case("hr", true)
718 .Case("img", true)
719 .Case("col", true)
720 .Default(false);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000721}
722
723} // end namespace comments
724} // end namespace clang
725