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