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