1//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/AST/CommentSema.h"
10#include "clang/AST/Attr.h"
11#include "clang/AST/CommentCommandTraits.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclTemplate.h"
14#include "clang/Basic/DiagnosticComment.h"
15#include "clang/Basic/LLVM.h"
16#include "clang/Basic/SimpleTypoCorrection.h"
17#include "clang/Basic/SourceManager.h"
18#include "clang/Lex/Preprocessor.h"
19#include "llvm/ADT/StringSwitch.h"
20
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
45ParagraphComment *Sema::actOnParagraphComment(
46 ArrayRef<InlineContentComment *> Content) {
47 return new (Allocator) ParagraphComment(Content);
48}
49
50BlockCommandComment *Sema::actOnBlockCommandStart(
51 SourceLocation LocBegin,
52 SourceLocation LocEnd,
53 unsigned CommandID,
54 CommandMarkerKind CommandMarker) {
55 BlockCommandComment *BC = new (Allocator) BlockCommandComment(LocBegin, LocEnd,
56 CommandID,
57 CommandMarker);
58 checkContainerDecl(Comment: BC);
59 return BC;
60}
61
62void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
63 ArrayRef<BlockCommandComment::Argument> Args) {
64 Command->setArgs(Args);
65}
66
67void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
68 ParagraphComment *Paragraph) {
69 Command->setParagraph(Paragraph);
70 checkBlockCommandEmptyParagraph(Command);
71 checkBlockCommandDuplicate(Command);
72 if (ThisDeclInfo) {
73 // These checks only make sense if the comment is attached to a
74 // declaration.
75 checkReturnsCommand(Command);
76 checkDeprecatedCommand(Comment: Command);
77 }
78}
79
80ParamCommandComment *Sema::actOnParamCommandStart(
81 SourceLocation LocBegin,
82 SourceLocation LocEnd,
83 unsigned CommandID,
84 CommandMarkerKind CommandMarker) {
85 ParamCommandComment *Command =
86 new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,
87 CommandMarker);
88
89 if (!involvesFunctionType())
90 Diag(Loc: Command->getLocation(),
91 DiagID: 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(CommandID: Comment->getCommandID());
100 if (!Info->IsFunctionDeclarationCommand)
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(Loc: Comment->getLocation(), DiagID: 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(CommandID: 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(Loc: Comment->getLocation(), DiagID: 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(CommandID: 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(Loc: Comment->getLocation(), DiagID: 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.
222static ParamCommandPassDirection getParamPassDirection(StringRef Arg) {
223 return llvm::StringSwitch<ParamCommandPassDirection>(Arg)
224 .Case(S: "[in]", Value: ParamCommandPassDirection::In)
225 .Case(S: "[out]", Value: ParamCommandPassDirection::Out)
226 .Cases(CaseStrings: {"[in,out]", "[out,in]"}, Value: ParamCommandPassDirection::InOut)
227 .Default(Value: static_cast<ParamCommandPassDirection>(-1));
228}
229
230void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
231 SourceLocation ArgLocBegin,
232 SourceLocation ArgLocEnd,
233 StringRef Arg) {
234 std::string ArgLower = Arg.lower();
235 ParamCommandPassDirection Direction = getParamPassDirection(Arg: ArgLower);
236
237 if (Direction == static_cast<ParamCommandPassDirection>(-1)) {
238 // Try again with whitespace removed.
239 llvm::erase_if(C&: ArgLower, P: clang::isWhitespace);
240 Direction = getParamPassDirection(Arg: ArgLower);
241
242 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
243 if (Direction != static_cast<ParamCommandPassDirection>(-1)) {
244 const char *FixedName =
245 ParamCommandComment::getDirectionAsString(D: Direction);
246 Diag(Loc: ArgLocBegin, DiagID: diag::warn_doc_param_spaces_in_direction)
247 << ArgRange << FixItHint::CreateReplacement(RemoveRange: ArgRange, Code: FixedName);
248 } else {
249 Diag(Loc: ArgLocBegin, DiagID: 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
257void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
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.
266 Command->setDirection(Direction: ParamCommandPassDirection::In,
267 /* Explicit = */ false);
268 }
269 auto *A = new (Allocator)
270 Comment::Argument{.Range: SourceRange(ArgLocBegin, ArgLocEnd), .Text: Arg};
271 Command->setArgs(ArrayRef(A, 1));
272}
273
274void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
275 ParagraphComment *Paragraph) {
276 Command->setParagraph(Paragraph);
277 checkBlockCommandEmptyParagraph(Command);
278}
279
280TParamCommandComment *Sema::actOnTParamCommandStart(
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(Loc: Command->getLocation(),
297 DiagID: diag::warn_doc_tparam_not_attached_to_a_template_decl)
298 << CommandMarker
299 << Command->getCommandNameRange(Traits);
300
301 return Command;
302}
303
304void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
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{.Range: SourceRange(ArgLocBegin, ArgLocEnd), .Text: 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;
322 SmallVector<unsigned, 2> Position;
323 if (resolveTParamReference(Name: Arg, TemplateParameters, Position: &Position)) {
324 Command->setPosition(copyArray(Source: ArrayRef(Position)));
325 TParamCommandComment *&PrevCommand = TemplateParameterDocs[Arg];
326 if (PrevCommand) {
327 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
328 Diag(Loc: ArgLocBegin, DiagID: diag::warn_doc_tparam_duplicate)
329 << Arg << ArgRange;
330 Diag(Loc: PrevCommand->getLocation(), DiagID: diag::note_doc_tparam_previous)
331 << PrevCommand->getParamNameRange();
332 }
333 PrevCommand = Command;
334 return;
335 }
336
337 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
338 Diag(Loc: ArgLocBegin, DiagID: 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(Idx: 0);
347 const IdentifierInfo *II = Param->getIdentifier();
348 if (II)
349 CorrectedName = II->getName();
350 } else {
351 CorrectedName = correctTypoInTParamReference(Typo: Arg, TemplateParameters);
352 }
353
354 if (!CorrectedName.empty()) {
355 Diag(Loc: ArgLocBegin, DiagID: diag::note_doc_tparam_name_suggestion)
356 << CorrectedName
357 << FixItHint::CreateReplacement(RemoveRange: ArgRange, Code: CorrectedName);
358 }
359}
360
361void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
362 ParagraphComment *Paragraph) {
363 Command->setParagraph(Paragraph);
364 checkBlockCommandEmptyParagraph(Command);
365}
366
367InlineCommandComment *
368Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
369 SourceLocation CommandLocEnd, unsigned CommandID,
370 CommandMarkerKind CommandMarker,
371 ArrayRef<Comment::Argument> Args) {
372 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
373
374 return new (Allocator) InlineCommandComment(
375 CommandLocBegin, CommandLocEnd, CommandID,
376 getInlineCommandRenderKind(Name: CommandName), CommandMarker, Args);
377}
378
379InlineContentComment *
380Sema::actOnUnknownCommand(SourceLocation LocBegin, SourceLocation LocEnd,
381 StringRef CommandName,
382 CommandMarkerKind CommandMarker) {
383 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
384 return actOnUnknownCommand(LocBegin, LocEnd, CommandID, CommandMarker);
385}
386
387InlineContentComment *
388Sema::actOnUnknownCommand(SourceLocation LocBegin, SourceLocation LocEnd,
389 unsigned CommandID, CommandMarkerKind CommandMarker) {
390 ArrayRef<InlineCommandComment::Argument> Args;
391 return new (Allocator) InlineCommandComment(LocBegin, LocEnd, CommandID,
392 InlineCommandRenderKind::Normal,
393 CommandMarker, Args);
394}
395
396TextComment *Sema::actOnText(SourceLocation LocBegin,
397 SourceLocation LocEnd,
398 StringRef Text) {
399 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
400}
401
402VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
403 unsigned CommandID) {
404 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
405 return new (Allocator) VerbatimBlockComment(
406 Loc,
407 Loc.getLocWithOffset(Offset: 1 + CommandName.size()),
408 CommandID);
409}
410
411VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
412 StringRef Text) {
413 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
414}
415
416void Sema::actOnVerbatimBlockFinish(
417 VerbatimBlockComment *Block,
418 SourceLocation CloseNameLocBegin,
419 StringRef CloseName,
420 ArrayRef<VerbatimBlockLineComment *> Lines) {
421 Block->setCloseName(Name: CloseName, LocBegin: CloseNameLocBegin);
422 Block->setLines(Lines);
423}
424
425VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
426 unsigned CommandID,
427 SourceLocation TextBegin,
428 StringRef Text) {
429 VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
430 LocBegin,
431 TextBegin.getLocWithOffset(Offset: Text.size()),
432 CommandID,
433 TextBegin,
434 Text);
435 checkFunctionDeclVerbatimLine(Comment: VL);
436 checkContainerDeclVerbatimLine(Comment: VL);
437 return VL;
438}
439
440HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
441 StringRef TagName) {
442 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
443}
444
445void Sema::actOnHTMLStartTagFinish(
446 HTMLStartTagComment *Tag,
447 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
448 SourceLocation GreaterLoc,
449 bool IsSelfClosing) {
450 Tag->setAttrs(Attrs);
451 Tag->setGreaterLoc(GreaterLoc);
452 if (IsSelfClosing)
453 Tag->setSelfClosing();
454 else if (!isHTMLEndTagForbidden(Name: Tag->getTagName()))
455 HTMLOpenTags.push_back(Elt: Tag);
456}
457
458HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
459 SourceLocation LocEnd,
460 StringRef TagName) {
461 HTMLEndTagComment *HET =
462 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
463 if (isHTMLEndTagForbidden(Name: TagName)) {
464 Diag(Loc: HET->getLocation(), DiagID: diag::warn_doc_html_end_forbidden)
465 << TagName << HET->getSourceRange();
466 HET->setIsMalformed();
467 return HET;
468 }
469
470 bool FoundOpen = false;
471 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
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(Loc: HET->getLocation(), DiagID: 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(Name: LastNotClosedTagName))
497 continue;
498
499 bool OpenLineInvalid;
500 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
501 Loc: HST->getLocation(),
502 Invalid: &OpenLineInvalid);
503 bool CloseLineInvalid;
504 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
505 Loc: HET->getLocation(),
506 Invalid: &CloseLineInvalid);
507
508 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
509 Diag(Loc: HST->getLocation(), DiagID: diag::warn_doc_html_start_end_mismatch)
510 << HST->getTagName() << HET->getTagName()
511 << HST->getSourceRange() << HET->getSourceRange();
512 HST->setIsMalformed();
513 } else {
514 Diag(Loc: HST->getLocation(), DiagID: diag::warn_doc_html_start_end_mismatch)
515 << HST->getTagName() << HET->getTagName()
516 << HST->getSourceRange();
517 Diag(Loc: HET->getLocation(), DiagID: diag::note_doc_html_end_tag)
518 << HET->getSourceRange();
519 HST->setIsMalformed();
520 }
521 }
522
523 return HET;
524}
525
526FullComment *Sema::actOnFullComment(
527 ArrayRef<BlockContentComment *> Blocks) {
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(Name: HST->getTagName()))
535 continue;
536
537 Diag(Loc: HST->getLocation(), DiagID: 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(CommandID: 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(Idx: Command->getNumArgs() - 1).getEnd();
554 if (!DiagLoc.isValid())
555 DiagLoc = Command->getCommandNameRange(Traits).getEnd();
556 Diag(Loc: DiagLoc, DiagID: 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(CommandID: 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(Loc: Command->getLocation(),
593 DiagID: 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(Loc: Command->getLocation(),
603 DiagID: 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(CommandID: 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(Loc: Command->getLocation(), DiagID: diag::warn_doc_block_command_duplicate)
631 << Command->getCommandMarker()
632 << CommandName
633 << Command->getSourceRange();
634 if (CommandName == PrevCommandName)
635 Diag(Loc: PrevCommand->getLocation(), DiagID: diag::note_doc_block_command_previous)
636 << PrevCommand->getCommandMarker()
637 << PrevCommandName
638 << PrevCommand->getSourceRange();
639 else
640 Diag(Loc: PrevCommand->getLocation(),
641 DiagID: 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(CommandID: 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(Loc: Command->getLocation(), DiagID: 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>(Val: 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(Name: "deprecated"),
686 tok::r_square, tok::r_square};
687 MacroName = PP->getLastMacroWithSpelling(Loc: 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(Name: "deprecated"),
696 tok::r_paren, tok::r_paren};
697 StringRef MacroName =
698 PP->getLastMacroWithSpelling(Loc: 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, DiagID: diag::note_add_deprecation_attr)
708 << FixItHint::CreateInsertion(InsertionLoc: Loc, Code: 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(N: ParamVars.size(), NV: 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>(Val: *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(Name: ParamName,
738 ParamVars);
739 if (ResolvedParamIndex == ParamCommandComment::VarArgParamIndex) {
740 PCC->setIsVarArgParam();
741 continue;
742 }
743 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
744 UnresolvedParamCommands.push_back(Elt: PCC);
745 continue;
746 }
747 PCC->setParamIndex(ResolvedParamIndex);
748 if (ParamVarDocs[ResolvedParamIndex]) {
749 SourceRange ArgRange = PCC->getParamNameRange();
750 Diag(Loc: ArgRange.getBegin(), DiagID: diag::warn_doc_param_duplicate)
751 << ParamName << ArgRange;
752 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
753 Diag(Loc: PrevCommand->getLocation(), DiagID: 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(Elt: 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(Loc: ArgRange.getBegin(), DiagID: 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(Typo: ParamName,
789 ParamVars: OrphanedParamDecls);
790 }
791 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
792 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
793 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
794 Diag(Loc: ArgRange.getBegin(), DiagID: diag::note_doc_param_name_suggestion)
795 << CorrectedII->getName()
796 << FixItHint::CreateReplacement(RemoveRange: ArgRange, Code: 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>(Val: 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>(Val: 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>(Val: 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>(Val: ThisDeclInfo->CurrentDecl)) {
871 TemplateSpecializationKind TSK = FD->getTemplateSpecializationKind();
872 return (TSK == TSK_ExplicitInstantiationDeclaration) ||
873 (TSK == TSK_ExplicitInstantiationDefinition);
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>(Val: 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>(Val: 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(D: 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(D: ThisDeclInfo->CurrentDecl))
926 return true;
927
928 if (auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(Val: 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>(Val: 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>(Val: 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>(Val: 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>(Val: 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())
989 return ParamCommandComment::VarArgParamIndex;
990 return ParamCommandComment::InvalidParamIndex;
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(Candidate: Param->getIdentifier());
1003 }
1004
1005 if (STC.hasCorrection())
1006 return STC.getCorrectionIndex();
1007
1008 return ParamCommandComment::InvalidParamIndex;
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(Idx: i);
1018 const IdentifierInfo *II = Param->getIdentifier();
1019 if (II && II->getName() == Name) {
1020 Position->push_back(Elt: i);
1021 return true;
1022 }
1023
1024 if (const TemplateTemplateParmDecl *TTP =
1025 dyn_cast<TemplateTemplateParmDecl>(Val: Param)) {
1026 Position->push_back(Elt: i);
1027 if (ResolveTParamReferenceHelper(Name, TemplateParameters: 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(Idx: i);
1054 if (!Param)
1055 continue;
1056
1057 STC.add(Candidate: Param->getIdentifier());
1058
1059 if (const TemplateTemplateParmDecl *TTP =
1060 dyn_cast<TemplateTemplateParmDecl>(Val: Param))
1061 CorrectTypoInTParamReferenceHelper(TemplateParameters: 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)
1082 .Case(S: "b", Value: InlineCommandRenderKind::Bold)
1083 .Cases(CaseStrings: {"c", "p"}, Value: InlineCommandRenderKind::Monospaced)
1084 .Cases(CaseStrings: {"a", "e", "em"}, Value: InlineCommandRenderKind::Emphasized)
1085 .Case(S: "anchor", Value: InlineCommandRenderKind::Anchor)
1086 .Default(Value: InlineCommandRenderKind::Normal);
1087}
1088
1089} // end namespace comments
1090} // end namespace clang
1091