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