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