clang 24.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
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
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 std::optional<unsigned> DiagSelect;
104 switch (Comment->getCommandID()) {
105 case CommandTraits::KCI_function:
106 if (!isAnyFunctionDecl() && !isFunctionTemplateDecl())
107 DiagSelect = diag::CallableKind::Function;
108 break;
109 case CommandTraits::KCI_functiongroup:
110 if (!isAnyFunctionDecl() && !isFunctionTemplateDecl())
111 DiagSelect = diag::CallableKind::FunctionGroup;
112 break;
113 case CommandTraits::KCI_method:
114 if (!isObjCMethodDecl())
115 DiagSelect = diag::CallableKind::Method;
116 break;
117 case CommandTraits::KCI_methodgroup:
118 if (!isObjCMethodDecl())
119 DiagSelect = diag::CallableKind::MethodGroup;
120 break;
121 case CommandTraits::KCI_callback:
122 if (!isFunctionPointerVarDecl())
123 DiagSelect = diag::CallableKind::Callback;
124 break;
125 }
126 if (DiagSelect)
127 Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)
128 << Comment->getCommandMarker() << (*DiagSelect) << (*DiagSelect)
129 << Comment->getSourceRange();
130}
131
132void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {
133 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
134 if (!Info->IsRecordLikeDeclarationCommand)
135 return;
136 std::optional<unsigned> DiagSelect;
137 switch (Comment->getCommandID()) {
138 case CommandTraits::KCI_class:
139 if (!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl())
140 DiagSelect = diag::DeclContainerKind::Class;
141
142 // Allow @class command on @interface declarations.
143 // FIXME. Currently, \class and @class are indistinguishable. So,
144 // \class is also allowed on an @interface declaration
145 if (DiagSelect && Comment->getCommandMarker() && isObjCInterfaceDecl())
146 DiagSelect = std::nullopt;
147 break;
148 case CommandTraits::KCI_interface:
149 if (!isObjCInterfaceDecl())
150 DiagSelect = diag::DeclContainerKind::Interface;
151 break;
152 case CommandTraits::KCI_protocol:
153 if (!isObjCProtocolDecl())
154 DiagSelect = diag::DeclContainerKind::Protocol;
155 break;
156 case CommandTraits::KCI_struct:
157 if (!isClassOrStructOrTagTypedefDecl())
158 DiagSelect = diag::DeclContainerKind::Struct;
159 break;
160 case CommandTraits::KCI_union:
161 if (!isUnionDecl())
162 DiagSelect = diag::DeclContainerKind::Union;
163 break;
164 default:
165 DiagSelect = std::nullopt;
166 break;
167 }
168 if (DiagSelect)
169 Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
170 << Comment->getCommandMarker() << (*DiagSelect) << (*DiagSelect)
171 << Comment->getSourceRange();
172}
173
174void Sema::checkContainerDecl(const BlockCommandComment *Comment) {
175 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
176 if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
177 return;
178 std::optional<unsigned> DiagSelect;
179 switch (Comment->getCommandID()) {
180 case CommandTraits::KCI_classdesign:
181 DiagSelect = diag::DocCommandKind::ClassDesign;
182 break;
183 case CommandTraits::KCI_coclass:
184 DiagSelect = diag::DocCommandKind::CoClass;
185 break;
186 case CommandTraits::KCI_dependency:
187 DiagSelect = diag::DocCommandKind::Dependency;
188 break;
189 case CommandTraits::KCI_helper:
190 DiagSelect = diag::DocCommandKind::Helper;
191 break;
192 case CommandTraits::KCI_helperclass:
193 DiagSelect = diag::DocCommandKind::HelperClass;
194 break;
195 case CommandTraits::KCI_helps:
196 DiagSelect = diag::DocCommandKind::Helps;
197 break;
198 case CommandTraits::KCI_instancesize:
199 DiagSelect = diag::DocCommandKind::InstanceSize;
200 break;
201 case CommandTraits::KCI_ownership:
202 DiagSelect = diag::DocCommandKind::Ownership;
203 break;
204 case CommandTraits::KCI_performance:
205 DiagSelect = diag::DocCommandKind::Performance;
206 break;
207 case CommandTraits::KCI_security:
208 DiagSelect = diag::DocCommandKind::Security;
209 break;
210 case CommandTraits::KCI_superclass:
211 DiagSelect = diag::DocCommandKind::Superclass;
212 break;
213 default:
214 DiagSelect = std::nullopt;
215 break;
216 }
217 if (DiagSelect)
218 Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
219 << Comment->getCommandMarker() << (*DiagSelect)
220 << Comment->getSourceRange();
221}
222
223/// Turn a string into the corresponding PassDirection or -1 if it's not
224/// valid.
226 return llvm::StringSwitch<ParamCommandPassDirection>(Arg)
227 .Case("[in]", ParamCommandPassDirection::In)
228 .Case("[out]", ParamCommandPassDirection::Out)
229 .Cases({"[in,out]", "[out,in]"}, ParamCommandPassDirection::InOut)
230 .Default(static_cast<ParamCommandPassDirection>(-1));
231}
232
234 SourceLocation ArgLocBegin,
235 SourceLocation ArgLocEnd,
236 StringRef Arg) {
237 std::string ArgLower = Arg.lower();
239
240 if (Direction == static_cast<ParamCommandPassDirection>(-1)) {
241 // Try again with whitespace removed.
242 llvm::erase_if(ArgLower, clang::isWhitespace);
243 Direction = getParamPassDirection(ArgLower);
244
245 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
246 if (Direction != static_cast<ParamCommandPassDirection>(-1)) {
247 const char *FixedName =
249 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
250 << ArgRange << FixItHint::CreateReplacement(ArgRange, FixedName);
251 } else {
252 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange;
253 Direction = ParamCommandPassDirection::In; // Sane fall back.
254 }
255 }
256 Command->setDirection(Direction,
257 /*Explicit=*/true);
258}
259
261 SourceLocation ArgLocBegin,
262 SourceLocation ArgLocEnd,
263 StringRef Arg) {
264 // Parser will not feed us more arguments than needed.
265 assert(Command->getNumArgs() == 0);
266
267 if (!Command->isDirectionExplicit()) {
268 // User didn't provide a direction argument.
270 /* Explicit = */ false);
271 }
272 auto *A = new (Allocator)
273 Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};
274 Command->setArgs(ArrayRef(A, 1));
275}
276
278 ParagraphComment *Paragraph) {
279 Command->setParagraph(Paragraph);
280 checkBlockCommandEmptyParagraph(Command);
281}
282
284 SourceLocation LocBegin,
285 SourceLocation LocEnd,
286 unsigned CommandID,
287 CommandMarkerKind CommandMarker) {
288 TParamCommandComment *Command =
289 new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID,
290 CommandMarker);
291
292 if (isExplicitFunctionTemplateInstantiation()) {
293 // Do not warn on explicit instantiations, since the documentation
294 // comments are on the primary template.
295 return Command;
296 }
297
298 if (!isTemplateOrSpecialization())
299 Diag(Command->getLocation(),
300 diag::warn_doc_tparam_not_attached_to_a_template_decl)
301 << CommandMarker
302 << Command->getCommandNameRange(Traits);
303
304 return Command;
305}
306
308 SourceLocation ArgLocBegin,
309 SourceLocation ArgLocEnd,
310 StringRef Arg) {
311 // Parser will not feed us more arguments than needed.
312 assert(Command->getNumArgs() == 0);
313
314 auto *A = new (Allocator)
315 Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};
316 Command->setArgs(ArrayRef(A, 1));
317
318 if (!isTemplateOrSpecialization()) {
319 // We already warned that this \\tparam is not attached to a template decl.
320 return;
321 }
322
323 const TemplateParameterList *TemplateParameters =
324 ThisDeclInfo->TemplateParameters;
326 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
327 Command->setPosition(copyArray(ArrayRef(Position)));
328 TParamCommandComment *&PrevCommand = TemplateParameterDocs[Arg];
329 if (PrevCommand) {
330 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
331 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
332 << Arg << ArgRange;
333 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
334 << PrevCommand->getParamNameRange();
335 }
336 PrevCommand = Command;
337 return;
338 }
339
340 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
341 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
342 << Arg << ArgRange;
343
344 if (!TemplateParameters || TemplateParameters->size() == 0)
345 return;
346
347 StringRef CorrectedName;
348 if (TemplateParameters->size() == 1) {
349 const NamedDecl *Param = TemplateParameters->getParam(0);
350 const IdentifierInfo *II = Param->getIdentifier();
351 if (II)
352 CorrectedName = II->getName();
353 } else {
354 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
355 }
356
357 if (!CorrectedName.empty()) {
358 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
359 << CorrectedName
360 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
361 }
362}
363
365 ParagraphComment *Paragraph) {
366 Command->setParagraph(Paragraph);
367 checkBlockCommandEmptyParagraph(Command);
368}
369
372 SourceLocation CommandLocEnd, unsigned CommandID,
373 CommandMarkerKind CommandMarker,
375 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
376
377 return new (Allocator) InlineCommandComment(
378 CommandLocBegin, CommandLocEnd, CommandID,
379 getInlineCommandRenderKind(CommandName), CommandMarker, Args);
380}
381
384 StringRef CommandName,
385 CommandMarkerKind CommandMarker) {
386 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
387 return actOnUnknownCommand(LocBegin, LocEnd, CommandID, CommandMarker);
388}
389
392 unsigned CommandID, CommandMarkerKind CommandMarker) {
394 return new (Allocator) InlineCommandComment(LocBegin, LocEnd, CommandID,
396 CommandMarker, Args);
397}
398
400 SourceLocation LocEnd,
401 StringRef Text) {
402 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
403}
404
406 unsigned CommandID) {
407 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
408 return new (Allocator) VerbatimBlockComment(
409 Loc,
410 Loc.getLocWithOffset(1 + CommandName.size()),
411 CommandID);
412}
413
415 StringRef Text) {
416 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
417}
418
421 SourceLocation CloseNameLocBegin,
422 StringRef CloseName,
424 Block->setCloseName(CloseName, CloseNameLocBegin);
425 Block->setLines(Lines);
426}
427
429 unsigned CommandID,
430 SourceLocation TextBegin,
431 StringRef Text) {
432 VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
433 LocBegin,
434 TextBegin.getLocWithOffset(Text.size()),
435 CommandID,
436 TextBegin,
437 Text);
438 checkFunctionDeclVerbatimLine(VL);
439 checkContainerDeclVerbatimLine(VL);
440 return VL;
441}
442
444 StringRef TagName) {
445 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
446}
447
451 SourceLocation GreaterLoc,
452 bool IsSelfClosing) {
453 Tag->setAttrs(Attrs);
454 Tag->setGreaterLoc(GreaterLoc);
455 if (IsSelfClosing)
456 Tag->setSelfClosing();
457 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
458 HTMLOpenTags.push_back(Tag);
459}
460
462 SourceLocation LocEnd,
463 StringRef TagName) {
464 HTMLEndTagComment *HET =
465 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
466 if (isHTMLEndTagForbidden(TagName)) {
467 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
468 << TagName << HET->getSourceRange();
469 HET->setIsMalformed();
470 return HET;
471 }
472
473 bool FoundOpen = false;
475 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
476 I != E; ++I) {
477 if ((*I)->getTagName() == TagName) {
478 FoundOpen = true;
479 break;
480 }
481 }
482 if (!FoundOpen) {
483 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
484 << HET->getSourceRange();
485 HET->setIsMalformed();
486 return HET;
487 }
488
489 while (!HTMLOpenTags.empty()) {
490 HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
491 StringRef LastNotClosedTagName = HST->getTagName();
492 if (LastNotClosedTagName == TagName) {
493 // If the start tag is malformed, end tag is malformed as well.
494 if (HST->isMalformed())
495 HET->setIsMalformed();
496 break;
497 }
498
499 if (isHTMLEndTagOptional(LastNotClosedTagName))
500 continue;
501
502 bool OpenLineInvalid;
503 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
504 HST->getLocation(),
505 &OpenLineInvalid);
506 bool CloseLineInvalid;
507 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
508 HET->getLocation(),
509 &CloseLineInvalid);
510
511 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
512 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
513 << HST->getTagName() << HET->getTagName()
514 << HST->getSourceRange() << HET->getSourceRange();
515 HST->setIsMalformed();
516 } else {
517 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
518 << HST->getTagName() << HET->getTagName()
519 << HST->getSourceRange();
520 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
521 << HET->getSourceRange();
522 HST->setIsMalformed();
523 }
524 }
525
526 return HET;
527}
528
531 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
532 resolveParamCommandIndexes(FC);
533
534 // Complain about HTML tags that are not closed.
535 while (!HTMLOpenTags.empty()) {
536 HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
537 if (isHTMLEndTagOptional(HST->getTagName()))
538 continue;
539
540 Diag(HST->getLocation(), diag::warn_doc_html_missing_end_tag)
541 << HST->getTagName() << HST->getSourceRange();
542 HST->setIsMalformed();
543 }
544
545 return FC;
546}
547
548void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
549 if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)
550 return;
551
552 ParagraphComment *Paragraph = Command->getParagraph();
553 if (Paragraph->isWhitespace()) {
554 SourceLocation DiagLoc;
555 if (Command->getNumArgs() > 0)
556 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
557 if (!DiagLoc.isValid())
558 DiagLoc = Command->getCommandNameRange(Traits).getEnd();
559 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
560 << Command->getCommandMarker()
561 << Command->getCommandName(Traits)
562 << Command->getSourceRange();
563 }
564}
565
566void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
567 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
568 return;
569
570 assert(ThisDeclInfo && "should not call this check on a bare comment");
571
572 // We allow the return command for all @properties because it can be used
573 // to document the value that the property getter returns.
574 if (isObjCPropertyDecl())
575 return;
576 if (involvesFunctionType()) {
577 assert(!ThisDeclInfo->ReturnType.isNull() &&
578 "should have a valid return type");
579 if (ThisDeclInfo->ReturnType->isVoidType()) {
580 unsigned DiagKind;
581 switch (ThisDeclInfo->CommentDecl->getKind()) {
582 default:
583 if (ThisDeclInfo->IsObjCMethod)
584 DiagKind = 3;
585 else
586 DiagKind = 0;
587 break;
588 case Decl::CXXConstructor:
589 DiagKind = 1;
590 break;
591 case Decl::CXXDestructor:
592 DiagKind = 2;
593 break;
594 }
595 Diag(Command->getLocation(),
596 diag::warn_doc_returns_attached_to_a_void_function)
597 << Command->getCommandMarker()
598 << Command->getCommandName(Traits)
599 << DiagKind
600 << Command->getSourceRange();
601 }
602 return;
603 }
604
605 Diag(Command->getLocation(),
606 diag::warn_doc_returns_not_attached_to_a_function_decl)
607 << Command->getCommandMarker()
608 << Command->getCommandName(Traits)
609 << Command->getSourceRange();
610}
611
612void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
613 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
614 const BlockCommandComment *PrevCommand = nullptr;
615 if (Info->IsBriefCommand) {
616 if (!BriefCommand) {
617 BriefCommand = Command;
618 return;
619 }
620 PrevCommand = BriefCommand;
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
650void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
651 if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
652 return;
653
654 assert(ThisDeclInfo && "should not call this check on a bare comment");
655
656 const Decl *D = ThisDeclInfo->CommentDecl;
657 if (!D)
658 return;
659
660 if (D->hasAttr<DeprecatedAttr>() ||
661 D->hasAttr<AvailabilityAttr>() ||
662 D->hasAttr<UnavailableAttr>())
663 return;
664
665 Diag(Command->getLocation(), diag::warn_doc_deprecated_not_sync)
666 << Command->getSourceRange() << Command->getCommandMarker();
667
668 // Try to emit a fixit with a deprecation attribute.
669 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
670 // Don't emit a Fix-It for non-member function definitions. GCC does not
671 // accept attributes on them.
672 const DeclContext *Ctx = FD->getDeclContext();
673 if ((!Ctx || !Ctx->isRecord()) &&
674 FD->doesThisDeclarationHaveABody())
675 return;
676
677 const LangOptions &LO = FD->getLangOpts();
678 const bool DoubleSquareBracket = LO.CPlusPlus14 || LO.C23;
679 StringRef AttributeSpelling =
680 DoubleSquareBracket ? "[[deprecated]]" : "__attribute__((deprecated))";
681 if (PP) {
682 // Try to find a replacement macro:
683 // - In C23/C++14 we prefer [[deprecated]].
684 // - If not found or an older C/C++ look for __attribute__((deprecated)).
685 StringRef MacroName;
686 if (DoubleSquareBracket) {
687 TokenValue Tokens[] = {tok::l_square, tok::l_square,
688 PP->getIdentifierInfo("deprecated"),
689 tok::r_square, tok::r_square};
690 MacroName = PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);
691 if (!MacroName.empty())
692 AttributeSpelling = MacroName;
693 }
694
695 if (MacroName.empty()) {
696 TokenValue Tokens[] = {
697 tok::kw___attribute, tok::l_paren,
698 tok::l_paren, PP->getIdentifierInfo("deprecated"),
699 tok::r_paren, tok::r_paren};
700 StringRef MacroName =
701 PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);
702 if (!MacroName.empty())
703 AttributeSpelling = MacroName;
704 }
705 }
706
707 SmallString<64> TextToInsert = AttributeSpelling;
708 TextToInsert += " ";
709 SourceLocation Loc = FD->getSourceRange().getBegin();
710 Diag(Loc, diag::note_add_deprecation_attr)
711 << FixItHint::CreateInsertion(Loc, TextToInsert);
712 }
713}
714
715void Sema::resolveParamCommandIndexes(const FullComment *FC) {
716 if (!involvesFunctionType()) {
717 // We already warned that \\param commands are not attached to a function
718 // decl.
719 return;
720 }
721
722 SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
723
724 // Comment AST nodes that correspond to \c ParamVars for which we have
725 // found a \\param command or NULL if no documentation was found so far.
726 SmallVector<ParamCommandComment *, 8> ParamVarDocs;
727
728 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
729 ParamVarDocs.resize(ParamVars.size(), nullptr);
730
731 // First pass over all \\param commands: resolve all parameter names.
732 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
733 I != E; ++I) {
734 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
735 if (!PCC || !PCC->hasParamName())
736 continue;
737 StringRef ParamName = PCC->getParamNameAsWritten();
738
739 // Check that referenced parameter name is in the function decl.
740 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
741 ParamVars);
742 if (ResolvedParamIndex == ParamCommandComment::VarArgParamIndex) {
743 PCC->setIsVarArgParam();
744 continue;
745 }
746 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
747 UnresolvedParamCommands.push_back(PCC);
748 continue;
749 }
750 PCC->setParamIndex(ResolvedParamIndex);
751 if (ParamVarDocs[ResolvedParamIndex]) {
752 SourceRange ArgRange = PCC->getParamNameRange();
753 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
754 << ParamName << ArgRange;
755 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
756 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
757 << PrevCommand->getParamNameRange();
758 }
759 ParamVarDocs[ResolvedParamIndex] = PCC;
760 }
761
762 // Find parameter declarations that have no corresponding \\param.
763 SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
764 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
765 if (!ParamVarDocs[i])
766 OrphanedParamDecls.push_back(ParamVars[i]);
767 }
768
769 // Second pass over unresolved \\param commands: do typo correction.
770 // Suggest corrections from a set of parameter declarations that have no
771 // corresponding \\param.
772 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
773 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
774
775 SourceRange ArgRange = PCC->getParamNameRange();
776 StringRef ParamName = PCC->getParamNameAsWritten();
777 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
778 << ParamName << ArgRange;
779
780 // All parameters documented -- can't suggest a correction.
781 if (OrphanedParamDecls.size() == 0)
782 continue;
783
784 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
785 if (OrphanedParamDecls.size() == 1) {
786 // If one parameter is not documented then that parameter is the only
787 // possible suggestion.
788 CorrectedParamIndex = 0;
789 } else {
790 // Do typo correction.
791 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
792 OrphanedParamDecls);
793 }
794 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
795 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
796 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
797 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
798 << CorrectedII->getName()
799 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
800 }
801 }
802}
803
804bool Sema::involvesFunctionType() {
805 if (!ThisDeclInfo)
806 return false;
807 if (!ThisDeclInfo->IsFilled)
808 inspectThisDecl();
809 return ThisDeclInfo->involvesFunctionType();
810}
811
812bool Sema::isFunctionDecl() {
813 if (!ThisDeclInfo)
814 return false;
815 if (!ThisDeclInfo->IsFilled)
816 inspectThisDecl();
817 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
818}
819
820bool Sema::isAnyFunctionDecl() {
821 return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
822 isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);
823}
824
825bool Sema::isFunctionOrMethodVariadic() {
826 if (!ThisDeclInfo)
827 return false;
828 if (!ThisDeclInfo->IsFilled)
829 inspectThisDecl();
830 return ThisDeclInfo->IsVariadic;
831}
832
833bool Sema::isObjCMethodDecl() {
834 return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
835 isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);
836}
837
838bool Sema::isFunctionPointerVarDecl() {
839 if (!ThisDeclInfo)
840 return false;
841 if (!ThisDeclInfo->IsFilled)
842 inspectThisDecl();
843 if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {
844 if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {
845 QualType QT = VD->getType();
846 return QT->isFunctionPointerType();
847 }
848 }
849 return false;
850}
851
852bool Sema::isObjCPropertyDecl() {
853 if (!ThisDeclInfo)
854 return false;
855 if (!ThisDeclInfo->IsFilled)
856 inspectThisDecl();
857 return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;
858}
859
860bool Sema::isTemplateOrSpecialization() {
861 if (!ThisDeclInfo)
862 return false;
863 if (!ThisDeclInfo->IsFilled)
864 inspectThisDecl();
865 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
866}
867
868bool Sema::isExplicitFunctionTemplateInstantiation() {
869 if (!ThisDeclInfo)
870 return false;
871 if (!ThisDeclInfo->IsFilled)
872 inspectThisDecl();
873 if (const auto *FD = dyn_cast<FunctionDecl>(ThisDeclInfo->CurrentDecl)) {
874 TemplateSpecializationKind TSK = FD->getTemplateSpecializationKind();
875 return (TSK == TSK_ExplicitInstantiationDeclaration) ||
877 }
878 return false;
879}
880
881bool Sema::isRecordLikeDecl() {
882 if (!ThisDeclInfo)
883 return false;
884 if (!ThisDeclInfo->IsFilled)
885 inspectThisDecl();
886 return isUnionDecl() || isClassOrStructDecl() || isObjCInterfaceDecl() ||
887 isObjCProtocolDecl();
888}
889
890bool Sema::isUnionDecl() {
891 if (!ThisDeclInfo)
892 return false;
893 if (!ThisDeclInfo->IsFilled)
894 inspectThisDecl();
895 if (const RecordDecl *RD =
896 dyn_cast_or_null<RecordDecl>(ThisDeclInfo->CurrentDecl))
897 return RD->isUnion();
898 return false;
899}
900static bool isClassOrStructDeclImpl(const Decl *D) {
901 if (auto *record = dyn_cast_or_null<RecordDecl>(D))
902 return !record->isUnion();
903
904 return false;
905}
906
907bool Sema::isClassOrStructDecl() {
908 if (!ThisDeclInfo)
909 return false;
910 if (!ThisDeclInfo->IsFilled)
911 inspectThisDecl();
912
913 if (!ThisDeclInfo->CurrentDecl)
914 return false;
915
916 return isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl);
917}
918
919bool Sema::isClassOrStructOrTagTypedefDecl() {
920 if (!ThisDeclInfo)
921 return false;
922 if (!ThisDeclInfo->IsFilled)
923 inspectThisDecl();
924
925 if (!ThisDeclInfo->CurrentDecl)
926 return false;
927
928 if (isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl))
929 return true;
930
931 if (auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(ThisDeclInfo->CurrentDecl))
932 if (auto *D = ThisTypedefDecl->getUnderlyingType()->getAsRecordDecl())
933 return isClassOrStructDeclImpl(D);
934
935 return false;
936}
937
938bool Sema::isClassTemplateDecl() {
939 if (!ThisDeclInfo)
940 return false;
941 if (!ThisDeclInfo->IsFilled)
942 inspectThisDecl();
943 return ThisDeclInfo->CurrentDecl &&
944 (isa<ClassTemplateDecl>(ThisDeclInfo->CurrentDecl));
945}
946
947bool Sema::isFunctionTemplateDecl() {
948 if (!ThisDeclInfo)
949 return false;
950 if (!ThisDeclInfo->IsFilled)
951 inspectThisDecl();
952 return ThisDeclInfo->CurrentDecl &&
953 (isa<FunctionTemplateDecl>(ThisDeclInfo->CurrentDecl));
954}
955
956bool Sema::isObjCInterfaceDecl() {
957 if (!ThisDeclInfo)
958 return false;
959 if (!ThisDeclInfo->IsFilled)
960 inspectThisDecl();
961 return ThisDeclInfo->CurrentDecl &&
962 isa<ObjCInterfaceDecl>(ThisDeclInfo->CurrentDecl);
963}
964
965bool Sema::isObjCProtocolDecl() {
966 if (!ThisDeclInfo)
967 return false;
968 if (!ThisDeclInfo->IsFilled)
969 inspectThisDecl();
970 return ThisDeclInfo->CurrentDecl &&
971 isa<ObjCProtocolDecl>(ThisDeclInfo->CurrentDecl);
972}
973
974ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
975 if (!ThisDeclInfo->IsFilled)
976 inspectThisDecl();
977 return ThisDeclInfo->ParamVars;
978}
979
980void Sema::inspectThisDecl() {
981 ThisDeclInfo->fill();
982}
983
984unsigned Sema::resolveParmVarReference(StringRef Name,
985 ArrayRef<const ParmVarDecl *> ParamVars) {
986 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
987 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
988 if (II && II->getName() == Name)
989 return i;
990 }
991 if (Name == "..." && isFunctionOrMethodVariadic())
994}
995
996unsigned
997Sema::correctTypoInParmVarReference(StringRef Typo,
998 ArrayRef<const ParmVarDecl *> ParamVars) {
999 SimpleTypoCorrection STC(Typo);
1000 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
1001 const ParmVarDecl *Param = ParamVars[i];
1002 if (!Param)
1003 continue;
1004
1005 STC.add(Param->getIdentifier());
1006 }
1007
1008 if (STC.hasCorrection())
1009 return STC.getCorrectionIndex();
1010
1012}
1013
1014namespace {
1015bool ResolveTParamReferenceHelper(
1016 StringRef Name,
1017 const TemplateParameterList *TemplateParameters,
1018 SmallVectorImpl<unsigned> *Position) {
1019 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1020 const NamedDecl *Param = TemplateParameters->getParam(i);
1021 const IdentifierInfo *II = Param->getIdentifier();
1022 if (II && II->getName() == Name) {
1023 Position->push_back(i);
1024 return true;
1025 }
1026
1027 if (const TemplateTemplateParmDecl *TTP =
1028 dyn_cast<TemplateTemplateParmDecl>(Param)) {
1029 Position->push_back(i);
1030 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
1031 Position))
1032 return true;
1033 Position->pop_back();
1034 }
1035 }
1036 return false;
1037}
1038} // end anonymous namespace
1039
1040bool Sema::resolveTParamReference(
1041 StringRef Name,
1042 const TemplateParameterList *TemplateParameters,
1043 SmallVectorImpl<unsigned> *Position) {
1044 Position->clear();
1045 if (!TemplateParameters)
1046 return false;
1047
1048 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
1049}
1050
1051namespace {
1052void CorrectTypoInTParamReferenceHelper(
1053 const TemplateParameterList *TemplateParameters,
1054 SimpleTypoCorrection &STC) {
1055 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1056 const NamedDecl *Param = TemplateParameters->getParam(i);
1057 if (!Param)
1058 continue;
1059
1060 STC.add(Param->getIdentifier());
1061
1062 if (const TemplateTemplateParmDecl *TTP =
1063 dyn_cast<TemplateTemplateParmDecl>(Param))
1064 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), STC);
1065 }
1066}
1067} // end anonymous namespace
1068
1069StringRef Sema::correctTypoInTParamReference(
1070 StringRef Typo,
1071 const TemplateParameterList *TemplateParameters) {
1072 SimpleTypoCorrection STC(Typo);
1073 CorrectTypoInTParamReferenceHelper(TemplateParameters, STC);
1074
1075 if (auto CorrectedTParamReference = STC.getCorrection())
1076 return *CorrectedTParamReference;
1077
1078 return StringRef();
1079}
1080
1081InlineCommandRenderKind Sema::getInlineCommandRenderKind(StringRef Name) const {
1082 assert(Traits.getCommandInfo(Name)->IsInlineCommand);
1083
1084 return llvm::StringSwitch<InlineCommandRenderKind>(Name)
1086 .Cases({"c", "p"}, InlineCommandRenderKind::Monospaced)
1087 .Cases({"a", "e", "em"}, InlineCommandRenderKind::Emphasized)
1088 .Case("anchor", InlineCommandRenderKind::Anchor)
1090}
1091
1092} // end namespace comments
1093} // end namespace clang
Defines the C++ template declaration subclasses.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
Kind getKind() const
Definition DeclBase.h:450
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:234
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:142
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:105
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:275
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1005
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.
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.
NamedDecl * getParam(unsigned Idx)
bool isVoidType() const
Definition TypeBase.h:9027
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition Comment.h:616
SourceRange getCommandNameRange(const CommandTraits &Traits) const
Definition Comment.h:672
void setParagraph(ParagraphComment *PC)
Definition Comment.h:707
StringRef getCommandName(const CommandTraits &Traits) const
Definition Comment.h:664
void setArgs(ArrayRef< Argument > A)
Definition Comment.h:690
SourceRange getArgRange(unsigned Idx) const
Definition Comment.h:686
ParagraphComment * getParagraph() const LLVM_READONLY
Definition Comment.h:699
CommandMarkerKind getCommandMarker() const LLVM_READONLY
Definition Comment.h:714
This class provides information about commands that can be used in comments.
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:1097
An opening HTML tag with attributes.
Definition Comment.h:445
void setAttrs(ArrayRef< Attribute > Attrs)
Definition Comment.h:504
StringRef getTagName() const LLVM_READONLY
Definition Comment.h:427
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:567
Doxygen \param command.
Definition Comment.h:723
bool isDirectionExplicit() const LLVM_READONLY
Definition Comment.h:755
static const char * getDirectionAsString(ParamCommandPassDirection D)
Definition Comment.cpp:189
void setDirection(ParamCommandPassDirection Direction, bool Explicit)
Definition Comment.h:759
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)
TextComment * actOnText(SourceLocation LocBegin, SourceLocation LocEnd, StringRef Text)
void actOnBlockCommandArgs(BlockCommandComment *Command, ArrayRef< BlockCommandComment::Argument > Args)
BlockCommandComment * actOnBlockCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
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)
VerbatimBlockComment * actOnVerbatimBlockStart(SourceLocation Loc, unsigned CommandID)
void actOnTParamCommandParamNameArg(TParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
void setDecl(const Decl *D)
InlineContentComment * actOnUnknownCommand(SourceLocation LocBegin, SourceLocation LocEnd, StringRef CommandName, CommandMarkerKind CommandMarker)
ParagraphComment * actOnParagraphComment(ArrayRef< InlineContentComment * > Content)
Doxygen \tparam command, describes a template parameter.
Definition Comment.h:805
void setPosition(ArrayRef< unsigned > NewPosition)
Definition Comment.h:859
SourceRange getParamNameRange() const
Definition Comment.h:841
A verbatim block command (e.
Definition Comment.h:891
A line of text contained in a verbatim block.
Definition Comment.h:866
A verbatim line command.
Definition Comment.h:942
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.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Default
Set to the current date and time.
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition CharInfo.h:108
TemplateSpecializationKind
Describes the kind of template specialization that a particular template specialization declaration r...
Definition Specifiers.h:189
@ TSK_ExplicitInstantiationDefinition
This template specialization was instantiated from a template due to an explicit instantiation defini...
Definition Specifiers.h:207
@ TSK_ExplicitInstantiationDeclaration
This template specialization was instantiated from a template due to an explicit instantiation declar...
Definition Specifiers.h:203
Information about a single command.
unsigned IsFunctionDeclarationCommand
True if verbatim-like line command is a function declaration.
Information about the declaration, useful to clients of FullComment.
Definition Comment.h:974
@ FunctionKind
Something that we consider a "function":
Definition Comment.h:1016
@ VariableKind
Something that we consider a "variable":
Definition Comment.h:1028
unsigned IsObjCMethod
Is CommentDecl an ObjCMethodDecl.
Definition Comment.h:1065
unsigned IsFilled
If false, only CommentDecl is valid.
Definition Comment.h:1053
const Decl * CommentDecl
Declaration the comment is actually attached to (in the source).
Definition Comment.h:977
const Decl * CurrentDecl
CurrentDecl is the declaration with which the FullComment is associated.
Definition Comment.h:987
QualType ReturnType
Function return type if CommentDecl is something that we consider a "function".
Definition Comment.h:995