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