xref: /freebsd/contrib/llvm-project/clang/lib/AST/CommentSema.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/AST/CommentSema.h"
10 #include "clang/AST/Attr.h"
11 #include "clang/AST/CommentCommandTraits.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/DeclTemplate.h"
14 #include "clang/Basic/DiagnosticComment.h"
15 #include "clang/Basic/LLVM.h"
16 #include "clang/Basic/SimpleTypoCorrection.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Lex/Preprocessor.h"
19 #include "llvm/ADT/StringSwitch.h"
20 
21 namespace clang {
22 namespace comments {
23 
24 namespace {
25 #include "clang/AST/CommentHTMLTagsProperties.inc"
26 } // end anonymous 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(nullptr), BriefCommand(nullptr),
33     HeaderfileCommand(nullptr) {
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   if (ThisDeclInfo) {
73     // These checks only make sense if the comment is attached to a
74     // declaration.
75     checkReturnsCommand(Command);
76     checkDeprecatedCommand(Command);
77   }
78 }
79 
actOnParamCommandStart(SourceLocation LocBegin,SourceLocation LocEnd,unsigned CommandID,CommandMarkerKind CommandMarker)80 ParamCommandComment *Sema::actOnParamCommandStart(
81                                       SourceLocation LocBegin,
82                                       SourceLocation LocEnd,
83                                       unsigned CommandID,
84                                       CommandMarkerKind CommandMarker) {
85   ParamCommandComment *Command =
86       new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,
87                                           CommandMarker);
88 
89   if (!involvesFunctionType())
90     Diag(Command->getLocation(),
91          diag::warn_doc_param_not_attached_to_a_function_decl)
92       << CommandMarker
93       << Command->getCommandNameRange(Traits);
94 
95   return Command;
96 }
97 
checkFunctionDeclVerbatimLine(const BlockCommandComment * Comment)98 void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
99   const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
100   if (!Info->IsFunctionDeclarationCommand)
101     return;
102 
103   unsigned DiagSelect;
104   switch (Comment->getCommandID()) {
105     case CommandTraits::KCI_function:
106       DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 1 : 0;
107       break;
108     case CommandTraits::KCI_functiongroup:
109       DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 2 : 0;
110       break;
111     case CommandTraits::KCI_method:
112       DiagSelect = !isObjCMethodDecl() ? 3 : 0;
113       break;
114     case CommandTraits::KCI_methodgroup:
115       DiagSelect = !isObjCMethodDecl() ? 4 : 0;
116       break;
117     case CommandTraits::KCI_callback:
118       DiagSelect = !isFunctionPointerVarDecl() ? 5 : 0;
119       break;
120     default:
121       DiagSelect = 0;
122       break;
123   }
124   if (DiagSelect)
125     Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)
126     << Comment->getCommandMarker()
127     << (DiagSelect-1) << (DiagSelect-1)
128     << Comment->getSourceRange();
129 }
130 
checkContainerDeclVerbatimLine(const BlockCommandComment * Comment)131 void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {
132   const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
133   if (!Info->IsRecordLikeDeclarationCommand)
134     return;
135   std::optional<unsigned> DiagSelect;
136   switch (Comment->getCommandID()) {
137     case CommandTraits::KCI_class:
138       if (!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl())
139         DiagSelect = diag::DeclContainerKind::Class;
140 
141       // Allow @class command on @interface declarations.
142       // FIXME. Currently, \class and @class are indistinguishable. So,
143       // \class is also allowed on an @interface declaration
144       if (DiagSelect && Comment->getCommandMarker() && isObjCInterfaceDecl())
145         DiagSelect = std::nullopt;
146       break;
147     case CommandTraits::KCI_interface:
148       if (!isObjCInterfaceDecl())
149         DiagSelect = diag::DeclContainerKind::Interface;
150       break;
151     case CommandTraits::KCI_protocol:
152       if (!isObjCProtocolDecl())
153         DiagSelect = diag::DeclContainerKind::Protocol;
154       break;
155     case CommandTraits::KCI_struct:
156       if (!isClassOrStructOrTagTypedefDecl())
157         DiagSelect = diag::DeclContainerKind::Struct;
158       break;
159     case CommandTraits::KCI_union:
160       if (!isUnionDecl())
161         DiagSelect = diag::DeclContainerKind::Union;
162       break;
163     default:
164       DiagSelect = std::nullopt;
165       break;
166   }
167   if (DiagSelect)
168     Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
169         << Comment->getCommandMarker() << (*DiagSelect) << (*DiagSelect)
170         << Comment->getSourceRange();
171 }
172 
checkContainerDecl(const BlockCommandComment * Comment)173 void Sema::checkContainerDecl(const BlockCommandComment *Comment) {
174   const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
175   if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
176     return;
177   std::optional<unsigned> DiagSelect;
178   switch (Comment->getCommandID()) {
179     case CommandTraits::KCI_classdesign:
180       DiagSelect = diag::DocCommandKind::ClassDesign;
181       break;
182     case CommandTraits::KCI_coclass:
183       DiagSelect = diag::DocCommandKind::CoClass;
184       break;
185     case CommandTraits::KCI_dependency:
186       DiagSelect = diag::DocCommandKind::Dependency;
187       break;
188     case CommandTraits::KCI_helper:
189       DiagSelect = diag::DocCommandKind::Helper;
190       break;
191     case CommandTraits::KCI_helperclass:
192       DiagSelect = diag::DocCommandKind::HelperClass;
193       break;
194     case CommandTraits::KCI_helps:
195       DiagSelect = diag::DocCommandKind::Helps;
196       break;
197     case CommandTraits::KCI_instancesize:
198       DiagSelect = diag::DocCommandKind::InstanceSize;
199       break;
200     case CommandTraits::KCI_ownership:
201       DiagSelect = diag::DocCommandKind::Ownership;
202       break;
203     case CommandTraits::KCI_performance:
204       DiagSelect = diag::DocCommandKind::Performance;
205       break;
206     case CommandTraits::KCI_security:
207       DiagSelect = diag::DocCommandKind::Security;
208       break;
209     case CommandTraits::KCI_superclass:
210       DiagSelect = diag::DocCommandKind::Superclass;
211       break;
212     default:
213       DiagSelect = std::nullopt;
214       break;
215   }
216   if (DiagSelect)
217     Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
218         << Comment->getCommandMarker() << (*DiagSelect)
219         << Comment->getSourceRange();
220 }
221 
222 /// Turn a string into the corresponding PassDirection or -1 if it's not
223 /// valid.
getParamPassDirection(StringRef Arg)224 static ParamCommandPassDirection getParamPassDirection(StringRef Arg) {
225   return llvm::StringSwitch<ParamCommandPassDirection>(Arg)
226       .Case("[in]", ParamCommandPassDirection::In)
227       .Case("[out]", ParamCommandPassDirection::Out)
228       .Cases("[in,out]", "[out,in]", ParamCommandPassDirection::InOut)
229       .Default(static_cast<ParamCommandPassDirection>(-1));
230 }
231 
actOnParamCommandDirectionArg(ParamCommandComment * Command,SourceLocation ArgLocBegin,SourceLocation ArgLocEnd,StringRef Arg)232 void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
233                                          SourceLocation ArgLocBegin,
234                                          SourceLocation ArgLocEnd,
235                                          StringRef Arg) {
236   std::string ArgLower = Arg.lower();
237   ParamCommandPassDirection Direction = getParamPassDirection(ArgLower);
238 
239   if (Direction == static_cast<ParamCommandPassDirection>(-1)) {
240     // Try again with whitespace removed.
241     llvm::erase_if(ArgLower, clang::isWhitespace);
242     Direction = getParamPassDirection(ArgLower);
243 
244     SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
245     if (Direction != static_cast<ParamCommandPassDirection>(-1)) {
246       const char *FixedName =
247           ParamCommandComment::getDirectionAsString(Direction);
248       Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
249           << ArgRange << FixItHint::CreateReplacement(ArgRange, FixedName);
250     } else {
251       Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange;
252       Direction = ParamCommandPassDirection::In; // Sane fall back.
253     }
254   }
255   Command->setDirection(Direction,
256                         /*Explicit=*/true);
257 }
258 
actOnParamCommandParamNameArg(ParamCommandComment * Command,SourceLocation ArgLocBegin,SourceLocation ArgLocEnd,StringRef Arg)259 void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
260                                          SourceLocation ArgLocBegin,
261                                          SourceLocation ArgLocEnd,
262                                          StringRef Arg) {
263   // Parser will not feed us more arguments than needed.
264   assert(Command->getNumArgs() == 0);
265 
266   if (!Command->isDirectionExplicit()) {
267     // User didn't provide a direction argument.
268     Command->setDirection(ParamCommandPassDirection::In,
269                           /* Explicit = */ false);
270   }
271   auto *A = new (Allocator)
272       Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};
273   Command->setArgs(ArrayRef(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   auto *A = new (Allocator)
308       Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};
309   Command->setArgs(ArrayRef(A, 1));
310 
311   if (!isTemplateOrSpecialization()) {
312     // We already warned that this \\tparam is not attached to a template decl.
313     return;
314   }
315 
316   const TemplateParameterList *TemplateParameters =
317       ThisDeclInfo->TemplateParameters;
318   SmallVector<unsigned, 2> Position;
319   if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
320     Command->setPosition(copyArray(ArrayRef(Position)));
321     TParamCommandComment *&PrevCommand = TemplateParameterDocs[Arg];
322     if (PrevCommand) {
323       SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
324       Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
325         << Arg << ArgRange;
326       Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
327         << PrevCommand->getParamNameRange();
328     }
329     PrevCommand = Command;
330     return;
331   }
332 
333   SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
334   Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
335     << Arg << ArgRange;
336 
337   if (!TemplateParameters || TemplateParameters->size() == 0)
338     return;
339 
340   StringRef CorrectedName;
341   if (TemplateParameters->size() == 1) {
342     const NamedDecl *Param = TemplateParameters->getParam(0);
343     const IdentifierInfo *II = Param->getIdentifier();
344     if (II)
345       CorrectedName = II->getName();
346   } else {
347     CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
348   }
349 
350   if (!CorrectedName.empty()) {
351     Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
352       << CorrectedName
353       << FixItHint::CreateReplacement(ArgRange, CorrectedName);
354   }
355 }
356 
actOnTParamCommandFinish(TParamCommandComment * Command,ParagraphComment * Paragraph)357 void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
358                                     ParagraphComment *Paragraph) {
359   Command->setParagraph(Paragraph);
360   checkBlockCommandEmptyParagraph(Command);
361 }
362 
363 InlineCommandComment *
actOnInlineCommand(SourceLocation CommandLocBegin,SourceLocation CommandLocEnd,unsigned CommandID,ArrayRef<Comment::Argument> Args)364 Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
365                          SourceLocation CommandLocEnd, unsigned CommandID,
366                          ArrayRef<Comment::Argument> Args) {
367   StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
368 
369   return new (Allocator)
370       InlineCommandComment(CommandLocBegin, CommandLocEnd, CommandID,
371                            getInlineCommandRenderKind(CommandName), Args);
372 }
373 
actOnUnknownCommand(SourceLocation LocBegin,SourceLocation LocEnd,StringRef CommandName)374 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
375                                                 SourceLocation LocEnd,
376                                                 StringRef CommandName) {
377   unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
378   return actOnUnknownCommand(LocBegin, LocEnd, CommandID);
379 }
380 
actOnUnknownCommand(SourceLocation LocBegin,SourceLocation LocEnd,unsigned CommandID)381 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
382                                                 SourceLocation LocEnd,
383                                                 unsigned CommandID) {
384   ArrayRef<InlineCommandComment::Argument> Args;
385   return new (Allocator) InlineCommandComment(
386       LocBegin, LocEnd, CommandID, InlineCommandRenderKind::Normal, Args);
387 }
388 
actOnText(SourceLocation LocBegin,SourceLocation LocEnd,StringRef Text)389 TextComment *Sema::actOnText(SourceLocation LocBegin,
390                              SourceLocation LocEnd,
391                              StringRef Text) {
392   return new (Allocator) TextComment(LocBegin, LocEnd, Text);
393 }
394 
actOnVerbatimBlockStart(SourceLocation Loc,unsigned CommandID)395 VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
396                                                     unsigned CommandID) {
397   StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
398   return new (Allocator) VerbatimBlockComment(
399                                   Loc,
400                                   Loc.getLocWithOffset(1 + CommandName.size()),
401                                   CommandID);
402 }
403 
actOnVerbatimBlockLine(SourceLocation Loc,StringRef Text)404 VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
405                                                        StringRef Text) {
406   return new (Allocator) VerbatimBlockLineComment(Loc, Text);
407 }
408 
actOnVerbatimBlockFinish(VerbatimBlockComment * Block,SourceLocation CloseNameLocBegin,StringRef CloseName,ArrayRef<VerbatimBlockLineComment * > Lines)409 void Sema::actOnVerbatimBlockFinish(
410                             VerbatimBlockComment *Block,
411                             SourceLocation CloseNameLocBegin,
412                             StringRef CloseName,
413                             ArrayRef<VerbatimBlockLineComment *> Lines) {
414   Block->setCloseName(CloseName, CloseNameLocBegin);
415   Block->setLines(Lines);
416 }
417 
actOnVerbatimLine(SourceLocation LocBegin,unsigned CommandID,SourceLocation TextBegin,StringRef Text)418 VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
419                                              unsigned CommandID,
420                                              SourceLocation TextBegin,
421                                              StringRef Text) {
422   VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
423                               LocBegin,
424                               TextBegin.getLocWithOffset(Text.size()),
425                               CommandID,
426                               TextBegin,
427                               Text);
428   checkFunctionDeclVerbatimLine(VL);
429   checkContainerDeclVerbatimLine(VL);
430   return VL;
431 }
432 
actOnHTMLStartTagStart(SourceLocation LocBegin,StringRef TagName)433 HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
434                                                   StringRef TagName) {
435   return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
436 }
437 
actOnHTMLStartTagFinish(HTMLStartTagComment * Tag,ArrayRef<HTMLStartTagComment::Attribute> Attrs,SourceLocation GreaterLoc,bool IsSelfClosing)438 void Sema::actOnHTMLStartTagFinish(
439                               HTMLStartTagComment *Tag,
440                               ArrayRef<HTMLStartTagComment::Attribute> Attrs,
441                               SourceLocation GreaterLoc,
442                               bool IsSelfClosing) {
443   Tag->setAttrs(Attrs);
444   Tag->setGreaterLoc(GreaterLoc);
445   if (IsSelfClosing)
446     Tag->setSelfClosing();
447   else if (!isHTMLEndTagForbidden(Tag->getTagName()))
448     HTMLOpenTags.push_back(Tag);
449 }
450 
actOnHTMLEndTag(SourceLocation LocBegin,SourceLocation LocEnd,StringRef TagName)451 HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
452                                          SourceLocation LocEnd,
453                                          StringRef TagName) {
454   HTMLEndTagComment *HET =
455       new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
456   if (isHTMLEndTagForbidden(TagName)) {
457     Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
458       << TagName << HET->getSourceRange();
459     HET->setIsMalformed();
460     return HET;
461   }
462 
463   bool FoundOpen = false;
464   for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
465        I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
466        I != E; ++I) {
467     if ((*I)->getTagName() == TagName) {
468       FoundOpen = true;
469       break;
470     }
471   }
472   if (!FoundOpen) {
473     Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
474       << HET->getSourceRange();
475     HET->setIsMalformed();
476     return HET;
477   }
478 
479   while (!HTMLOpenTags.empty()) {
480     HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
481     StringRef LastNotClosedTagName = HST->getTagName();
482     if (LastNotClosedTagName == TagName) {
483       // If the start tag is malformed, end tag is malformed as well.
484       if (HST->isMalformed())
485         HET->setIsMalformed();
486       break;
487     }
488 
489     if (isHTMLEndTagOptional(LastNotClosedTagName))
490       continue;
491 
492     bool OpenLineInvalid;
493     const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
494                                                 HST->getLocation(),
495                                                 &OpenLineInvalid);
496     bool CloseLineInvalid;
497     const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
498                                                 HET->getLocation(),
499                                                 &CloseLineInvalid);
500 
501     if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
502       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
503         << HST->getTagName() << HET->getTagName()
504         << HST->getSourceRange() << HET->getSourceRange();
505       HST->setIsMalformed();
506     } else {
507       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
508         << HST->getTagName() << HET->getTagName()
509         << HST->getSourceRange();
510       Diag(HET->getLocation(), diag::note_doc_html_end_tag)
511         << HET->getSourceRange();
512       HST->setIsMalformed();
513     }
514   }
515 
516   return HET;
517 }
518 
actOnFullComment(ArrayRef<BlockContentComment * > Blocks)519 FullComment *Sema::actOnFullComment(
520                               ArrayRef<BlockContentComment *> Blocks) {
521   FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
522   resolveParamCommandIndexes(FC);
523 
524   // Complain about HTML tags that are not closed.
525   while (!HTMLOpenTags.empty()) {
526     HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
527     if (isHTMLEndTagOptional(HST->getTagName()))
528       continue;
529 
530     Diag(HST->getLocation(), diag::warn_doc_html_missing_end_tag)
531       << HST->getTagName() << HST->getSourceRange();
532     HST->setIsMalformed();
533   }
534 
535   return FC;
536 }
537 
checkBlockCommandEmptyParagraph(BlockCommandComment * Command)538 void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
539   if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)
540     return;
541 
542   ParagraphComment *Paragraph = Command->getParagraph();
543   if (Paragraph->isWhitespace()) {
544     SourceLocation DiagLoc;
545     if (Command->getNumArgs() > 0)
546       DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
547     if (!DiagLoc.isValid())
548       DiagLoc = Command->getCommandNameRange(Traits).getEnd();
549     Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
550       << Command->getCommandMarker()
551       << Command->getCommandName(Traits)
552       << Command->getSourceRange();
553   }
554 }
555 
checkReturnsCommand(const BlockCommandComment * Command)556 void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
557   if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
558     return;
559 
560   assert(ThisDeclInfo && "should not call this check on a bare comment");
561 
562   // We allow the return command for all @properties because it can be used
563   // to document the value that the property getter returns.
564   if (isObjCPropertyDecl())
565     return;
566   if (involvesFunctionType()) {
567     assert(!ThisDeclInfo->ReturnType.isNull() &&
568            "should have a valid return type");
569     if (ThisDeclInfo->ReturnType->isVoidType()) {
570       unsigned DiagKind;
571       switch (ThisDeclInfo->CommentDecl->getKind()) {
572       default:
573         if (ThisDeclInfo->IsObjCMethod)
574           DiagKind = 3;
575         else
576           DiagKind = 0;
577         break;
578       case Decl::CXXConstructor:
579         DiagKind = 1;
580         break;
581       case Decl::CXXDestructor:
582         DiagKind = 2;
583         break;
584       }
585       Diag(Command->getLocation(),
586            diag::warn_doc_returns_attached_to_a_void_function)
587         << Command->getCommandMarker()
588         << Command->getCommandName(Traits)
589         << DiagKind
590         << Command->getSourceRange();
591     }
592     return;
593   }
594 
595   Diag(Command->getLocation(),
596        diag::warn_doc_returns_not_attached_to_a_function_decl)
597     << Command->getCommandMarker()
598     << Command->getCommandName(Traits)
599     << Command->getSourceRange();
600 }
601 
checkBlockCommandDuplicate(const BlockCommandComment * Command)602 void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
603   const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
604   const BlockCommandComment *PrevCommand = nullptr;
605   if (Info->IsBriefCommand) {
606     if (!BriefCommand) {
607       BriefCommand = Command;
608       return;
609     }
610     PrevCommand = BriefCommand;
611   } else if (Info->IsHeaderfileCommand) {
612     if (!HeaderfileCommand) {
613       HeaderfileCommand = Command;
614       return;
615     }
616     PrevCommand = HeaderfileCommand;
617   } else {
618     // We don't want to check this command for duplicates.
619     return;
620   }
621   StringRef CommandName = Command->getCommandName(Traits);
622   StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
623   Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
624       << Command->getCommandMarker()
625       << CommandName
626       << Command->getSourceRange();
627   if (CommandName == PrevCommandName)
628     Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
629         << PrevCommand->getCommandMarker()
630         << PrevCommandName
631         << PrevCommand->getSourceRange();
632   else
633     Diag(PrevCommand->getLocation(),
634          diag::note_doc_block_command_previous_alias)
635         << PrevCommand->getCommandMarker()
636         << PrevCommandName
637         << CommandName;
638 }
639 
checkDeprecatedCommand(const BlockCommandComment * Command)640 void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
641   if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
642     return;
643 
644   assert(ThisDeclInfo && "should not call this check on a bare comment");
645 
646   const Decl *D = ThisDeclInfo->CommentDecl;
647   if (!D)
648     return;
649 
650   if (D->hasAttr<DeprecatedAttr>() ||
651       D->hasAttr<AvailabilityAttr>() ||
652       D->hasAttr<UnavailableAttr>())
653     return;
654 
655   Diag(Command->getLocation(), diag::warn_doc_deprecated_not_sync)
656       << Command->getSourceRange() << Command->getCommandMarker();
657 
658   // Try to emit a fixit with a deprecation attribute.
659   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
660     // Don't emit a Fix-It for non-member function definitions.  GCC does not
661     // accept attributes on them.
662     const DeclContext *Ctx = FD->getDeclContext();
663     if ((!Ctx || !Ctx->isRecord()) &&
664         FD->doesThisDeclarationHaveABody())
665       return;
666 
667     const LangOptions &LO = FD->getLangOpts();
668     const bool DoubleSquareBracket = LO.CPlusPlus14 || LO.C23;
669     StringRef AttributeSpelling =
670         DoubleSquareBracket ? "[[deprecated]]" : "__attribute__((deprecated))";
671     if (PP) {
672       // Try to find a replacement macro:
673       // - In C23/C++14 we prefer [[deprecated]].
674       // - If not found or an older C/C++ look for __attribute__((deprecated)).
675       StringRef MacroName;
676       if (DoubleSquareBracket) {
677         TokenValue Tokens[] = {tok::l_square, tok::l_square,
678                                PP->getIdentifierInfo("deprecated"),
679                                tok::r_square, tok::r_square};
680         MacroName = PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);
681         if (!MacroName.empty())
682           AttributeSpelling = MacroName;
683       }
684 
685       if (MacroName.empty()) {
686         TokenValue Tokens[] = {
687             tok::kw___attribute, tok::l_paren,
688             tok::l_paren,        PP->getIdentifierInfo("deprecated"),
689             tok::r_paren,        tok::r_paren};
690         StringRef MacroName =
691             PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);
692         if (!MacroName.empty())
693           AttributeSpelling = MacroName;
694       }
695     }
696 
697     SmallString<64> TextToInsert = AttributeSpelling;
698     TextToInsert += " ";
699     SourceLocation Loc = FD->getSourceRange().getBegin();
700     Diag(Loc, diag::note_add_deprecation_attr)
701         << FixItHint::CreateInsertion(Loc, TextToInsert);
702   }
703 }
704 
resolveParamCommandIndexes(const FullComment * FC)705 void Sema::resolveParamCommandIndexes(const FullComment *FC) {
706   if (!involvesFunctionType()) {
707     // We already warned that \\param commands are not attached to a function
708     // decl.
709     return;
710   }
711 
712   SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
713 
714   // Comment AST nodes that correspond to \c ParamVars for which we have
715   // found a \\param command or NULL if no documentation was found so far.
716   SmallVector<ParamCommandComment *, 8> ParamVarDocs;
717 
718   ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
719   ParamVarDocs.resize(ParamVars.size(), nullptr);
720 
721   // First pass over all \\param commands: resolve all parameter names.
722   for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
723        I != E; ++I) {
724     ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
725     if (!PCC || !PCC->hasParamName())
726       continue;
727     StringRef ParamName = PCC->getParamNameAsWritten();
728 
729     // Check that referenced parameter name is in the function decl.
730     const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
731                                                                 ParamVars);
732     if (ResolvedParamIndex == ParamCommandComment::VarArgParamIndex) {
733       PCC->setIsVarArgParam();
734       continue;
735     }
736     if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
737       UnresolvedParamCommands.push_back(PCC);
738       continue;
739     }
740     PCC->setParamIndex(ResolvedParamIndex);
741     if (ParamVarDocs[ResolvedParamIndex]) {
742       SourceRange ArgRange = PCC->getParamNameRange();
743       Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
744         << ParamName << ArgRange;
745       ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
746       Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
747         << PrevCommand->getParamNameRange();
748     }
749     ParamVarDocs[ResolvedParamIndex] = PCC;
750   }
751 
752   // Find parameter declarations that have no corresponding \\param.
753   SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
754   for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
755     if (!ParamVarDocs[i])
756       OrphanedParamDecls.push_back(ParamVars[i]);
757   }
758 
759   // Second pass over unresolved \\param commands: do typo correction.
760   // Suggest corrections from a set of parameter declarations that have no
761   // corresponding \\param.
762   for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
763     const ParamCommandComment *PCC = UnresolvedParamCommands[i];
764 
765     SourceRange ArgRange = PCC->getParamNameRange();
766     StringRef ParamName = PCC->getParamNameAsWritten();
767     Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
768       << ParamName << ArgRange;
769 
770     // All parameters documented -- can't suggest a correction.
771     if (OrphanedParamDecls.size() == 0)
772       continue;
773 
774     unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
775     if (OrphanedParamDecls.size() == 1) {
776       // If one parameter is not documented then that parameter is the only
777       // possible suggestion.
778       CorrectedParamIndex = 0;
779     } else {
780       // Do typo correction.
781       CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
782                                                           OrphanedParamDecls);
783     }
784     if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
785       const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
786       if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
787         Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
788           << CorrectedII->getName()
789           << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
790     }
791   }
792 }
793 
involvesFunctionType()794 bool Sema::involvesFunctionType() {
795   if (!ThisDeclInfo)
796     return false;
797   if (!ThisDeclInfo->IsFilled)
798     inspectThisDecl();
799   return ThisDeclInfo->involvesFunctionType();
800 }
801 
isFunctionDecl()802 bool Sema::isFunctionDecl() {
803   if (!ThisDeclInfo)
804     return false;
805   if (!ThisDeclInfo->IsFilled)
806     inspectThisDecl();
807   return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
808 }
809 
isAnyFunctionDecl()810 bool Sema::isAnyFunctionDecl() {
811   return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
812          isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);
813 }
814 
isFunctionOrMethodVariadic()815 bool Sema::isFunctionOrMethodVariadic() {
816   if (!ThisDeclInfo)
817     return false;
818   if (!ThisDeclInfo->IsFilled)
819     inspectThisDecl();
820   return ThisDeclInfo->IsVariadic;
821 }
822 
isObjCMethodDecl()823 bool Sema::isObjCMethodDecl() {
824   return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
825          isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);
826 }
827 
isFunctionPointerVarDecl()828 bool Sema::isFunctionPointerVarDecl() {
829   if (!ThisDeclInfo)
830     return false;
831   if (!ThisDeclInfo->IsFilled)
832     inspectThisDecl();
833   if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {
834     if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {
835       QualType QT = VD->getType();
836       return QT->isFunctionPointerType();
837     }
838   }
839   return false;
840 }
841 
isObjCPropertyDecl()842 bool Sema::isObjCPropertyDecl() {
843   if (!ThisDeclInfo)
844     return false;
845   if (!ThisDeclInfo->IsFilled)
846     inspectThisDecl();
847   return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;
848 }
849 
isTemplateOrSpecialization()850 bool Sema::isTemplateOrSpecialization() {
851   if (!ThisDeclInfo)
852     return false;
853   if (!ThisDeclInfo->IsFilled)
854     inspectThisDecl();
855   return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
856 }
857 
isRecordLikeDecl()858 bool Sema::isRecordLikeDecl() {
859   if (!ThisDeclInfo)
860     return false;
861   if (!ThisDeclInfo->IsFilled)
862     inspectThisDecl();
863   return isUnionDecl() || isClassOrStructDecl() || isObjCInterfaceDecl() ||
864          isObjCProtocolDecl();
865 }
866 
isUnionDecl()867 bool Sema::isUnionDecl() {
868   if (!ThisDeclInfo)
869     return false;
870   if (!ThisDeclInfo->IsFilled)
871     inspectThisDecl();
872   if (const RecordDecl *RD =
873         dyn_cast_or_null<RecordDecl>(ThisDeclInfo->CurrentDecl))
874     return RD->isUnion();
875   return false;
876 }
isClassOrStructDeclImpl(const Decl * D)877 static bool isClassOrStructDeclImpl(const Decl *D) {
878   if (auto *record = dyn_cast_or_null<RecordDecl>(D))
879     return !record->isUnion();
880 
881   return false;
882 }
883 
isClassOrStructDecl()884 bool Sema::isClassOrStructDecl() {
885   if (!ThisDeclInfo)
886     return false;
887   if (!ThisDeclInfo->IsFilled)
888     inspectThisDecl();
889 
890   if (!ThisDeclInfo->CurrentDecl)
891     return false;
892 
893   return isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl);
894 }
895 
isClassOrStructOrTagTypedefDecl()896 bool Sema::isClassOrStructOrTagTypedefDecl() {
897   if (!ThisDeclInfo)
898     return false;
899   if (!ThisDeclInfo->IsFilled)
900     inspectThisDecl();
901 
902   if (!ThisDeclInfo->CurrentDecl)
903     return false;
904 
905   if (isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl))
906     return true;
907 
908   if (auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(ThisDeclInfo->CurrentDecl)) {
909     auto UnderlyingType = ThisTypedefDecl->getUnderlyingType();
910     if (auto ThisElaboratedType = dyn_cast<ElaboratedType>(UnderlyingType)) {
911       auto DesugaredType = ThisElaboratedType->desugar();
912       if (auto *DesugaredTypePtr = DesugaredType.getTypePtrOrNull()) {
913         if (auto *ThisRecordType = dyn_cast<RecordType>(DesugaredTypePtr)) {
914           return isClassOrStructDeclImpl(ThisRecordType->getAsRecordDecl());
915         }
916       }
917     }
918   }
919 
920   return false;
921 }
922 
isClassTemplateDecl()923 bool Sema::isClassTemplateDecl() {
924   if (!ThisDeclInfo)
925     return false;
926   if (!ThisDeclInfo->IsFilled)
927     inspectThisDecl();
928   return ThisDeclInfo->CurrentDecl &&
929           (isa<ClassTemplateDecl>(ThisDeclInfo->CurrentDecl));
930 }
931 
isFunctionTemplateDecl()932 bool Sema::isFunctionTemplateDecl() {
933   if (!ThisDeclInfo)
934     return false;
935   if (!ThisDeclInfo->IsFilled)
936     inspectThisDecl();
937   return ThisDeclInfo->CurrentDecl &&
938          (isa<FunctionTemplateDecl>(ThisDeclInfo->CurrentDecl));
939 }
940 
isObjCInterfaceDecl()941 bool Sema::isObjCInterfaceDecl() {
942   if (!ThisDeclInfo)
943     return false;
944   if (!ThisDeclInfo->IsFilled)
945     inspectThisDecl();
946   return ThisDeclInfo->CurrentDecl &&
947          isa<ObjCInterfaceDecl>(ThisDeclInfo->CurrentDecl);
948 }
949 
isObjCProtocolDecl()950 bool Sema::isObjCProtocolDecl() {
951   if (!ThisDeclInfo)
952     return false;
953   if (!ThisDeclInfo->IsFilled)
954     inspectThisDecl();
955   return ThisDeclInfo->CurrentDecl &&
956          isa<ObjCProtocolDecl>(ThisDeclInfo->CurrentDecl);
957 }
958 
getParamVars()959 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
960   if (!ThisDeclInfo->IsFilled)
961     inspectThisDecl();
962   return ThisDeclInfo->ParamVars;
963 }
964 
inspectThisDecl()965 void Sema::inspectThisDecl() {
966   ThisDeclInfo->fill();
967 }
968 
resolveParmVarReference(StringRef Name,ArrayRef<const ParmVarDecl * > ParamVars)969 unsigned Sema::resolveParmVarReference(StringRef Name,
970                                        ArrayRef<const ParmVarDecl *> ParamVars) {
971   for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
972     const IdentifierInfo *II = ParamVars[i]->getIdentifier();
973     if (II && II->getName() == Name)
974       return i;
975   }
976   if (Name == "..." && isFunctionOrMethodVariadic())
977     return ParamCommandComment::VarArgParamIndex;
978   return ParamCommandComment::InvalidParamIndex;
979 }
980 
981 unsigned
correctTypoInParmVarReference(StringRef Typo,ArrayRef<const ParmVarDecl * > ParamVars)982 Sema::correctTypoInParmVarReference(StringRef Typo,
983                                     ArrayRef<const ParmVarDecl *> ParamVars) {
984   SimpleTypoCorrection STC(Typo);
985   for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
986     const ParmVarDecl *Param = ParamVars[i];
987     if (!Param)
988       continue;
989 
990     STC.add(Param->getIdentifier());
991   }
992 
993   if (STC.hasCorrection())
994     return STC.getCorrectionIndex();
995 
996   return ParamCommandComment::InvalidParamIndex;
997 }
998 
999 namespace {
ResolveTParamReferenceHelper(StringRef Name,const TemplateParameterList * TemplateParameters,SmallVectorImpl<unsigned> * Position)1000 bool ResolveTParamReferenceHelper(
1001                             StringRef Name,
1002                             const TemplateParameterList *TemplateParameters,
1003                             SmallVectorImpl<unsigned> *Position) {
1004   for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1005     const NamedDecl *Param = TemplateParameters->getParam(i);
1006     const IdentifierInfo *II = Param->getIdentifier();
1007     if (II && II->getName() == Name) {
1008       Position->push_back(i);
1009       return true;
1010     }
1011 
1012     if (const TemplateTemplateParmDecl *TTP =
1013             dyn_cast<TemplateTemplateParmDecl>(Param)) {
1014       Position->push_back(i);
1015       if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
1016                                        Position))
1017         return true;
1018       Position->pop_back();
1019     }
1020   }
1021   return false;
1022 }
1023 } // end anonymous namespace
1024 
resolveTParamReference(StringRef Name,const TemplateParameterList * TemplateParameters,SmallVectorImpl<unsigned> * Position)1025 bool Sema::resolveTParamReference(
1026                             StringRef Name,
1027                             const TemplateParameterList *TemplateParameters,
1028                             SmallVectorImpl<unsigned> *Position) {
1029   Position->clear();
1030   if (!TemplateParameters)
1031     return false;
1032 
1033   return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
1034 }
1035 
1036 namespace {
CorrectTypoInTParamReferenceHelper(const TemplateParameterList * TemplateParameters,SimpleTypoCorrection & STC)1037 void CorrectTypoInTParamReferenceHelper(
1038     const TemplateParameterList *TemplateParameters,
1039     SimpleTypoCorrection &STC) {
1040   for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1041     const NamedDecl *Param = TemplateParameters->getParam(i);
1042     if (!Param)
1043       continue;
1044 
1045     STC.add(Param->getIdentifier());
1046 
1047     if (const TemplateTemplateParmDecl *TTP =
1048             dyn_cast<TemplateTemplateParmDecl>(Param))
1049       CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), STC);
1050   }
1051 }
1052 } // end anonymous namespace
1053 
correctTypoInTParamReference(StringRef Typo,const TemplateParameterList * TemplateParameters)1054 StringRef Sema::correctTypoInTParamReference(
1055                             StringRef Typo,
1056                             const TemplateParameterList *TemplateParameters) {
1057   SimpleTypoCorrection STC(Typo);
1058   CorrectTypoInTParamReferenceHelper(TemplateParameters, STC);
1059 
1060   if (auto CorrectedTParamReference = STC.getCorrection())
1061     return *CorrectedTParamReference;
1062 
1063   return StringRef();
1064 }
1065 
getInlineCommandRenderKind(StringRef Name) const1066 InlineCommandRenderKind Sema::getInlineCommandRenderKind(StringRef Name) const {
1067   assert(Traits.getCommandInfo(Name)->IsInlineCommand);
1068 
1069   return llvm::StringSwitch<InlineCommandRenderKind>(Name)
1070       .Case("b", InlineCommandRenderKind::Bold)
1071       .Cases("c", "p", InlineCommandRenderKind::Monospaced)
1072       .Cases("a", "e", "em", InlineCommandRenderKind::Emphasized)
1073       .Case("anchor", InlineCommandRenderKind::Anchor)
1074       .Default(InlineCommandRenderKind::Normal);
1075 }
1076 
1077 } // end namespace comments
1078 } // end namespace clang
1079