]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/AST/CommentSema.cpp
DTrace: option for time-ordered output
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / AST / CommentSema.cpp
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
20 namespace clang {
21 namespace comments {
22
23 namespace {
24 #include "clang/AST/CommentHTMLTagsProperties.inc"
25 } // unnamed namespace
26
27 Sema::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
34 void 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
43 ParagraphComment *Sema::actOnParagraphComment(
44                               ArrayRef<InlineContentComment *> Content) {
45   return new (Allocator) ParagraphComment(Content);
46 }
47
48 BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
49                                                   SourceLocation LocEnd,
50                                                   unsigned CommandID) {
51   return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID);
52 }
53
54 void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
55                                  ArrayRef<BlockCommandComment::Argument> Args) {
56   Command->setArgs(Args);
57 }
58
59 void 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
68 ParamCommandComment *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
82 void 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
137 void 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
155 void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
156                                    ParagraphComment *Paragraph) {
157   Command->setParagraph(Paragraph);
158   checkBlockCommandEmptyParagraph(Command);
159 }
160
161 TParamCommandComment *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
175 void 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
238 void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
239                                     ParagraphComment *Paragraph) {
240   Command->setParagraph(Paragraph);
241   checkBlockCommandEmptyParagraph(Command);
242 }
243
244 InlineCommandComment *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
257 InlineCommandComment *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
277 InlineContentComment *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
284 InlineContentComment *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
294 TextComment *Sema::actOnText(SourceLocation LocBegin,
295                              SourceLocation LocEnd,
296                              StringRef Text) {
297   return new (Allocator) TextComment(LocBegin, LocEnd, Text);
298 }
299
300 VerbatimBlockComment *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
309 VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
310                                                        StringRef Text) {
311   return new (Allocator) VerbatimBlockLineComment(Loc, Text);
312 }
313
314 void 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
323 VerbatimLineComment *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
335 HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
336                                                   StringRef TagName) {
337   return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
338 }
339
340 void 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
353 HTMLEndTagComment *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
414 FullComment *Sema::actOnFullComment(
415                               ArrayRef<BlockContentComment *> Blocks) {
416   FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
417   resolveParamCommandIndexes(FC);
418   return FC;
419 }
420
421 void 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
438 void 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
472 void 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
507 void 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
555 void 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
640 bool Sema::isFunctionDecl() {
641   if (!ThisDeclInfo)
642     return false;
643   if (!ThisDeclInfo->IsFilled)
644     inspectThisDecl();
645   return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
646 }
647
648 bool Sema::isTemplateOrSpecialization() {
649   if (!ThisDeclInfo)
650     return false;
651   if (!ThisDeclInfo->IsFilled)
652     inspectThisDecl();
653   return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
654 }
655
656 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
657   if (!ThisDeclInfo->IsFilled)
658     inspectThisDecl();
659   return ThisDeclInfo->ParamVars;
660 }
661
662 void Sema::inspectThisDecl() {
663   ThisDeclInfo->fill();
664 }
665
666 unsigned 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
676 namespace {
677 class SimpleTypoCorrector {
678   StringRef Typo;
679   const unsigned MaxEditDistance;
680
681   const NamedDecl *BestDecl;
682   unsigned BestEditDistance;
683   unsigned BestIndex;
684   unsigned NextIndex;
685
686 public:
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
708 void 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
730 unsigned 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
742 namespace {
743 bool 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
768 bool 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
779 namespace {
780 void 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
795 StringRef 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
808 InlineCommandComment::RenderKind
809 Sema::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