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 
21 namespace clang {
22 namespace comments {
23 
24 namespace {
25 #include "clang/AST/CommentHTMLTagsProperties.inc"
26 } // unnamed namespace
27 
28 Sema::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 
35 void 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 
44 ParagraphComment *Sema::actOnParagraphComment(
45                               ArrayRef<InlineContentComment *> Content) {
46   return new (Allocator) ParagraphComment(Content);
47 }
48 
49 BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
50                                                   SourceLocation LocEnd,
51                                                   unsigned CommandID) {
52   return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID);
53 }
54 
55 void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
56                                  ArrayRef<BlockCommandComment::Argument> Args) {
57   Command->setArgs(Args);
58 }
59 
60 void 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 
69 ParamCommandComment *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 
83 void 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 
138 void 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 
156 void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
157                                    ParagraphComment *Paragraph) {
158   Command->setParagraph(Paragraph);
159   checkBlockCommandEmptyParagraph(Command);
160 }
161 
162 TParamCommandComment *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 
176 void 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 
239 void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
240                                     ParagraphComment *Paragraph) {
241   Command->setParagraph(Paragraph);
242   checkBlockCommandEmptyParagraph(Command);
243 }
244 
245 InlineCommandComment *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 
258 InlineCommandComment *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 
278 InlineContentComment *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 
285 InlineContentComment *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 
295 TextComment *Sema::actOnText(SourceLocation LocBegin,
296                              SourceLocation LocEnd,
297                              StringRef Text) {
298   return new (Allocator) TextComment(LocBegin, LocEnd, Text);
299 }
300 
301 VerbatimBlockComment *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 
310 VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
311                                                        StringRef Text) {
312   return new (Allocator) VerbatimBlockLineComment(Loc, Text);
313 }
314 
315 void 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 
324 VerbatimLineComment *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 
336 HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
337                                                   StringRef TagName) {
338   return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
339 }
340 
341 void 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 
354 HTMLEndTagComment *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 
415 FullComment *Sema::actOnFullComment(
416                               ArrayRef<BlockContentComment *> Blocks) {
417   FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
418   resolveParamCommandIndexes(FC);
419   return FC;
420 }
421 
422 void 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 
439 void 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 
473 void 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 
508 void 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 
556 void 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   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   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   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 
641 bool Sema::isFunctionDecl() {
642   if (!ThisDeclInfo)
643     return false;
644   if (!ThisDeclInfo->IsFilled)
645     inspectThisDecl();
646   return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
647 }
648 
649 bool Sema::isTemplateOrSpecialization() {
650   if (!ThisDeclInfo)
651     return false;
652   if (!ThisDeclInfo->IsFilled)
653     inspectThisDecl();
654   return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
655 }
656 
657 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
658   if (!ThisDeclInfo->IsFilled)
659     inspectThisDecl();
660   return ThisDeclInfo->ParamVars;
661 }
662 
663 void Sema::inspectThisDecl() {
664   ThisDeclInfo->fill();
665 }
666 
667 unsigned 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 
677 namespace {
678 class SimpleTypoCorrector {
679   StringRef Typo;
680   const unsigned MaxEditDistance;
681 
682   const NamedDecl *BestDecl;
683   unsigned BestEditDistance;
684   unsigned BestIndex;
685   unsigned NextIndex;
686 
687 public:
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 
709 void 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 
731 unsigned 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 
743 namespace {
744 bool 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 
769 bool 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 
780 namespace {
781 void 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 
796 StringRef 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 
809 InlineCommandComment::RenderKind
810 Sema::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