clang 22.0.0git
CommentSema.cpp
Go to the documentation of this file.
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
10#include "clang/AST/Attr.h"
12#include "clang/AST/Decl.h"
15#include "clang/Basic/LLVM.h"
19#include "llvm/ADT/StringSwitch.h"
20
21namespace clang {
22namespace comments {
23
24namespace {
25#include "clang/AST/CommentHTMLTagsProperties.inc"
26} // end anonymous namespace
27
28Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
29 DiagnosticsEngine &Diags, CommandTraits &Traits,
30 const Preprocessor *PP) :
31 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
32 PP(PP), ThisDeclInfo(nullptr), BriefCommand(nullptr),
33 HeaderfileCommand(nullptr) {
34}
35
36void 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
47 return new (Allocator) ParagraphComment(Content);
48}
49
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
64 Command->setArgs(Args);
65}
66
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
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
98void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
99 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
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
131void 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
173void 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.
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
233 SourceLocation ArgLocBegin,
234 SourceLocation ArgLocEnd,
235 StringRef Arg) {
236 std::string ArgLower = Arg.lower();
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 =
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
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.
269 /* Explicit = */ false);
270 }
271 auto *A = new (Allocator)
272 Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};
273 Command->setArgs(ArrayRef(A, 1));
274}
275
277 ParagraphComment *Paragraph) {
278 Command->setParagraph(Paragraph);
279 checkBlockCommandEmptyParagraph(Command);
280}
281
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
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;
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
358 ParagraphComment *Paragraph) {
359 Command->setParagraph(Paragraph);
360 checkBlockCommandEmptyParagraph(Command);
361}
362
365 SourceLocation CommandLocEnd, unsigned CommandID,
366 CommandMarkerKind CommandMarker,
368 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
369
370 return new (Allocator) InlineCommandComment(
371 CommandLocBegin, CommandLocEnd, CommandID,
372 getInlineCommandRenderKind(CommandName), CommandMarker, Args);
373}
374
376 SourceLocation LocEnd,
377 StringRef CommandName) {
378 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
379 return actOnUnknownCommand(LocBegin, LocEnd, CommandID);
380}
381
383 SourceLocation LocEnd,
384 unsigned CommandID) {
386 return new (Allocator) InlineCommandComment(
387 LocBegin, LocEnd, CommandID, InlineCommandRenderKind::Normal, Args);
388}
389
391 SourceLocation LocEnd,
392 StringRef Text) {
393 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
394}
395
397 unsigned CommandID) {
398 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
399 return new (Allocator) VerbatimBlockComment(
400 Loc,
401 Loc.getLocWithOffset(1 + CommandName.size()),
402 CommandID);
403}
404
406 StringRef Text) {
407 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
408}
409
412 SourceLocation CloseNameLocBegin,
413 StringRef CloseName,
415 Block->setCloseName(CloseName, CloseNameLocBegin);
416 Block->setLines(Lines);
417}
418
420 unsigned CommandID,
421 SourceLocation TextBegin,
422 StringRef Text) {
423 VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
424 LocBegin,
425 TextBegin.getLocWithOffset(Text.size()),
426 CommandID,
427 TextBegin,
428 Text);
429 checkFunctionDeclVerbatimLine(VL);
430 checkContainerDeclVerbatimLine(VL);
431 return VL;
432}
433
435 StringRef TagName) {
436 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
437}
438
442 SourceLocation GreaterLoc,
443 bool IsSelfClosing) {
444 Tag->setAttrs(Attrs);
445 Tag->setGreaterLoc(GreaterLoc);
446 if (IsSelfClosing)
447 Tag->setSelfClosing();
448 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
449 HTMLOpenTags.push_back(Tag);
450}
451
453 SourceLocation LocEnd,
454 StringRef TagName) {
455 HTMLEndTagComment *HET =
456 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
457 if (isHTMLEndTagForbidden(TagName)) {
458 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
459 << TagName << HET->getSourceRange();
460 HET->setIsMalformed();
461 return HET;
462 }
463
464 bool FoundOpen = false;
466 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
467 I != E; ++I) {
468 if ((*I)->getTagName() == TagName) {
469 FoundOpen = true;
470 break;
471 }
472 }
473 if (!FoundOpen) {
474 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
475 << HET->getSourceRange();
476 HET->setIsMalformed();
477 return HET;
478 }
479
480 while (!HTMLOpenTags.empty()) {
481 HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
482 StringRef LastNotClosedTagName = HST->getTagName();
483 if (LastNotClosedTagName == TagName) {
484 // If the start tag is malformed, end tag is malformed as well.
485 if (HST->isMalformed())
486 HET->setIsMalformed();
487 break;
488 }
489
490 if (isHTMLEndTagOptional(LastNotClosedTagName))
491 continue;
492
493 bool OpenLineInvalid;
494 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
495 HST->getLocation(),
496 &OpenLineInvalid);
497 bool CloseLineInvalid;
498 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
499 HET->getLocation(),
500 &CloseLineInvalid);
501
502 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
503 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
504 << HST->getTagName() << HET->getTagName()
505 << HST->getSourceRange() << HET->getSourceRange();
506 HST->setIsMalformed();
507 } else {
508 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
509 << HST->getTagName() << HET->getTagName()
510 << HST->getSourceRange();
511 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
512 << HET->getSourceRange();
513 HST->setIsMalformed();
514 }
515 }
516
517 return HET;
518}
519
522 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
523 resolveParamCommandIndexes(FC);
524
525 // Complain about HTML tags that are not closed.
526 while (!HTMLOpenTags.empty()) {
527 HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
528 if (isHTMLEndTagOptional(HST->getTagName()))
529 continue;
530
531 Diag(HST->getLocation(), diag::warn_doc_html_missing_end_tag)
532 << HST->getTagName() << HST->getSourceRange();
533 HST->setIsMalformed();
534 }
535
536 return FC;
537}
538
539void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
541 return;
542
543 ParagraphComment *Paragraph = Command->getParagraph();
544 if (Paragraph->isWhitespace()) {
545 SourceLocation DiagLoc;
546 if (Command->getNumArgs() > 0)
547 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
548 if (!DiagLoc.isValid())
549 DiagLoc = Command->getCommandNameRange(Traits).getEnd();
550 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
551 << Command->getCommandMarker()
552 << Command->getCommandName(Traits)
553 << Command->getSourceRange();
554 }
555}
556
557void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
558 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
559 return;
560
561 assert(ThisDeclInfo && "should not call this check on a bare comment");
562
563 // We allow the return command for all @properties because it can be used
564 // to document the value that the property getter returns.
565 if (isObjCPropertyDecl())
566 return;
567 if (involvesFunctionType()) {
568 assert(!ThisDeclInfo->ReturnType.isNull() &&
569 "should have a valid return type");
570 if (ThisDeclInfo->ReturnType->isVoidType()) {
571 unsigned DiagKind;
572 switch (ThisDeclInfo->CommentDecl->getKind()) {
573 default:
574 if (ThisDeclInfo->IsObjCMethod)
575 DiagKind = 3;
576 else
577 DiagKind = 0;
578 break;
579 case Decl::CXXConstructor:
580 DiagKind = 1;
581 break;
582 case Decl::CXXDestructor:
583 DiagKind = 2;
584 break;
585 }
586 Diag(Command->getLocation(),
587 diag::warn_doc_returns_attached_to_a_void_function)
588 << Command->getCommandMarker()
589 << Command->getCommandName(Traits)
590 << DiagKind
591 << Command->getSourceRange();
592 }
593 return;
594 }
595
596 Diag(Command->getLocation(),
597 diag::warn_doc_returns_not_attached_to_a_function_decl)
598 << Command->getCommandMarker()
599 << Command->getCommandName(Traits)
600 << Command->getSourceRange();
601}
602
603void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
604 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
605 const BlockCommandComment *PrevCommand = nullptr;
606 if (Info->IsBriefCommand) {
607 if (!BriefCommand) {
608 BriefCommand = Command;
609 return;
610 }
611 PrevCommand = BriefCommand;
612 } else if (Info->IsHeaderfileCommand) {
613 if (!HeaderfileCommand) {
614 HeaderfileCommand = Command;
615 return;
616 }
617 PrevCommand = HeaderfileCommand;
618 } else {
619 // We don't want to check this command for duplicates.
620 return;
621 }
622 StringRef CommandName = Command->getCommandName(Traits);
623 StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
624 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
625 << Command->getCommandMarker()
626 << CommandName
627 << Command->getSourceRange();
628 if (CommandName == PrevCommandName)
629 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
630 << PrevCommand->getCommandMarker()
631 << PrevCommandName
632 << PrevCommand->getSourceRange();
633 else
634 Diag(PrevCommand->getLocation(),
635 diag::note_doc_block_command_previous_alias)
636 << PrevCommand->getCommandMarker()
637 << PrevCommandName
638 << CommandName;
639}
640
641void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
642 if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
643 return;
644
645 assert(ThisDeclInfo && "should not call this check on a bare comment");
646
647 const Decl *D = ThisDeclInfo->CommentDecl;
648 if (!D)
649 return;
650
651 if (D->hasAttr<DeprecatedAttr>() ||
652 D->hasAttr<AvailabilityAttr>() ||
653 D->hasAttr<UnavailableAttr>())
654 return;
655
656 Diag(Command->getLocation(), diag::warn_doc_deprecated_not_sync)
657 << Command->getSourceRange() << Command->getCommandMarker();
658
659 // Try to emit a fixit with a deprecation attribute.
660 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
661 // Don't emit a Fix-It for non-member function definitions. GCC does not
662 // accept attributes on them.
663 const DeclContext *Ctx = FD->getDeclContext();
664 if ((!Ctx || !Ctx->isRecord()) &&
665 FD->doesThisDeclarationHaveABody())
666 return;
667
668 const LangOptions &LO = FD->getLangOpts();
669 const bool DoubleSquareBracket = LO.CPlusPlus14 || LO.C23;
670 StringRef AttributeSpelling =
671 DoubleSquareBracket ? "[[deprecated]]" : "__attribute__((deprecated))";
672 if (PP) {
673 // Try to find a replacement macro:
674 // - In C23/C++14 we prefer [[deprecated]].
675 // - If not found or an older C/C++ look for __attribute__((deprecated)).
676 StringRef MacroName;
677 if (DoubleSquareBracket) {
678 TokenValue Tokens[] = {tok::l_square, tok::l_square,
679 PP->getIdentifierInfo("deprecated"),
680 tok::r_square, tok::r_square};
681 MacroName = PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);
682 if (!MacroName.empty())
683 AttributeSpelling = MacroName;
684 }
685
686 if (MacroName.empty()) {
687 TokenValue Tokens[] = {
688 tok::kw___attribute, tok::l_paren,
689 tok::l_paren, PP->getIdentifierInfo("deprecated"),
690 tok::r_paren, tok::r_paren};
691 StringRef MacroName =
692 PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);
693 if (!MacroName.empty())
694 AttributeSpelling = MacroName;
695 }
696 }
697
698 SmallString<64> TextToInsert = AttributeSpelling;
699 TextToInsert += " ";
700 SourceLocation Loc = FD->getSourceRange().getBegin();
701 Diag(Loc, diag::note_add_deprecation_attr)
702 << FixItHint::CreateInsertion(Loc, TextToInsert);
703 }
704}
705
706void Sema::resolveParamCommandIndexes(const FullComment *FC) {
707 if (!involvesFunctionType()) {
708 // We already warned that \\param commands are not attached to a function
709 // decl.
710 return;
711 }
712
713 SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
714
715 // Comment AST nodes that correspond to \c ParamVars for which we have
716 // found a \\param command or NULL if no documentation was found so far.
717 SmallVector<ParamCommandComment *, 8> ParamVarDocs;
718
719 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
720 ParamVarDocs.resize(ParamVars.size(), nullptr);
721
722 // First pass over all \\param commands: resolve all parameter names.
723 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
724 I != E; ++I) {
725 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
726 if (!PCC || !PCC->hasParamName())
727 continue;
728 StringRef ParamName = PCC->getParamNameAsWritten();
729
730 // Check that referenced parameter name is in the function decl.
731 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
732 ParamVars);
733 if (ResolvedParamIndex == ParamCommandComment::VarArgParamIndex) {
734 PCC->setIsVarArgParam();
735 continue;
736 }
737 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
738 UnresolvedParamCommands.push_back(PCC);
739 continue;
740 }
741 PCC->setParamIndex(ResolvedParamIndex);
742 if (ParamVarDocs[ResolvedParamIndex]) {
743 SourceRange ArgRange = PCC->getParamNameRange();
744 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
745 << ParamName << ArgRange;
746 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
747 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
748 << PrevCommand->getParamNameRange();
749 }
750 ParamVarDocs[ResolvedParamIndex] = PCC;
751 }
752
753 // Find parameter declarations that have no corresponding \\param.
754 SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
755 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
756 if (!ParamVarDocs[i])
757 OrphanedParamDecls.push_back(ParamVars[i]);
758 }
759
760 // Second pass over unresolved \\param commands: do typo correction.
761 // Suggest corrections from a set of parameter declarations that have no
762 // corresponding \\param.
763 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
764 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
765
766 SourceRange ArgRange = PCC->getParamNameRange();
767 StringRef ParamName = PCC->getParamNameAsWritten();
768 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
769 << ParamName << ArgRange;
770
771 // All parameters documented -- can't suggest a correction.
772 if (OrphanedParamDecls.size() == 0)
773 continue;
774
775 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
776 if (OrphanedParamDecls.size() == 1) {
777 // If one parameter is not documented then that parameter is the only
778 // possible suggestion.
779 CorrectedParamIndex = 0;
780 } else {
781 // Do typo correction.
782 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
783 OrphanedParamDecls);
784 }
785 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
786 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
787 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
788 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
789 << CorrectedII->getName()
790 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
791 }
792 }
793}
794
795bool Sema::involvesFunctionType() {
796 if (!ThisDeclInfo)
797 return false;
798 if (!ThisDeclInfo->IsFilled)
799 inspectThisDecl();
800 return ThisDeclInfo->involvesFunctionType();
801}
802
803bool Sema::isFunctionDecl() {
804 if (!ThisDeclInfo)
805 return false;
806 if (!ThisDeclInfo->IsFilled)
807 inspectThisDecl();
808 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
809}
810
811bool Sema::isAnyFunctionDecl() {
812 return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
813 isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);
814}
815
816bool Sema::isFunctionOrMethodVariadic() {
817 if (!ThisDeclInfo)
818 return false;
819 if (!ThisDeclInfo->IsFilled)
820 inspectThisDecl();
821 return ThisDeclInfo->IsVariadic;
822}
823
824bool Sema::isObjCMethodDecl() {
825 return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
826 isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);
827}
828
829bool Sema::isFunctionPointerVarDecl() {
830 if (!ThisDeclInfo)
831 return false;
832 if (!ThisDeclInfo->IsFilled)
833 inspectThisDecl();
834 if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {
835 if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {
836 QualType QT = VD->getType();
837 return QT->isFunctionPointerType();
838 }
839 }
840 return false;
841}
842
843bool Sema::isObjCPropertyDecl() {
844 if (!ThisDeclInfo)
845 return false;
846 if (!ThisDeclInfo->IsFilled)
847 inspectThisDecl();
848 return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;
849}
850
851bool Sema::isTemplateOrSpecialization() {
852 if (!ThisDeclInfo)
853 return false;
854 if (!ThisDeclInfo->IsFilled)
855 inspectThisDecl();
856 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
857}
858
859bool Sema::isRecordLikeDecl() {
860 if (!ThisDeclInfo)
861 return false;
862 if (!ThisDeclInfo->IsFilled)
863 inspectThisDecl();
864 return isUnionDecl() || isClassOrStructDecl() || isObjCInterfaceDecl() ||
865 isObjCProtocolDecl();
866}
867
868bool Sema::isUnionDecl() {
869 if (!ThisDeclInfo)
870 return false;
871 if (!ThisDeclInfo->IsFilled)
872 inspectThisDecl();
873 if (const RecordDecl *RD =
874 dyn_cast_or_null<RecordDecl>(ThisDeclInfo->CurrentDecl))
875 return RD->isUnion();
876 return false;
877}
878static bool isClassOrStructDeclImpl(const Decl *D) {
879 if (auto *record = dyn_cast_or_null<RecordDecl>(D))
880 return !record->isUnion();
881
882 return false;
883}
884
885bool Sema::isClassOrStructDecl() {
886 if (!ThisDeclInfo)
887 return false;
888 if (!ThisDeclInfo->IsFilled)
889 inspectThisDecl();
890
891 if (!ThisDeclInfo->CurrentDecl)
892 return false;
893
894 return isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl);
895}
896
897bool Sema::isClassOrStructOrTagTypedefDecl() {
898 if (!ThisDeclInfo)
899 return false;
900 if (!ThisDeclInfo->IsFilled)
901 inspectThisDecl();
902
903 if (!ThisDeclInfo->CurrentDecl)
904 return false;
905
906 if (isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl))
907 return true;
908
909 if (auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(ThisDeclInfo->CurrentDecl))
910 if (auto *D = ThisTypedefDecl->getUnderlyingType()->getAsRecordDecl())
912
913 return false;
914}
915
916bool Sema::isClassTemplateDecl() {
917 if (!ThisDeclInfo)
918 return false;
919 if (!ThisDeclInfo->IsFilled)
920 inspectThisDecl();
921 return ThisDeclInfo->CurrentDecl &&
922 (isa<ClassTemplateDecl>(ThisDeclInfo->CurrentDecl));
923}
924
925bool Sema::isFunctionTemplateDecl() {
926 if (!ThisDeclInfo)
927 return false;
928 if (!ThisDeclInfo->IsFilled)
929 inspectThisDecl();
930 return ThisDeclInfo->CurrentDecl &&
931 (isa<FunctionTemplateDecl>(ThisDeclInfo->CurrentDecl));
932}
933
934bool Sema::isObjCInterfaceDecl() {
935 if (!ThisDeclInfo)
936 return false;
937 if (!ThisDeclInfo->IsFilled)
938 inspectThisDecl();
939 return ThisDeclInfo->CurrentDecl &&
940 isa<ObjCInterfaceDecl>(ThisDeclInfo->CurrentDecl);
941}
942
943bool Sema::isObjCProtocolDecl() {
944 if (!ThisDeclInfo)
945 return false;
946 if (!ThisDeclInfo->IsFilled)
947 inspectThisDecl();
948 return ThisDeclInfo->CurrentDecl &&
949 isa<ObjCProtocolDecl>(ThisDeclInfo->CurrentDecl);
950}
951
952ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
953 if (!ThisDeclInfo->IsFilled)
954 inspectThisDecl();
955 return ThisDeclInfo->ParamVars;
956}
957
958void Sema::inspectThisDecl() {
959 ThisDeclInfo->fill();
960}
961
962unsigned Sema::resolveParmVarReference(StringRef Name,
963 ArrayRef<const ParmVarDecl *> ParamVars) {
964 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
965 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
966 if (II && II->getName() == Name)
967 return i;
968 }
969 if (Name == "..." && isFunctionOrMethodVariadic())
972}
973
974unsigned
975Sema::correctTypoInParmVarReference(StringRef Typo,
976 ArrayRef<const ParmVarDecl *> ParamVars) {
977 SimpleTypoCorrection STC(Typo);
978 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
979 const ParmVarDecl *Param = ParamVars[i];
980 if (!Param)
981 continue;
982
983 STC.add(Param->getIdentifier());
984 }
985
986 if (STC.hasCorrection())
987 return STC.getCorrectionIndex();
988
990}
991
992namespace {
993bool ResolveTParamReferenceHelper(
994 StringRef Name,
995 const TemplateParameterList *TemplateParameters,
996 SmallVectorImpl<unsigned> *Position) {
997 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
998 const NamedDecl *Param = TemplateParameters->getParam(i);
999 const IdentifierInfo *II = Param->getIdentifier();
1000 if (II && II->getName() == Name) {
1001 Position->push_back(i);
1002 return true;
1003 }
1004
1005 if (const TemplateTemplateParmDecl *TTP =
1006 dyn_cast<TemplateTemplateParmDecl>(Param)) {
1007 Position->push_back(i);
1008 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
1009 Position))
1010 return true;
1011 Position->pop_back();
1012 }
1013 }
1014 return false;
1015}
1016} // end anonymous namespace
1017
1018bool Sema::resolveTParamReference(
1019 StringRef Name,
1020 const TemplateParameterList *TemplateParameters,
1021 SmallVectorImpl<unsigned> *Position) {
1022 Position->clear();
1023 if (!TemplateParameters)
1024 return false;
1025
1026 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
1027}
1028
1029namespace {
1030void CorrectTypoInTParamReferenceHelper(
1031 const TemplateParameterList *TemplateParameters,
1032 SimpleTypoCorrection &STC) {
1033 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1034 const NamedDecl *Param = TemplateParameters->getParam(i);
1035 if (!Param)
1036 continue;
1037
1038 STC.add(Param->getIdentifier());
1039
1040 if (const TemplateTemplateParmDecl *TTP =
1041 dyn_cast<TemplateTemplateParmDecl>(Param))
1042 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), STC);
1043 }
1044}
1045} // end anonymous namespace
1046
1047StringRef Sema::correctTypoInTParamReference(
1048 StringRef Typo,
1049 const TemplateParameterList *TemplateParameters) {
1050 SimpleTypoCorrection STC(Typo);
1051 CorrectTypoInTParamReferenceHelper(TemplateParameters, STC);
1052
1053 if (auto CorrectedTParamReference = STC.getCorrection())
1054 return *CorrectedTParamReference;
1055
1056 return StringRef();
1057}
1058
1059InlineCommandRenderKind Sema::getInlineCommandRenderKind(StringRef Name) const {
1060 assert(Traits.getCommandInfo(Name)->IsInlineCommand);
1061
1062 return llvm::StringSwitch<InlineCommandRenderKind>(Name)
1064 .Cases("c", "p", InlineCommandRenderKind::Monospaced)
1065 .Cases("a", "e", "em", InlineCommandRenderKind::Emphasized)
1066 .Case("anchor", InlineCommandRenderKind::Anchor)
1068}
1069
1070} // end namespace comments
1071} // end namespace clang
const Decl * D
Expr * E
Defines the C++ template declaration subclasses.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::Preprocessor interface.
SourceLocation Loc
Definition: SemaObjC.cpp:754
Defines the SourceManager interface.
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
Kind getKind() const
Definition: DeclBase.h:442
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:231
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
Definition: Diagnostic.h:139
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition: Diagnostic.h:102
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
This represents a decl that may have a name.
Definition: Decl.h:273
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:294
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:145
IdentifierInfo * getIdentifierInfo(StringRef Name) const
Return information about the specified preprocessor identifier token.
StringRef getLastMacroWithSpelling(SourceLocation Loc, ArrayRef< TokenValue > Tokens) const
Return the name of the macro defined before Loc that has spelling Tokens.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: TypeBase.h:1004
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Stores a list of template parameters for a TemplateDecl and its derived classes.
Definition: DeclTemplate.h:74
NamedDecl * getParam(unsigned Idx)
Definition: DeclTemplate.h:146
bool isVoidType() const
Definition: TypeBase.h:8936
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition: Comment.h:625
SourceRange getCommandNameRange(const CommandTraits &Traits) const
Definition: Comment.h:681
void setParagraph(ParagraphComment *PC)
Definition: Comment.h:716
StringRef getCommandName(const CommandTraits &Traits) const
Definition: Comment.h:673
void setArgs(ArrayRef< Argument > A)
Definition: Comment.h:699
SourceRange getArgRange(unsigned Idx) const
Definition: Comment.h:695
ParagraphComment * getParagraph() const LLVM_READONLY
Definition: Comment.h:708
CommandMarkerKind getCommandMarker() const LLVM_READONLY
Definition: Comment.h:723
This class provides information about commands that can be used in comments.
const CommandInfo * getCommandInfo(StringRef Name) const
const CommandInfo * registerUnknownCommand(StringRef CommandName)
Any part of the comment.
Definition: Comment.h:66
Comment *const * child_iterator
Definition: Comment.h:257
child_iterator child_begin() const
Definition: Comment.cpp:83
SourceLocation getLocation() const LLVM_READONLY
Definition: Comment.h:255
SourceRange getSourceRange() const LLVM_READONLY
Definition: Comment.h:249
A full comment attached to a declaration, contains block content.
Definition: Comment.h:1104
An opening HTML tag with attributes.
Definition: Comment.h:454
StringRef getTagName() const LLVM_READONLY
Definition: Comment.h:436
A command with word-like arguments that is considered inline content.
Definition: Comment.h:341
Inline content (contained within a block).
Definition: Comment.h:271
A single paragraph that contains inline content.
Definition: Comment.h:576
Doxygen \param command.
Definition: Comment.h:732
bool isDirectionExplicit() const LLVM_READONLY
Definition: Comment.h:764
static const char * getDirectionAsString(ParamCommandPassDirection D)
Definition: Comment.cpp:189
void setDirection(ParamCommandPassDirection Direction, bool Explicit)
Definition: Comment.h:768
FullComment * actOnFullComment(ArrayRef< BlockContentComment * > Blocks)
void actOnParamCommandDirectionArg(ParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
InlineCommandComment * actOnInlineCommand(SourceLocation CommandLocBegin, SourceLocation CommandLocEnd, unsigned CommandID, CommandMarkerKind CommandMarker, ArrayRef< Comment::Argument > Args)
TParamCommandComment * actOnTParamCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
void actOnTParamCommandFinish(TParamCommandComment *Command, ParagraphComment *Paragraph)
void actOnBlockCommandFinish(BlockCommandComment *Command, ParagraphComment *Paragraph)
Definition: CommentSema.cpp:67
TextComment * actOnText(SourceLocation LocBegin, SourceLocation LocEnd, StringRef Text)
void actOnBlockCommandArgs(BlockCommandComment *Command, ArrayRef< BlockCommandComment::Argument > Args)
Definition: CommentSema.cpp:62
BlockCommandComment * actOnBlockCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
Definition: CommentSema.cpp:50
void actOnParamCommandParamNameArg(ParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
HTMLEndTagComment * actOnHTMLEndTag(SourceLocation LocBegin, SourceLocation LocEnd, StringRef TagName)
VerbatimLineComment * actOnVerbatimLine(SourceLocation LocBegin, unsigned CommandID, SourceLocation TextBegin, StringRef Text)
void actOnVerbatimBlockFinish(VerbatimBlockComment *Block, SourceLocation CloseNameLocBegin, StringRef CloseName, ArrayRef< VerbatimBlockLineComment * > Lines)
VerbatimBlockLineComment * actOnVerbatimBlockLine(SourceLocation Loc, StringRef Text)
void actOnParamCommandFinish(ParamCommandComment *Command, ParagraphComment *Paragraph)
ArrayRef< T > copyArray(ArrayRef< T > Source)
Returns a copy of array, owned by Sema's allocator.
Definition: CommentSema.h:80
HTMLStartTagComment * actOnHTMLStartTagStart(SourceLocation LocBegin, StringRef TagName)
void actOnHTMLStartTagFinish(HTMLStartTagComment *Tag, ArrayRef< HTMLStartTagComment::Attribute > Attrs, SourceLocation GreaterLoc, bool IsSelfClosing)
ParamCommandComment * actOnParamCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
Definition: CommentSema.cpp:80
VerbatimBlockComment * actOnVerbatimBlockStart(SourceLocation Loc, unsigned CommandID)
void actOnTParamCommandParamNameArg(TParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
void setDecl(const Decl *D)
Definition: CommentSema.cpp:36
InlineContentComment * actOnUnknownCommand(SourceLocation LocBegin, SourceLocation LocEnd, StringRef CommandName)
ParagraphComment * actOnParagraphComment(ArrayRef< InlineContentComment * > Content)
Definition: CommentSema.cpp:45
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:814
void setPosition(ArrayRef< unsigned > NewPosition)
Definition: Comment.h:868
SourceRange getParamNameRange() const
Definition: Comment.h:850
A verbatim block command (e.
Definition: Comment.h:900
A line of text contained in a verbatim block.
Definition: Comment.h:875
A verbatim line command.
Definition: Comment.h:951
ParamCommandPassDirection
Definition: Comment.h:729
static bool isClassOrStructDeclImpl(const Decl *D)
InlineCommandRenderKind
The most appropriate rendering mode for this command, chosen on command semantics in Doxygen.
Definition: Comment.h:332
CommandMarkerKind
Describes the syntax that was used in a documentation command.
Definition: Comment.h:39
static ParamCommandPassDirection getParamPassDirection(StringRef Arg)
Turn a string into the corresponding PassDirection or -1 if it's not valid.
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
Definition: ModuleMapFile.h:36
The JSON file list parser is used to communicate input to InstallAPI.
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition: CharInfo.h:108
Information about a single command.
unsigned IsFunctionDeclarationCommand
True if verbatim-like line command is a function declaration.
unsigned IsInlineCommand
True if this command is a inline command (of any kind).
unsigned IsDeprecatedCommand
True if this command is \deprecated or an alias.
unsigned IsReturnsCommand
True if this command is \returns or an alias.
unsigned IsEmptyParagraphAllowed
True if we don't want to warn about this command being passed an empty paragraph.
Information about the declaration, useful to clients of FullComment.
Definition: Comment.h:983
@ FunctionKind
Something that we consider a "function":
Definition: Comment.h:1025
@ VariableKind
Something that we consider a "variable":
Definition: Comment.h:1037
unsigned IsObjCMethod
Is CommentDecl an ObjCMethodDecl.
Definition: Comment.h:1072
const TemplateParameterList * TemplateParameters
Template parameters that can be referenced by \tparam if CommentDecl is a template (IsTemplateDecl or...
Definition: Comment.h:1009
ArrayRef< const ParmVarDecl * > ParamVars
Parameters that can be referenced by \param if CommentDecl is something that we consider a "function"...
Definition: Comment.h:1000
DeclKind getKind() const LLVM_READONLY
Definition: Comment.h:1092
TemplateDeclKind getTemplateKind() const LLVM_READONLY
Definition: Comment.h:1096
bool involvesFunctionType() const
Definition: Comment.h:1100
unsigned IsFilled
If false, only CommentDecl is valid.
Definition: Comment.h:1060
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition: Comment.h:986
unsigned IsVariadic
Is CommentDecl something we consider a "function" that's variadic.
Definition: Comment.h:1088
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition: Comment.h:996
QualType ReturnType
Function return type if CommentDecl is something that we consider a "function".
Definition: Comment.h:1004