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