1 //===--- 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"
11 #include "clang/AST/CommentDiagnostic.h"
12 #include "clang/AST/CommentCommandTraits.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclTemplate.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "llvm/ADT/StringSwitch.h"
17 
18 namespace clang {
19 namespace comments {
20 
21 Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
22            DiagnosticsEngine &Diags, const CommandTraits &Traits) :
23     Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
24     ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
25 }
26 
27 void Sema::setDecl(const Decl *D) {
28   if (!D)
29     return;
30 
31   ThisDeclInfo = new (Allocator) DeclInfo;
32   ThisDeclInfo->ThisDecl = D;
33   ThisDeclInfo->IsFilled = false;
34 }
35 
36 ParagraphComment *Sema::actOnParagraphComment(
37                               ArrayRef<InlineContentComment *> Content) {
38   return new (Allocator) ParagraphComment(Content);
39 }
40 
41 BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
42                                                   SourceLocation LocEnd,
43                                                   StringRef Name) {
44   return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
45 }
46 
47 void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
48                                  ArrayRef<BlockCommandComment::Argument> Args) {
49   Command->setArgs(Args);
50 }
51 
52 void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
53                                    ParagraphComment *Paragraph) {
54   Command->setParagraph(Paragraph);
55   checkBlockCommandEmptyParagraph(Command);
56   checkBlockCommandDuplicate(Command);
57   checkReturnsCommand(Command);
58 }
59 
60 ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
61                                                   SourceLocation LocEnd,
62                                                   StringRef Name) {
63   ParamCommandComment *Command =
64       new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
65 
66   if (!isFunctionDecl())
67     Diag(Command->getLocation(),
68          diag::warn_doc_param_not_attached_to_a_function_decl)
69       << Command->getCommandNameRange();
70 
71   return Command;
72 }
73 
74 void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
75                                          SourceLocation ArgLocBegin,
76                                          SourceLocation ArgLocEnd,
77                                          StringRef Arg) {
78   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());
99 
100     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;
110     } else {
111       Direction = ParamCommandComment::In;
112       RemovingWhitespaceHelped = false;
113     }
114 
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;
125   }
126   Command->setDirection(Direction, /* Explicit = */ true);
127 }
128 
129 void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
130                                          SourceLocation ArgLocBegin,
131                                          SourceLocation ArgLocEnd,
132                                          StringRef Arg) {
133   // Parser will not feed us more arguments than needed.
134   assert(Command->getNumArgs() == 0);
135 
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));
145 
146   if (!isFunctionDecl()) {
147     // We already warned that this \\param is not attached to a function decl.
148     return;
149   }
150 
151   ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
152 
153   // Check that referenced parameter name is in the function decl.
154   const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
155   if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
156     Command->setParamIndex(ResolvedParamIndex);
157     if (ParamVarDocs[ResolvedParamIndex]) {
158       SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
159       Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
160         << Arg << ArgRange;
161       ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
162       Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
163         << PrevCommand->getParamNameRange();
164     }
165     ParamVarDocs[ResolvedParamIndex] = Command;
166     return;
167   }
168 
169   SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
170   Diag(ArgLocBegin, diag::warn_doc_param_not_found)
171     << Arg << ArgRange;
172 
173   // No parameters -- can't suggest a correction.
174   if (ParamVars.size() == 0)
175     return;
176 
177   unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
178   if (ParamVars.size() == 1) {
179     // If function has only one parameter then only that parameter
180     // can be documented.
181     CorrectedParamIndex = 0;
182   } else {
183     // Do typo correction.
184     CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
185   }
186   if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
187     const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
188     if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
189       Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
190         << CorrectedII->getName()
191         << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
192   }
193 
194   return;
195 }
196 
197 void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
198                                    ParagraphComment *Paragraph) {
199   Command->setParagraph(Paragraph);
200   checkBlockCommandEmptyParagraph(Command);
201 }
202 
203 TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
204                                                     SourceLocation LocEnd,
205                                                     StringRef Name) {
206   TParamCommandComment *Command =
207       new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
208 
209   if (!isTemplateOrSpecialization())
210     Diag(Command->getLocation(),
211          diag::warn_doc_tparam_not_attached_to_a_template_decl)
212       << Command->getCommandNameRange();
213 
214   return Command;
215 }
216 
217 void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
218                                           SourceLocation ArgLocBegin,
219                                           SourceLocation ArgLocEnd,
220                                           StringRef Arg) {
221   // Parser will not feed us more arguments than needed.
222   assert(Command->getNumArgs() == 0);
223 
224   typedef BlockCommandComment::Argument Argument;
225   Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
226                                                      ArgLocEnd),
227                                          Arg);
228   Command->setArgs(llvm::makeArrayRef(A, 1));
229 
230   if (!isTemplateOrSpecialization()) {
231     // We already warned that this \\tparam is not attached to a template decl.
232     return;
233   }
234 
235   const TemplateParameterList *TemplateParameters =
236       ThisDeclInfo->TemplateParameters;
237   SmallVector<unsigned, 2> Position;
238   if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
239     Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
240     llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
241         TemplateParameterDocs.find(Arg);
242     if (PrevCommandIt != TemplateParameterDocs.end()) {
243       SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
244       Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
245         << Arg << ArgRange;
246       TParamCommandComment *PrevCommand = PrevCommandIt->second;
247       Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
248         << PrevCommand->getParamNameRange();
249     }
250     TemplateParameterDocs[Arg] = Command;
251     return;
252   }
253 
254   SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
255   Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
256     << Arg << ArgRange;
257 
258   if (!TemplateParameters || TemplateParameters->size() == 0)
259     return;
260 
261   StringRef CorrectedName;
262   if (TemplateParameters->size() == 1) {
263     const NamedDecl *Param = TemplateParameters->getParam(0);
264     const IdentifierInfo *II = Param->getIdentifier();
265     if (II)
266       CorrectedName = II->getName();
267   } else {
268     CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
269   }
270 
271   if (!CorrectedName.empty()) {
272     Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
273       << CorrectedName
274       << FixItHint::CreateReplacement(ArgRange, CorrectedName);
275   }
276 
277   return;
278 }
279 
280 void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
281                                     ParagraphComment *Paragraph) {
282   Command->setParagraph(Paragraph);
283   checkBlockCommandEmptyParagraph(Command);
284 }
285 
286 InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
287                                                SourceLocation CommandLocEnd,
288                                                StringRef CommandName) {
289   ArrayRef<InlineCommandComment::Argument> Args;
290   return new (Allocator) InlineCommandComment(
291                                   CommandLocBegin,
292                                   CommandLocEnd,
293                                   CommandName,
294                                   getInlineCommandRenderKind(CommandName),
295                                   Args);
296 }
297 
298 InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
299                                                SourceLocation CommandLocEnd,
300                                                StringRef CommandName,
301                                                SourceLocation ArgLocBegin,
302                                                SourceLocation ArgLocEnd,
303                                                StringRef Arg) {
304   typedef InlineCommandComment::Argument Argument;
305   Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
306                                                      ArgLocEnd),
307                                          Arg);
308 
309   return new (Allocator) InlineCommandComment(
310                                   CommandLocBegin,
311                                   CommandLocEnd,
312                                   CommandName,
313                                   getInlineCommandRenderKind(CommandName),
314                                   llvm::makeArrayRef(A, 1));
315 }
316 
317 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
318                                                 SourceLocation LocEnd,
319                                                 StringRef Name) {
320   ArrayRef<InlineCommandComment::Argument> Args;
321   return new (Allocator) InlineCommandComment(
322                                   LocBegin, LocEnd, Name,
323                                   InlineCommandComment::RenderNormal,
324                                   Args);
325 }
326 
327 TextComment *Sema::actOnText(SourceLocation LocBegin,
328                              SourceLocation LocEnd,
329                              StringRef Text) {
330   return new (Allocator) TextComment(LocBegin, LocEnd, Text);
331 }
332 
333 VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
334                                                     StringRef Name) {
335   return new (Allocator) VerbatimBlockComment(
336                                   Loc,
337                                   Loc.getLocWithOffset(1 + Name.size()),
338                                   Name);
339 }
340 
341 VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
342                                                        StringRef Text) {
343   return new (Allocator) VerbatimBlockLineComment(Loc, Text);
344 }
345 
346 void Sema::actOnVerbatimBlockFinish(
347                             VerbatimBlockComment *Block,
348                             SourceLocation CloseNameLocBegin,
349                             StringRef CloseName,
350                             ArrayRef<VerbatimBlockLineComment *> Lines) {
351   Block->setCloseName(CloseName, CloseNameLocBegin);
352   Block->setLines(Lines);
353 }
354 
355 VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
356                                              StringRef Name,
357                                              SourceLocation TextBegin,
358                                              StringRef Text) {
359   return new (Allocator) VerbatimLineComment(
360                               LocBegin,
361                               TextBegin.getLocWithOffset(Text.size()),
362                               Name,
363                               TextBegin,
364                               Text);
365 }
366 
367 HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
368                                                   StringRef TagName) {
369   return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
370 }
371 
372 void Sema::actOnHTMLStartTagFinish(
373                               HTMLStartTagComment *Tag,
374                               ArrayRef<HTMLStartTagComment::Attribute> Attrs,
375                               SourceLocation GreaterLoc,
376                               bool IsSelfClosing) {
377   Tag->setAttrs(Attrs);
378   Tag->setGreaterLoc(GreaterLoc);
379   if (IsSelfClosing)
380     Tag->setSelfClosing();
381   else if (!isHTMLEndTagForbidden(Tag->getTagName()))
382     HTMLOpenTags.push_back(Tag);
383 }
384 
385 HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
386                                          SourceLocation LocEnd,
387                                          StringRef TagName) {
388   HTMLEndTagComment *HET =
389       new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
390   if (isHTMLEndTagForbidden(TagName)) {
391     Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
392       << TagName << HET->getSourceRange();
393     return HET;
394   }
395 
396   bool FoundOpen = false;
397   for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
398        I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
399        I != E; ++I) {
400     if ((*I)->getTagName() == TagName) {
401       FoundOpen = true;
402       break;
403     }
404   }
405   if (!FoundOpen) {
406     Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
407       << HET->getSourceRange();
408     return HET;
409   }
410 
411   while (!HTMLOpenTags.empty()) {
412     const HTMLStartTagComment *HST = HTMLOpenTags.back();
413     HTMLOpenTags.pop_back();
414     StringRef LastNotClosedTagName = HST->getTagName();
415     if (LastNotClosedTagName == TagName)
416       break;
417 
418     if (isHTMLEndTagOptional(LastNotClosedTagName))
419       continue;
420 
421     bool OpenLineInvalid;
422     const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
423                                                 HST->getLocation(),
424                                                 &OpenLineInvalid);
425     bool CloseLineInvalid;
426     const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
427                                                 HET->getLocation(),
428                                                 &CloseLineInvalid);
429 
430     if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
431       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
432         << HST->getTagName() << HET->getTagName()
433         << HST->getSourceRange() << HET->getSourceRange();
434     else {
435       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
436         << HST->getTagName() << HET->getTagName()
437         << HST->getSourceRange();
438       Diag(HET->getLocation(), diag::note_doc_html_end_tag)
439         << HET->getSourceRange();
440     }
441   }
442 
443   return HET;
444 }
445 
446 FullComment *Sema::actOnFullComment(
447                               ArrayRef<BlockContentComment *> Blocks) {
448   return new (Allocator) FullComment(Blocks, ThisDeclInfo);
449 }
450 
451 void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
452   ParagraphComment *Paragraph = Command->getParagraph();
453   if (Paragraph->isWhitespace()) {
454     SourceLocation DiagLoc;
455     if (Command->getNumArgs() > 0)
456       DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
457     if (!DiagLoc.isValid())
458       DiagLoc = Command->getCommandNameRange().getEnd();
459     Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
460       << Command->getCommandName()
461       << Command->getSourceRange();
462   }
463 }
464 
465 void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
466   if (!Traits.isReturnsCommand(Command->getCommandName()))
467     return;
468   if (isFunctionDecl()) {
469     if (ThisDeclInfo->ResultType->isVoidType()) {
470       unsigned DiagKind;
471       switch (ThisDeclInfo->ThisDecl->getKind()) {
472       default:
473         if (ThisDeclInfo->IsObjCMethod)
474           DiagKind = 3;
475         else
476           DiagKind = 0;
477         break;
478       case Decl::CXXConstructor:
479         DiagKind = 1;
480         break;
481       case Decl::CXXDestructor:
482         DiagKind = 2;
483         break;
484       }
485       Diag(Command->getLocation(),
486            diag::warn_doc_returns_attached_to_a_void_function)
487         << Command->getCommandName()
488         << DiagKind
489         << Command->getSourceRange();
490     }
491     return;
492   }
493   Diag(Command->getLocation(),
494        diag::warn_doc_returns_not_attached_to_a_function_decl)
495     << Command->getCommandName()
496     << Command->getSourceRange();
497 }
498 
499 void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
500   StringRef Name = Command->getCommandName();
501   const BlockCommandComment *PrevCommand = NULL;
502   if (Traits.isBriefCommand(Name)) {
503     if (!BriefCommand) {
504       BriefCommand = Command;
505       return;
506     }
507     PrevCommand = BriefCommand;
508   } else if (Traits.isReturnsCommand(Name)) {
509     if (!ReturnsCommand) {
510       ReturnsCommand = Command;
511       return;
512     }
513     PrevCommand = ReturnsCommand;
514   } else {
515     // We don't want to check this command for duplicates.
516     return;
517   }
518   Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
519       << Name
520       << Command->getSourceRange();
521   if (Name == PrevCommand->getCommandName())
522     Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
523         << PrevCommand->getCommandName()
524         << Command->getSourceRange();
525   else
526     Diag(PrevCommand->getLocation(),
527          diag::note_doc_block_command_previous_alias)
528         << PrevCommand->getCommandName()
529         << Name;
530 }
531 
532 bool Sema::isFunctionDecl() {
533   if (!ThisDeclInfo)
534     return false;
535   if (!ThisDeclInfo->IsFilled)
536     inspectThisDecl();
537   return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
538 }
539 
540 bool Sema::isTemplateOrSpecialization() {
541   if (!ThisDeclInfo)
542     return false;
543   if (!ThisDeclInfo->IsFilled)
544     inspectThisDecl();
545   return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
546 }
547 
548 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
549   if (!ThisDeclInfo->IsFilled)
550     inspectThisDecl();
551   return ThisDeclInfo->ParamVars;
552 }
553 
554 void Sema::inspectThisDecl() {
555   ThisDeclInfo->fill();
556   ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
557 }
558 
559 unsigned Sema::resolveParmVarReference(StringRef Name,
560                                        ArrayRef<const ParmVarDecl *> ParamVars) {
561   for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
562     const IdentifierInfo *II = ParamVars[i]->getIdentifier();
563     if (II && II->getName() == Name)
564       return i;
565   }
566   return ParamCommandComment::InvalidParamIndex;
567 }
568 
569 namespace {
570 class SimpleTypoCorrector {
571   StringRef Typo;
572   const unsigned MaxEditDistance;
573 
574   const NamedDecl *BestDecl;
575   unsigned BestEditDistance;
576   unsigned BestIndex;
577   unsigned NextIndex;
578 
579 public:
580   SimpleTypoCorrector(StringRef Typo) :
581       Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
582       BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
583       BestIndex(0), NextIndex(0)
584   { }
585 
586   void addDecl(const NamedDecl *ND);
587 
588   const NamedDecl *getBestDecl() const {
589     if (BestEditDistance > MaxEditDistance)
590       return NULL;
591 
592     return BestDecl;
593   }
594 
595   unsigned getBestDeclIndex() const {
596     assert(getBestDecl());
597     return BestIndex;
598   }
599 };
600 
601 void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
602   unsigned CurrIndex = NextIndex++;
603 
604   const IdentifierInfo *II = ND->getIdentifier();
605   if (!II)
606     return;
607 
608   StringRef Name = II->getName();
609   unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
610   if (MinPossibleEditDistance > 0 &&
611       Typo.size() / MinPossibleEditDistance < 3)
612     return;
613 
614   unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
615   if (EditDistance < BestEditDistance) {
616     BestEditDistance = EditDistance;
617     BestDecl = ND;
618     BestIndex = CurrIndex;
619   }
620 }
621 } // unnamed namespace
622 
623 unsigned Sema::correctTypoInParmVarReference(
624                                     StringRef Typo,
625                                     ArrayRef<const ParmVarDecl *> ParamVars) {
626   SimpleTypoCorrector Corrector(Typo);
627   for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
628     Corrector.addDecl(ParamVars[i]);
629   if (Corrector.getBestDecl())
630     return Corrector.getBestDeclIndex();
631   else
632     return ParamCommandComment::InvalidParamIndex;;
633 }
634 
635 namespace {
636 bool ResolveTParamReferenceHelper(
637                             StringRef Name,
638                             const TemplateParameterList *TemplateParameters,
639                             SmallVectorImpl<unsigned> *Position) {
640   for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
641     const NamedDecl *Param = TemplateParameters->getParam(i);
642     const IdentifierInfo *II = Param->getIdentifier();
643     if (II && II->getName() == Name) {
644       Position->push_back(i);
645       return true;
646     }
647 
648     if (const TemplateTemplateParmDecl *TTP =
649             dyn_cast<TemplateTemplateParmDecl>(Param)) {
650       Position->push_back(i);
651       if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
652                                        Position))
653         return true;
654       Position->pop_back();
655     }
656   }
657   return false;
658 }
659 } // unnamed namespace
660 
661 bool Sema::resolveTParamReference(
662                             StringRef Name,
663                             const TemplateParameterList *TemplateParameters,
664                             SmallVectorImpl<unsigned> *Position) {
665   Position->clear();
666   if (!TemplateParameters)
667     return false;
668 
669   return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
670 }
671 
672 namespace {
673 void CorrectTypoInTParamReferenceHelper(
674                             const TemplateParameterList *TemplateParameters,
675                             SimpleTypoCorrector &Corrector) {
676   for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
677     const NamedDecl *Param = TemplateParameters->getParam(i);
678     Corrector.addDecl(Param);
679 
680     if (const TemplateTemplateParmDecl *TTP =
681             dyn_cast<TemplateTemplateParmDecl>(Param))
682       CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
683                                          Corrector);
684   }
685 }
686 } // unnamed namespace
687 
688 StringRef Sema::correctTypoInTParamReference(
689                             StringRef Typo,
690                             const TemplateParameterList *TemplateParameters) {
691   SimpleTypoCorrector Corrector(Typo);
692   CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
693   if (const NamedDecl *ND = Corrector.getBestDecl()) {
694     const IdentifierInfo *II = ND->getIdentifier();
695     assert(II && "SimpleTypoCorrector should not return this decl");
696     return II->getName();
697   }
698   return StringRef();
699 }
700 
701 InlineCommandComment::RenderKind
702 Sema::getInlineCommandRenderKind(StringRef Name) const {
703   assert(Traits.isInlineCommand(Name));
704 
705   return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
706       .Case("b", InlineCommandComment::RenderBold)
707       .Cases("c", "p", InlineCommandComment::RenderMonospaced)
708       .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
709       .Default(InlineCommandComment::RenderNormal);
710 }
711 
712 bool Sema::isHTMLEndTagOptional(StringRef Name) {
713   return llvm::StringSwitch<bool>(Name)
714       .Case("p", true)
715       .Case("li", true)
716       .Case("dt", true)
717       .Case("dd", true)
718       .Case("tr", true)
719       .Case("th", true)
720       .Case("td", true)
721       .Case("thead", true)
722       .Case("tfoot", true)
723       .Case("tbody", true)
724       .Case("colgroup", true)
725       .Default(false);
726 }
727 
728 bool Sema::isHTMLEndTagForbidden(StringRef Name) {
729   return llvm::StringSwitch<bool>(Name)
730       .Case("br", true)
731       .Case("hr", true)
732       .Case("img", true)
733       .Case("col", true)
734       .Default(false);
735 }
736 
737 } // end namespace comments
738 } // end namespace clang
739 
740