• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Sema(llvm::BumpPtrAllocator & Allocator,const SourceManager & SourceMgr,DiagnosticsEngine & Diags,CommandTraits & Traits,const Preprocessor * PP)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     HeaderfileCommand(NULL) {
34 }
35 
setDecl(const Decl * D)36 void Sema::setDecl(const Decl *D) {
37   if (!D)
38     return;
39 
40   ThisDeclInfo = new (Allocator) DeclInfo;
41   ThisDeclInfo->CommentDecl = D;
42   ThisDeclInfo->IsFilled = false;
43 }
44 
actOnParagraphComment(ArrayRef<InlineContentComment * > Content)45 ParagraphComment *Sema::actOnParagraphComment(
46                               ArrayRef<InlineContentComment *> Content) {
47   return new (Allocator) ParagraphComment(Content);
48 }
49 
actOnBlockCommandStart(SourceLocation LocBegin,SourceLocation LocEnd,unsigned CommandID,CommandMarkerKind CommandMarker)50 BlockCommandComment *Sema::actOnBlockCommandStart(
51                                       SourceLocation LocBegin,
52                                       SourceLocation LocEnd,
53                                       unsigned CommandID,
54                                       CommandMarkerKind CommandMarker) {
55   BlockCommandComment *BC = new (Allocator) BlockCommandComment(LocBegin, LocEnd,
56                                                                 CommandID,
57                                                                 CommandMarker);
58   checkContainerDecl(BC);
59   return BC;
60 }
61 
actOnBlockCommandArgs(BlockCommandComment * Command,ArrayRef<BlockCommandComment::Argument> Args)62 void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
63                                  ArrayRef<BlockCommandComment::Argument> Args) {
64   Command->setArgs(Args);
65 }
66 
actOnBlockCommandFinish(BlockCommandComment * Command,ParagraphComment * Paragraph)67 void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
68                                    ParagraphComment *Paragraph) {
69   Command->setParagraph(Paragraph);
70   checkBlockCommandEmptyParagraph(Command);
71   checkBlockCommandDuplicate(Command);
72   checkReturnsCommand(Command);
73   checkDeprecatedCommand(Command);
74 }
75 
actOnParamCommandStart(SourceLocation LocBegin,SourceLocation LocEnd,unsigned CommandID,CommandMarkerKind CommandMarker)76 ParamCommandComment *Sema::actOnParamCommandStart(
77                                       SourceLocation LocBegin,
78                                       SourceLocation LocEnd,
79                                       unsigned CommandID,
80                                       CommandMarkerKind CommandMarker) {
81   ParamCommandComment *Command =
82       new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,
83                                           CommandMarker);
84 
85   if (!isFunctionDecl())
86     Diag(Command->getLocation(),
87          diag::warn_doc_param_not_attached_to_a_function_decl)
88       << CommandMarker
89       << Command->getCommandNameRange(Traits);
90 
91   return Command;
92 }
93 
checkFunctionDeclVerbatimLine(const BlockCommandComment * Comment)94 void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
95   const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
96   if (!Info->IsFunctionDeclarationCommand)
97     return;
98 
99   unsigned DiagSelect;
100   switch (Comment->getCommandID()) {
101     case CommandTraits::KCI_function:
102       DiagSelect = !isAnyFunctionDecl() ? 1 : 0;
103       break;
104     case CommandTraits::KCI_method:
105       DiagSelect = !isObjCMethodDecl() ? 2 : 0;
106       break;
107     case CommandTraits::KCI_callback:
108       DiagSelect = !isFunctionPointerVarDecl() ? 3 : 0;
109       break;
110     default:
111       DiagSelect = 0;
112       break;
113   }
114   if (DiagSelect)
115     Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)
116     << Comment->getCommandMarker()
117     << (DiagSelect-1) << (DiagSelect-1)
118     << Comment->getSourceRange();
119 }
120 
checkContainerDeclVerbatimLine(const BlockCommandComment * Comment)121 void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {
122   const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
123   if (!Info->IsRecordLikeDeclarationCommand)
124     return;
125   unsigned DiagSelect;
126   switch (Comment->getCommandID()) {
127     case CommandTraits::KCI_class:
128       DiagSelect = !isClassOrStructDecl() ? 1 : 0;
129       break;
130     case CommandTraits::KCI_interface:
131       DiagSelect = !isObjCInterfaceDecl() ? 2 : 0;
132       break;
133     case CommandTraits::KCI_protocol:
134       DiagSelect = !isObjCProtocolDecl() ? 3 : 0;
135       break;
136     case CommandTraits::KCI_struct:
137       DiagSelect = !isClassOrStructDecl() ? 4 : 0;
138       break;
139     case CommandTraits::KCI_union:
140       DiagSelect = !isUnionDecl() ? 5 : 0;
141       break;
142     default:
143       DiagSelect = 0;
144       break;
145   }
146   if (DiagSelect)
147     Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
148     << Comment->getCommandMarker()
149     << (DiagSelect-1) << (DiagSelect-1)
150     << Comment->getSourceRange();
151 }
152 
checkContainerDecl(const BlockCommandComment * Comment)153 void Sema::checkContainerDecl(const BlockCommandComment *Comment) {
154   const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
155   if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
156     return;
157   unsigned DiagSelect;
158   switch (Comment->getCommandID()) {
159     case CommandTraits::KCI_classdesign:
160       DiagSelect = 1;
161       break;
162     case CommandTraits::KCI_coclass:
163       DiagSelect = 2;
164       break;
165     case CommandTraits::KCI_dependency:
166       DiagSelect = 3;
167       break;
168     case CommandTraits::KCI_helper:
169       DiagSelect = 4;
170       break;
171     case CommandTraits::KCI_helperclass:
172       DiagSelect = 5;
173       break;
174     case CommandTraits::KCI_helps:
175       DiagSelect = 6;
176       break;
177     case CommandTraits::KCI_instancesize:
178       DiagSelect = 7;
179       break;
180     case CommandTraits::KCI_ownership:
181       DiagSelect = 8;
182       break;
183     case CommandTraits::KCI_performance:
184       DiagSelect = 9;
185       break;
186     case CommandTraits::KCI_security:
187       DiagSelect = 10;
188       break;
189     case CommandTraits::KCI_superclass:
190       DiagSelect = 11;
191       break;
192     default:
193       DiagSelect = 0;
194       break;
195   }
196   if (DiagSelect)
197     Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
198     << Comment->getCommandMarker()
199     << (DiagSelect-1)
200     << Comment->getSourceRange();
201 }
202 
actOnParamCommandDirectionArg(ParamCommandComment * Command,SourceLocation ArgLocBegin,SourceLocation ArgLocEnd,StringRef Arg)203 void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
204                                          SourceLocation ArgLocBegin,
205                                          SourceLocation ArgLocEnd,
206                                          StringRef Arg) {
207   ParamCommandComment::PassDirection Direction;
208   std::string ArgLower = Arg.lower();
209   // TODO: optimize: lower Name first (need an API in SmallString for that),
210   // after that StringSwitch.
211   if (ArgLower == "[in]")
212     Direction = ParamCommandComment::In;
213   else if (ArgLower == "[out]")
214     Direction = ParamCommandComment::Out;
215   else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
216     Direction = ParamCommandComment::InOut;
217   else {
218     // Remove spaces.
219     std::string::iterator O = ArgLower.begin();
220     for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
221          I != E; ++I) {
222       const char C = *I;
223       if (C != ' ' && C != '\n' && C != '\r' &&
224           C != '\t' && C != '\v' && C != '\f')
225         *O++ = C;
226     }
227     ArgLower.resize(O - ArgLower.begin());
228 
229     bool RemovingWhitespaceHelped = false;
230     if (ArgLower == "[in]") {
231       Direction = ParamCommandComment::In;
232       RemovingWhitespaceHelped = true;
233     } else if (ArgLower == "[out]") {
234       Direction = ParamCommandComment::Out;
235       RemovingWhitespaceHelped = true;
236     } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
237       Direction = ParamCommandComment::InOut;
238       RemovingWhitespaceHelped = true;
239     } else {
240       Direction = ParamCommandComment::In;
241       RemovingWhitespaceHelped = false;
242     }
243 
244     SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
245     if (RemovingWhitespaceHelped)
246       Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
247         << ArgRange
248         << FixItHint::CreateReplacement(
249                           ArgRange,
250                           ParamCommandComment::getDirectionAsString(Direction));
251     else
252       Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
253         << ArgRange;
254   }
255   Command->setDirection(Direction, /* Explicit = */ true);
256 }
257 
actOnParamCommandParamNameArg(ParamCommandComment * Command,SourceLocation ArgLocBegin,SourceLocation ArgLocEnd,StringRef Arg)258 void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
259                                          SourceLocation ArgLocBegin,
260                                          SourceLocation ArgLocEnd,
261                                          StringRef Arg) {
262   // Parser will not feed us more arguments than needed.
263   assert(Command->getNumArgs() == 0);
264 
265   if (!Command->isDirectionExplicit()) {
266     // User didn't provide a direction argument.
267     Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
268   }
269   typedef BlockCommandComment::Argument Argument;
270   Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
271                                                      ArgLocEnd),
272                                          Arg);
273   Command->setArgs(llvm::makeArrayRef(A, 1));
274 }
275 
actOnParamCommandFinish(ParamCommandComment * Command,ParagraphComment * Paragraph)276 void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
277                                    ParagraphComment *Paragraph) {
278   Command->setParagraph(Paragraph);
279   checkBlockCommandEmptyParagraph(Command);
280 }
281 
actOnTParamCommandStart(SourceLocation LocBegin,SourceLocation LocEnd,unsigned CommandID,CommandMarkerKind CommandMarker)282 TParamCommandComment *Sema::actOnTParamCommandStart(
283                                       SourceLocation LocBegin,
284                                       SourceLocation LocEnd,
285                                       unsigned CommandID,
286                                       CommandMarkerKind CommandMarker) {
287   TParamCommandComment *Command =
288       new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID,
289                                            CommandMarker);
290 
291   if (!isTemplateOrSpecialization())
292     Diag(Command->getLocation(),
293          diag::warn_doc_tparam_not_attached_to_a_template_decl)
294       << CommandMarker
295       << Command->getCommandNameRange(Traits);
296 
297   return Command;
298 }
299 
actOnTParamCommandParamNameArg(TParamCommandComment * Command,SourceLocation ArgLocBegin,SourceLocation ArgLocEnd,StringRef Arg)300 void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
301                                           SourceLocation ArgLocBegin,
302                                           SourceLocation ArgLocEnd,
303                                           StringRef Arg) {
304   // Parser will not feed us more arguments than needed.
305   assert(Command->getNumArgs() == 0);
306 
307   typedef BlockCommandComment::Argument Argument;
308   Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
309                                                      ArgLocEnd),
310                                          Arg);
311   Command->setArgs(llvm::makeArrayRef(A, 1));
312 
313   if (!isTemplateOrSpecialization()) {
314     // We already warned that this \\tparam is not attached to a template decl.
315     return;
316   }
317 
318   const TemplateParameterList *TemplateParameters =
319       ThisDeclInfo->TemplateParameters;
320   SmallVector<unsigned, 2> Position;
321   if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
322     Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
323     llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
324         TemplateParameterDocs.find(Arg);
325     if (PrevCommandIt != TemplateParameterDocs.end()) {
326       SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
327       Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
328         << Arg << ArgRange;
329       TParamCommandComment *PrevCommand = PrevCommandIt->second;
330       Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
331         << PrevCommand->getParamNameRange();
332     }
333     TemplateParameterDocs[Arg] = Command;
334     return;
335   }
336 
337   SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
338   Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
339     << Arg << ArgRange;
340 
341   if (!TemplateParameters || TemplateParameters->size() == 0)
342     return;
343 
344   StringRef CorrectedName;
345   if (TemplateParameters->size() == 1) {
346     const NamedDecl *Param = TemplateParameters->getParam(0);
347     const IdentifierInfo *II = Param->getIdentifier();
348     if (II)
349       CorrectedName = II->getName();
350   } else {
351     CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
352   }
353 
354   if (!CorrectedName.empty()) {
355     Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
356       << CorrectedName
357       << FixItHint::CreateReplacement(ArgRange, CorrectedName);
358   }
359 
360   return;
361 }
362 
actOnTParamCommandFinish(TParamCommandComment * Command,ParagraphComment * Paragraph)363 void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
364                                     ParagraphComment *Paragraph) {
365   Command->setParagraph(Paragraph);
366   checkBlockCommandEmptyParagraph(Command);
367 }
368 
actOnInlineCommand(SourceLocation CommandLocBegin,SourceLocation CommandLocEnd,unsigned CommandID)369 InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
370                                                SourceLocation CommandLocEnd,
371                                                unsigned CommandID) {
372   ArrayRef<InlineCommandComment::Argument> Args;
373   StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
374   return new (Allocator) InlineCommandComment(
375                                   CommandLocBegin,
376                                   CommandLocEnd,
377                                   CommandID,
378                                   getInlineCommandRenderKind(CommandName),
379                                   Args);
380 }
381 
actOnInlineCommand(SourceLocation CommandLocBegin,SourceLocation CommandLocEnd,unsigned CommandID,SourceLocation ArgLocBegin,SourceLocation ArgLocEnd,StringRef Arg)382 InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
383                                                SourceLocation CommandLocEnd,
384                                                unsigned CommandID,
385                                                SourceLocation ArgLocBegin,
386                                                SourceLocation ArgLocEnd,
387                                                StringRef Arg) {
388   typedef InlineCommandComment::Argument Argument;
389   Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
390                                                      ArgLocEnd),
391                                          Arg);
392   StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
393 
394   return new (Allocator) InlineCommandComment(
395                                   CommandLocBegin,
396                                   CommandLocEnd,
397                                   CommandID,
398                                   getInlineCommandRenderKind(CommandName),
399                                   llvm::makeArrayRef(A, 1));
400 }
401 
actOnUnknownCommand(SourceLocation LocBegin,SourceLocation LocEnd,StringRef CommandName)402 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
403                                                 SourceLocation LocEnd,
404                                                 StringRef CommandName) {
405   unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
406   return actOnUnknownCommand(LocBegin, LocEnd, CommandID);
407 }
408 
actOnUnknownCommand(SourceLocation LocBegin,SourceLocation LocEnd,unsigned CommandID)409 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
410                                                 SourceLocation LocEnd,
411                                                 unsigned CommandID) {
412   ArrayRef<InlineCommandComment::Argument> Args;
413   return new (Allocator) InlineCommandComment(
414                                   LocBegin, LocEnd, CommandID,
415                                   InlineCommandComment::RenderNormal,
416                                   Args);
417 }
418 
actOnText(SourceLocation LocBegin,SourceLocation LocEnd,StringRef Text)419 TextComment *Sema::actOnText(SourceLocation LocBegin,
420                              SourceLocation LocEnd,
421                              StringRef Text) {
422   return new (Allocator) TextComment(LocBegin, LocEnd, Text);
423 }
424 
actOnVerbatimBlockStart(SourceLocation Loc,unsigned CommandID)425 VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
426                                                     unsigned CommandID) {
427   StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
428   return new (Allocator) VerbatimBlockComment(
429                                   Loc,
430                                   Loc.getLocWithOffset(1 + CommandName.size()),
431                                   CommandID);
432 }
433 
actOnVerbatimBlockLine(SourceLocation Loc,StringRef Text)434 VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
435                                                        StringRef Text) {
436   return new (Allocator) VerbatimBlockLineComment(Loc, Text);
437 }
438 
actOnVerbatimBlockFinish(VerbatimBlockComment * Block,SourceLocation CloseNameLocBegin,StringRef CloseName,ArrayRef<VerbatimBlockLineComment * > Lines)439 void Sema::actOnVerbatimBlockFinish(
440                             VerbatimBlockComment *Block,
441                             SourceLocation CloseNameLocBegin,
442                             StringRef CloseName,
443                             ArrayRef<VerbatimBlockLineComment *> Lines) {
444   Block->setCloseName(CloseName, CloseNameLocBegin);
445   Block->setLines(Lines);
446 }
447 
actOnVerbatimLine(SourceLocation LocBegin,unsigned CommandID,SourceLocation TextBegin,StringRef Text)448 VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
449                                              unsigned CommandID,
450                                              SourceLocation TextBegin,
451                                              StringRef Text) {
452   VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
453                               LocBegin,
454                               TextBegin.getLocWithOffset(Text.size()),
455                               CommandID,
456                               TextBegin,
457                               Text);
458   checkFunctionDeclVerbatimLine(VL);
459   checkContainerDeclVerbatimLine(VL);
460   return VL;
461 }
462 
actOnHTMLStartTagStart(SourceLocation LocBegin,StringRef TagName)463 HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
464                                                   StringRef TagName) {
465   return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
466 }
467 
actOnHTMLStartTagFinish(HTMLStartTagComment * Tag,ArrayRef<HTMLStartTagComment::Attribute> Attrs,SourceLocation GreaterLoc,bool IsSelfClosing)468 void Sema::actOnHTMLStartTagFinish(
469                               HTMLStartTagComment *Tag,
470                               ArrayRef<HTMLStartTagComment::Attribute> Attrs,
471                               SourceLocation GreaterLoc,
472                               bool IsSelfClosing) {
473   Tag->setAttrs(Attrs);
474   Tag->setGreaterLoc(GreaterLoc);
475   if (IsSelfClosing)
476     Tag->setSelfClosing();
477   else if (!isHTMLEndTagForbidden(Tag->getTagName()))
478     HTMLOpenTags.push_back(Tag);
479 }
480 
actOnHTMLEndTag(SourceLocation LocBegin,SourceLocation LocEnd,StringRef TagName)481 HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
482                                          SourceLocation LocEnd,
483                                          StringRef TagName) {
484   HTMLEndTagComment *HET =
485       new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
486   if (isHTMLEndTagForbidden(TagName)) {
487     Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
488       << TagName << HET->getSourceRange();
489     return HET;
490   }
491 
492   bool FoundOpen = false;
493   for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
494        I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
495        I != E; ++I) {
496     if ((*I)->getTagName() == TagName) {
497       FoundOpen = true;
498       break;
499     }
500   }
501   if (!FoundOpen) {
502     Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
503       << HET->getSourceRange();
504     return HET;
505   }
506 
507   while (!HTMLOpenTags.empty()) {
508     const HTMLStartTagComment *HST = HTMLOpenTags.back();
509     HTMLOpenTags.pop_back();
510     StringRef LastNotClosedTagName = HST->getTagName();
511     if (LastNotClosedTagName == TagName)
512       break;
513 
514     if (isHTMLEndTagOptional(LastNotClosedTagName))
515       continue;
516 
517     bool OpenLineInvalid;
518     const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
519                                                 HST->getLocation(),
520                                                 &OpenLineInvalid);
521     bool CloseLineInvalid;
522     const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
523                                                 HET->getLocation(),
524                                                 &CloseLineInvalid);
525 
526     if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
527       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
528         << HST->getTagName() << HET->getTagName()
529         << HST->getSourceRange() << HET->getSourceRange();
530     else {
531       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
532         << HST->getTagName() << HET->getTagName()
533         << HST->getSourceRange();
534       Diag(HET->getLocation(), diag::note_doc_html_end_tag)
535         << HET->getSourceRange();
536     }
537   }
538 
539   return HET;
540 }
541 
actOnFullComment(ArrayRef<BlockContentComment * > Blocks)542 FullComment *Sema::actOnFullComment(
543                               ArrayRef<BlockContentComment *> Blocks) {
544   FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
545   resolveParamCommandIndexes(FC);
546   return FC;
547 }
548 
checkBlockCommandEmptyParagraph(BlockCommandComment * Command)549 void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
550   if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)
551     return;
552 
553   ParagraphComment *Paragraph = Command->getParagraph();
554   if (Paragraph->isWhitespace()) {
555     SourceLocation DiagLoc;
556     if (Command->getNumArgs() > 0)
557       DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
558     if (!DiagLoc.isValid())
559       DiagLoc = Command->getCommandNameRange(Traits).getEnd();
560     Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
561       << Command->getCommandMarker()
562       << Command->getCommandName(Traits)
563       << Command->getSourceRange();
564   }
565 }
566 
checkReturnsCommand(const BlockCommandComment * Command)567 void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
568   if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
569     return;
570   if (isFunctionDecl()) {
571     if (ThisDeclInfo->ResultType->isVoidType()) {
572       unsigned DiagKind;
573       switch (ThisDeclInfo->CommentDecl->getKind()) {
574       default:
575         if (ThisDeclInfo->IsObjCMethod)
576           DiagKind = 3;
577         else
578           DiagKind = 0;
579         break;
580       case Decl::CXXConstructor:
581         DiagKind = 1;
582         break;
583       case Decl::CXXDestructor:
584         DiagKind = 2;
585         break;
586       }
587       Diag(Command->getLocation(),
588            diag::warn_doc_returns_attached_to_a_void_function)
589         << Command->getCommandMarker()
590         << Command->getCommandName(Traits)
591         << DiagKind
592         << Command->getSourceRange();
593     }
594     return;
595   }
596   else if (isObjCPropertyDecl())
597     return;
598 
599   Diag(Command->getLocation(),
600        diag::warn_doc_returns_not_attached_to_a_function_decl)
601     << Command->getCommandMarker()
602     << Command->getCommandName(Traits)
603     << Command->getSourceRange();
604 }
605 
checkBlockCommandDuplicate(const BlockCommandComment * Command)606 void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
607   const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
608   const BlockCommandComment *PrevCommand = NULL;
609   if (Info->IsBriefCommand) {
610     if (!BriefCommand) {
611       BriefCommand = Command;
612       return;
613     }
614     PrevCommand = BriefCommand;
615   } else if (Info->IsReturnsCommand) {
616     if (!ReturnsCommand) {
617       ReturnsCommand = Command;
618       return;
619     }
620     PrevCommand = ReturnsCommand;
621   } else if (Info->IsHeaderfileCommand) {
622     if (!HeaderfileCommand) {
623       HeaderfileCommand = Command;
624       return;
625     }
626     PrevCommand = HeaderfileCommand;
627   } else {
628     // We don't want to check this command for duplicates.
629     return;
630   }
631   StringRef CommandName = Command->getCommandName(Traits);
632   StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
633   Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
634       << Command->getCommandMarker()
635       << CommandName
636       << Command->getSourceRange();
637   if (CommandName == PrevCommandName)
638     Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
639         << PrevCommand->getCommandMarker()
640         << PrevCommandName
641         << PrevCommand->getSourceRange();
642   else
643     Diag(PrevCommand->getLocation(),
644          diag::note_doc_block_command_previous_alias)
645         << PrevCommand->getCommandMarker()
646         << PrevCommandName
647         << CommandName;
648 }
649 
checkDeprecatedCommand(const BlockCommandComment * Command)650 void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
651   if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
652     return;
653 
654   const Decl *D = ThisDeclInfo->CommentDecl;
655   if (!D)
656     return;
657 
658   if (D->hasAttr<DeprecatedAttr>() ||
659       D->hasAttr<AvailabilityAttr>() ||
660       D->hasAttr<UnavailableAttr>())
661     return;
662 
663   Diag(Command->getLocation(),
664        diag::warn_doc_deprecated_not_sync)
665     << Command->getSourceRange();
666 
667   // Try to emit a fixit with a deprecation attribute.
668   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
669     // Don't emit a Fix-It for non-member function definitions.  GCC does not
670     // accept attributes on them.
671     const DeclContext *Ctx = FD->getDeclContext();
672     if ((!Ctx || !Ctx->isRecord()) &&
673         FD->doesThisDeclarationHaveABody())
674       return;
675 
676     StringRef AttributeSpelling = "__attribute__((deprecated))";
677     if (PP) {
678       TokenValue Tokens[] = {
679         tok::kw___attribute, tok::l_paren, tok::l_paren,
680         PP->getIdentifierInfo("deprecated"),
681         tok::r_paren, tok::r_paren
682       };
683       StringRef MacroName = PP->getLastMacroWithSpelling(FD->getLocation(),
684                                                          Tokens);
685       if (!MacroName.empty())
686         AttributeSpelling = MacroName;
687     }
688 
689     SmallString<64> TextToInsert(" ");
690     TextToInsert += AttributeSpelling;
691     Diag(FD->getLocEnd(),
692          diag::note_add_deprecation_attr)
693       << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1),
694                                     TextToInsert);
695   }
696 }
697 
resolveParamCommandIndexes(const FullComment * FC)698 void Sema::resolveParamCommandIndexes(const FullComment *FC) {
699   if (!isFunctionDecl()) {
700     // We already warned that \\param commands are not attached to a function
701     // decl.
702     return;
703   }
704 
705   SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
706 
707   // Comment AST nodes that correspond to \c ParamVars for which we have
708   // found a \\param command or NULL if no documentation was found so far.
709   SmallVector<ParamCommandComment *, 8> ParamVarDocs;
710 
711   ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
712   ParamVarDocs.resize(ParamVars.size(), NULL);
713 
714   // First pass over all \\param commands: resolve all parameter names.
715   for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
716        I != E; ++I) {
717     ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
718     if (!PCC || !PCC->hasParamName())
719       continue;
720     StringRef ParamName = PCC->getParamNameAsWritten();
721 
722     // Check that referenced parameter name is in the function decl.
723     const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
724                                                                 ParamVars);
725     if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
726       UnresolvedParamCommands.push_back(PCC);
727       continue;
728     }
729     PCC->setParamIndex(ResolvedParamIndex);
730     if (ParamVarDocs[ResolvedParamIndex]) {
731       SourceRange ArgRange = PCC->getParamNameRange();
732       Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
733         << ParamName << ArgRange;
734       ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
735       Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
736         << PrevCommand->getParamNameRange();
737     }
738     ParamVarDocs[ResolvedParamIndex] = PCC;
739   }
740 
741   // Find parameter declarations that have no corresponding \\param.
742   SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
743   for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
744     if (!ParamVarDocs[i])
745       OrphanedParamDecls.push_back(ParamVars[i]);
746   }
747 
748   // Second pass over unresolved \\param commands: do typo correction.
749   // Suggest corrections from a set of parameter declarations that have no
750   // corresponding \\param.
751   for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
752     const ParamCommandComment *PCC = UnresolvedParamCommands[i];
753 
754     SourceRange ArgRange = PCC->getParamNameRange();
755     StringRef ParamName = PCC->getParamNameAsWritten();
756     Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
757       << ParamName << ArgRange;
758 
759     // All parameters documented -- can't suggest a correction.
760     if (OrphanedParamDecls.size() == 0)
761       continue;
762 
763     unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
764     if (OrphanedParamDecls.size() == 1) {
765       // If one parameter is not documented then that parameter is the only
766       // possible suggestion.
767       CorrectedParamIndex = 0;
768     } else {
769       // Do typo correction.
770       CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
771                                                           OrphanedParamDecls);
772     }
773     if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
774       const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
775       if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
776         Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
777           << CorrectedII->getName()
778           << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
779     }
780   }
781 }
782 
isFunctionDecl()783 bool Sema::isFunctionDecl() {
784   if (!ThisDeclInfo)
785     return false;
786   if (!ThisDeclInfo->IsFilled)
787     inspectThisDecl();
788   return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
789 }
790 
isAnyFunctionDecl()791 bool Sema::isAnyFunctionDecl() {
792   return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
793          isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);
794 }
795 
isObjCMethodDecl()796 bool Sema::isObjCMethodDecl() {
797   return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
798          isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);
799 }
800 
801 /// isFunctionPointerVarDecl - returns 'true' if declaration is a pointer to
802 /// function decl.
isFunctionPointerVarDecl()803 bool Sema::isFunctionPointerVarDecl() {
804   if (!ThisDeclInfo)
805     return false;
806   if (!ThisDeclInfo->IsFilled)
807     inspectThisDecl();
808   if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {
809     if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {
810       QualType QT = VD->getType();
811       return QT->isFunctionPointerType();
812     }
813   }
814   return false;
815 }
816 
isObjCPropertyDecl()817 bool Sema::isObjCPropertyDecl() {
818   if (!ThisDeclInfo)
819     return false;
820   if (!ThisDeclInfo->IsFilled)
821     inspectThisDecl();
822   return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;
823 }
824 
isTemplateOrSpecialization()825 bool Sema::isTemplateOrSpecialization() {
826   if (!ThisDeclInfo)
827     return false;
828   if (!ThisDeclInfo->IsFilled)
829     inspectThisDecl();
830   return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
831 }
832 
isRecordLikeDecl()833 bool Sema::isRecordLikeDecl() {
834   if (!ThisDeclInfo)
835     return false;
836   if (!ThisDeclInfo->IsFilled)
837     inspectThisDecl();
838   return isUnionDecl() || isClassOrStructDecl()
839          || isObjCInterfaceDecl() || isObjCProtocolDecl();
840 }
841 
isUnionDecl()842 bool Sema::isUnionDecl() {
843   if (!ThisDeclInfo)
844     return false;
845   if (!ThisDeclInfo->IsFilled)
846     inspectThisDecl();
847   if (const RecordDecl *RD =
848         dyn_cast_or_null<RecordDecl>(ThisDeclInfo->CurrentDecl))
849     return RD->isUnion();
850   return false;
851 }
852 
isClassOrStructDecl()853 bool Sema::isClassOrStructDecl() {
854   if (!ThisDeclInfo)
855     return false;
856   if (!ThisDeclInfo->IsFilled)
857     inspectThisDecl();
858   return ThisDeclInfo->CurrentDecl &&
859          isa<RecordDecl>(ThisDeclInfo->CurrentDecl) &&
860          !isUnionDecl();
861 }
862 
isObjCInterfaceDecl()863 bool Sema::isObjCInterfaceDecl() {
864   if (!ThisDeclInfo)
865     return false;
866   if (!ThisDeclInfo->IsFilled)
867     inspectThisDecl();
868   return ThisDeclInfo->CurrentDecl &&
869          isa<ObjCInterfaceDecl>(ThisDeclInfo->CurrentDecl);
870 }
871 
isObjCProtocolDecl()872 bool Sema::isObjCProtocolDecl() {
873   if (!ThisDeclInfo)
874     return false;
875   if (!ThisDeclInfo->IsFilled)
876     inspectThisDecl();
877   return ThisDeclInfo->CurrentDecl &&
878          isa<ObjCProtocolDecl>(ThisDeclInfo->CurrentDecl);
879 }
880 
getParamVars()881 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
882   if (!ThisDeclInfo->IsFilled)
883     inspectThisDecl();
884   return ThisDeclInfo->ParamVars;
885 }
886 
inspectThisDecl()887 void Sema::inspectThisDecl() {
888   ThisDeclInfo->fill();
889 }
890 
resolveParmVarReference(StringRef Name,ArrayRef<const ParmVarDecl * > ParamVars)891 unsigned Sema::resolveParmVarReference(StringRef Name,
892                                        ArrayRef<const ParmVarDecl *> ParamVars) {
893   for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
894     const IdentifierInfo *II = ParamVars[i]->getIdentifier();
895     if (II && II->getName() == Name)
896       return i;
897   }
898   return ParamCommandComment::InvalidParamIndex;
899 }
900 
901 namespace {
902 class SimpleTypoCorrector {
903   StringRef Typo;
904   const unsigned MaxEditDistance;
905 
906   const NamedDecl *BestDecl;
907   unsigned BestEditDistance;
908   unsigned BestIndex;
909   unsigned NextIndex;
910 
911 public:
SimpleTypoCorrector(StringRef Typo)912   SimpleTypoCorrector(StringRef Typo) :
913       Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
914       BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
915       BestIndex(0), NextIndex(0)
916   { }
917 
918   void addDecl(const NamedDecl *ND);
919 
getBestDecl() const920   const NamedDecl *getBestDecl() const {
921     if (BestEditDistance > MaxEditDistance)
922       return NULL;
923 
924     return BestDecl;
925   }
926 
getBestDeclIndex() const927   unsigned getBestDeclIndex() const {
928     assert(getBestDecl());
929     return BestIndex;
930   }
931 };
932 
addDecl(const NamedDecl * ND)933 void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
934   unsigned CurrIndex = NextIndex++;
935 
936   const IdentifierInfo *II = ND->getIdentifier();
937   if (!II)
938     return;
939 
940   StringRef Name = II->getName();
941   unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
942   if (MinPossibleEditDistance > 0 &&
943       Typo.size() / MinPossibleEditDistance < 3)
944     return;
945 
946   unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
947   if (EditDistance < BestEditDistance) {
948     BestEditDistance = EditDistance;
949     BestDecl = ND;
950     BestIndex = CurrIndex;
951   }
952 }
953 } // unnamed namespace
954 
correctTypoInParmVarReference(StringRef Typo,ArrayRef<const ParmVarDecl * > ParamVars)955 unsigned Sema::correctTypoInParmVarReference(
956                                     StringRef Typo,
957                                     ArrayRef<const ParmVarDecl *> ParamVars) {
958   SimpleTypoCorrector Corrector(Typo);
959   for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
960     Corrector.addDecl(ParamVars[i]);
961   if (Corrector.getBestDecl())
962     return Corrector.getBestDeclIndex();
963   else
964     return ParamCommandComment::InvalidParamIndex;
965 }
966 
967 namespace {
ResolveTParamReferenceHelper(StringRef Name,const TemplateParameterList * TemplateParameters,SmallVectorImpl<unsigned> * Position)968 bool ResolveTParamReferenceHelper(
969                             StringRef Name,
970                             const TemplateParameterList *TemplateParameters,
971                             SmallVectorImpl<unsigned> *Position) {
972   for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
973     const NamedDecl *Param = TemplateParameters->getParam(i);
974     const IdentifierInfo *II = Param->getIdentifier();
975     if (II && II->getName() == Name) {
976       Position->push_back(i);
977       return true;
978     }
979 
980     if (const TemplateTemplateParmDecl *TTP =
981             dyn_cast<TemplateTemplateParmDecl>(Param)) {
982       Position->push_back(i);
983       if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
984                                        Position))
985         return true;
986       Position->pop_back();
987     }
988   }
989   return false;
990 }
991 } // unnamed namespace
992 
resolveTParamReference(StringRef Name,const TemplateParameterList * TemplateParameters,SmallVectorImpl<unsigned> * Position)993 bool Sema::resolveTParamReference(
994                             StringRef Name,
995                             const TemplateParameterList *TemplateParameters,
996                             SmallVectorImpl<unsigned> *Position) {
997   Position->clear();
998   if (!TemplateParameters)
999     return false;
1000 
1001   return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
1002 }
1003 
1004 namespace {
CorrectTypoInTParamReferenceHelper(const TemplateParameterList * TemplateParameters,SimpleTypoCorrector & Corrector)1005 void CorrectTypoInTParamReferenceHelper(
1006                             const TemplateParameterList *TemplateParameters,
1007                             SimpleTypoCorrector &Corrector) {
1008   for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1009     const NamedDecl *Param = TemplateParameters->getParam(i);
1010     Corrector.addDecl(Param);
1011 
1012     if (const TemplateTemplateParmDecl *TTP =
1013             dyn_cast<TemplateTemplateParmDecl>(Param))
1014       CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
1015                                          Corrector);
1016   }
1017 }
1018 } // unnamed namespace
1019 
correctTypoInTParamReference(StringRef Typo,const TemplateParameterList * TemplateParameters)1020 StringRef Sema::correctTypoInTParamReference(
1021                             StringRef Typo,
1022                             const TemplateParameterList *TemplateParameters) {
1023   SimpleTypoCorrector Corrector(Typo);
1024   CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
1025   if (const NamedDecl *ND = Corrector.getBestDecl()) {
1026     const IdentifierInfo *II = ND->getIdentifier();
1027     assert(II && "SimpleTypoCorrector should not return this decl");
1028     return II->getName();
1029   }
1030   return StringRef();
1031 }
1032 
1033 InlineCommandComment::RenderKind
getInlineCommandRenderKind(StringRef Name) const1034 Sema::getInlineCommandRenderKind(StringRef Name) const {
1035   assert(Traits.getCommandInfo(Name)->IsInlineCommand);
1036 
1037   return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
1038       .Case("b", InlineCommandComment::RenderBold)
1039       .Cases("c", "p", InlineCommandComment::RenderMonospaced)
1040       .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
1041       .Default(InlineCommandComment::RenderNormal);
1042 }
1043 
1044 } // end namespace comments
1045 } // end namespace clang
1046 
1047