Path: blob/main/contrib/llvm-project/clang/lib/AST/CommentSema.cpp
35260 views
//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/AST/CommentSema.h"9#include "clang/AST/Attr.h"10#include "clang/AST/CommentCommandTraits.h"11#include "clang/AST/CommentDiagnostic.h"12#include "clang/AST/Decl.h"13#include "clang/AST/DeclTemplate.h"14#include "clang/Basic/LLVM.h"15#include "clang/Basic/SourceManager.h"16#include "clang/Lex/Preprocessor.h"17#include "llvm/ADT/SmallString.h"18#include "llvm/ADT/StringSwitch.h"1920namespace clang {21namespace comments {2223namespace {24#include "clang/AST/CommentHTMLTagsProperties.inc"25} // end anonymous namespace2627Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,28DiagnosticsEngine &Diags, CommandTraits &Traits,29const Preprocessor *PP) :30Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),31PP(PP), ThisDeclInfo(nullptr), BriefCommand(nullptr),32HeaderfileCommand(nullptr) {33}3435void Sema::setDecl(const Decl *D) {36if (!D)37return;3839ThisDeclInfo = new (Allocator) DeclInfo;40ThisDeclInfo->CommentDecl = D;41ThisDeclInfo->IsFilled = false;42}4344ParagraphComment *Sema::actOnParagraphComment(45ArrayRef<InlineContentComment *> Content) {46return new (Allocator) ParagraphComment(Content);47}4849BlockCommandComment *Sema::actOnBlockCommandStart(50SourceLocation LocBegin,51SourceLocation LocEnd,52unsigned CommandID,53CommandMarkerKind CommandMarker) {54BlockCommandComment *BC = new (Allocator) BlockCommandComment(LocBegin, LocEnd,55CommandID,56CommandMarker);57checkContainerDecl(BC);58return BC;59}6061void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,62ArrayRef<BlockCommandComment::Argument> Args) {63Command->setArgs(Args);64}6566void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,67ParagraphComment *Paragraph) {68Command->setParagraph(Paragraph);69checkBlockCommandEmptyParagraph(Command);70checkBlockCommandDuplicate(Command);71if (ThisDeclInfo) {72// These checks only make sense if the comment is attached to a73// declaration.74checkReturnsCommand(Command);75checkDeprecatedCommand(Command);76}77}7879ParamCommandComment *Sema::actOnParamCommandStart(80SourceLocation LocBegin,81SourceLocation LocEnd,82unsigned CommandID,83CommandMarkerKind CommandMarker) {84ParamCommandComment *Command =85new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,86CommandMarker);8788if (!involvesFunctionType())89Diag(Command->getLocation(),90diag::warn_doc_param_not_attached_to_a_function_decl)91<< CommandMarker92<< Command->getCommandNameRange(Traits);9394return Command;95}9697void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {98const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());99if (!Info->IsFunctionDeclarationCommand)100return;101102unsigned DiagSelect;103switch (Comment->getCommandID()) {104case CommandTraits::KCI_function:105DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 1 : 0;106break;107case CommandTraits::KCI_functiongroup:108DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 2 : 0;109break;110case CommandTraits::KCI_method:111DiagSelect = !isObjCMethodDecl() ? 3 : 0;112break;113case CommandTraits::KCI_methodgroup:114DiagSelect = !isObjCMethodDecl() ? 4 : 0;115break;116case CommandTraits::KCI_callback:117DiagSelect = !isFunctionPointerVarDecl() ? 5 : 0;118break;119default:120DiagSelect = 0;121break;122}123if (DiagSelect)124Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)125<< Comment->getCommandMarker()126<< (DiagSelect-1) << (DiagSelect-1)127<< Comment->getSourceRange();128}129130void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {131const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());132if (!Info->IsRecordLikeDeclarationCommand)133return;134unsigned DiagSelect;135switch (Comment->getCommandID()) {136case CommandTraits::KCI_class:137DiagSelect =138(!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl()) ? 1139: 0;140// Allow @class command on @interface declarations.141// FIXME. Currently, \class and @class are indistinguishable. So,142// \class is also allowed on an @interface declaration143if (DiagSelect && Comment->getCommandMarker() && isObjCInterfaceDecl())144DiagSelect = 0;145break;146case CommandTraits::KCI_interface:147DiagSelect = !isObjCInterfaceDecl() ? 2 : 0;148break;149case CommandTraits::KCI_protocol:150DiagSelect = !isObjCProtocolDecl() ? 3 : 0;151break;152case CommandTraits::KCI_struct:153DiagSelect = !isClassOrStructOrTagTypedefDecl() ? 4 : 0;154break;155case CommandTraits::KCI_union:156DiagSelect = !isUnionDecl() ? 5 : 0;157break;158default:159DiagSelect = 0;160break;161}162if (DiagSelect)163Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)164<< Comment->getCommandMarker()165<< (DiagSelect-1) << (DiagSelect-1)166<< Comment->getSourceRange();167}168169void Sema::checkContainerDecl(const BlockCommandComment *Comment) {170const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());171if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())172return;173unsigned DiagSelect;174switch (Comment->getCommandID()) {175case CommandTraits::KCI_classdesign:176DiagSelect = 1;177break;178case CommandTraits::KCI_coclass:179DiagSelect = 2;180break;181case CommandTraits::KCI_dependency:182DiagSelect = 3;183break;184case CommandTraits::KCI_helper:185DiagSelect = 4;186break;187case CommandTraits::KCI_helperclass:188DiagSelect = 5;189break;190case CommandTraits::KCI_helps:191DiagSelect = 6;192break;193case CommandTraits::KCI_instancesize:194DiagSelect = 7;195break;196case CommandTraits::KCI_ownership:197DiagSelect = 8;198break;199case CommandTraits::KCI_performance:200DiagSelect = 9;201break;202case CommandTraits::KCI_security:203DiagSelect = 10;204break;205case CommandTraits::KCI_superclass:206DiagSelect = 11;207break;208default:209DiagSelect = 0;210break;211}212if (DiagSelect)213Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)214<< Comment->getCommandMarker()215<< (DiagSelect-1)216<< Comment->getSourceRange();217}218219/// Turn a string into the corresponding PassDirection or -1 if it's not220/// valid.221static ParamCommandPassDirection getParamPassDirection(StringRef Arg) {222return llvm::StringSwitch<ParamCommandPassDirection>(Arg)223.Case("[in]", ParamCommandPassDirection::In)224.Case("[out]", ParamCommandPassDirection::Out)225.Cases("[in,out]", "[out,in]", ParamCommandPassDirection::InOut)226.Default(static_cast<ParamCommandPassDirection>(-1));227}228229void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,230SourceLocation ArgLocBegin,231SourceLocation ArgLocEnd,232StringRef Arg) {233std::string ArgLower = Arg.lower();234ParamCommandPassDirection Direction = getParamPassDirection(ArgLower);235236if (Direction == static_cast<ParamCommandPassDirection>(-1)) {237// Try again with whitespace removed.238llvm::erase_if(ArgLower, clang::isWhitespace);239Direction = getParamPassDirection(ArgLower);240241SourceRange ArgRange(ArgLocBegin, ArgLocEnd);242if (Direction != static_cast<ParamCommandPassDirection>(-1)) {243const char *FixedName =244ParamCommandComment::getDirectionAsString(Direction);245Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)246<< ArgRange << FixItHint::CreateReplacement(ArgRange, FixedName);247} else {248Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange;249Direction = ParamCommandPassDirection::In; // Sane fall back.250}251}252Command->setDirection(Direction,253/*Explicit=*/true);254}255256void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,257SourceLocation ArgLocBegin,258SourceLocation ArgLocEnd,259StringRef Arg) {260// Parser will not feed us more arguments than needed.261assert(Command->getNumArgs() == 0);262263if (!Command->isDirectionExplicit()) {264// User didn't provide a direction argument.265Command->setDirection(ParamCommandPassDirection::In,266/* Explicit = */ false);267}268auto *A = new (Allocator)269Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};270Command->setArgs(llvm::ArrayRef(A, 1));271}272273void Sema::actOnParamCommandFinish(ParamCommandComment *Command,274ParagraphComment *Paragraph) {275Command->setParagraph(Paragraph);276checkBlockCommandEmptyParagraph(Command);277}278279TParamCommandComment *Sema::actOnTParamCommandStart(280SourceLocation LocBegin,281SourceLocation LocEnd,282unsigned CommandID,283CommandMarkerKind CommandMarker) {284TParamCommandComment *Command =285new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID,286CommandMarker);287288if (!isTemplateOrSpecialization())289Diag(Command->getLocation(),290diag::warn_doc_tparam_not_attached_to_a_template_decl)291<< CommandMarker292<< Command->getCommandNameRange(Traits);293294return Command;295}296297void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,298SourceLocation ArgLocBegin,299SourceLocation ArgLocEnd,300StringRef Arg) {301// Parser will not feed us more arguments than needed.302assert(Command->getNumArgs() == 0);303304auto *A = new (Allocator)305Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};306Command->setArgs(llvm::ArrayRef(A, 1));307308if (!isTemplateOrSpecialization()) {309// We already warned that this \\tparam is not attached to a template decl.310return;311}312313const TemplateParameterList *TemplateParameters =314ThisDeclInfo->TemplateParameters;315SmallVector<unsigned, 2> Position;316if (resolveTParamReference(Arg, TemplateParameters, &Position)) {317Command->setPosition(copyArray(llvm::ArrayRef(Position)));318TParamCommandComment *&PrevCommand = TemplateParameterDocs[Arg];319if (PrevCommand) {320SourceRange ArgRange(ArgLocBegin, ArgLocEnd);321Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)322<< Arg << ArgRange;323Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)324<< PrevCommand->getParamNameRange();325}326PrevCommand = Command;327return;328}329330SourceRange ArgRange(ArgLocBegin, ArgLocEnd);331Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)332<< Arg << ArgRange;333334if (!TemplateParameters || TemplateParameters->size() == 0)335return;336337StringRef CorrectedName;338if (TemplateParameters->size() == 1) {339const NamedDecl *Param = TemplateParameters->getParam(0);340const IdentifierInfo *II = Param->getIdentifier();341if (II)342CorrectedName = II->getName();343} else {344CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);345}346347if (!CorrectedName.empty()) {348Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)349<< CorrectedName350<< FixItHint::CreateReplacement(ArgRange, CorrectedName);351}352}353354void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,355ParagraphComment *Paragraph) {356Command->setParagraph(Paragraph);357checkBlockCommandEmptyParagraph(Command);358}359360InlineCommandComment *361Sema::actOnInlineCommand(SourceLocation CommandLocBegin,362SourceLocation CommandLocEnd, unsigned CommandID,363ArrayRef<Comment::Argument> Args) {364StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;365366return new (Allocator)367InlineCommandComment(CommandLocBegin, CommandLocEnd, CommandID,368getInlineCommandRenderKind(CommandName), Args);369}370371InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,372SourceLocation LocEnd,373StringRef CommandName) {374unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();375return actOnUnknownCommand(LocBegin, LocEnd, CommandID);376}377378InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,379SourceLocation LocEnd,380unsigned CommandID) {381ArrayRef<InlineCommandComment::Argument> Args;382return new (Allocator) InlineCommandComment(383LocBegin, LocEnd, CommandID, InlineCommandRenderKind::Normal, Args);384}385386TextComment *Sema::actOnText(SourceLocation LocBegin,387SourceLocation LocEnd,388StringRef Text) {389return new (Allocator) TextComment(LocBegin, LocEnd, Text);390}391392VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,393unsigned CommandID) {394StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;395return new (Allocator) VerbatimBlockComment(396Loc,397Loc.getLocWithOffset(1 + CommandName.size()),398CommandID);399}400401VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,402StringRef Text) {403return new (Allocator) VerbatimBlockLineComment(Loc, Text);404}405406void Sema::actOnVerbatimBlockFinish(407VerbatimBlockComment *Block,408SourceLocation CloseNameLocBegin,409StringRef CloseName,410ArrayRef<VerbatimBlockLineComment *> Lines) {411Block->setCloseName(CloseName, CloseNameLocBegin);412Block->setLines(Lines);413}414415VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,416unsigned CommandID,417SourceLocation TextBegin,418StringRef Text) {419VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(420LocBegin,421TextBegin.getLocWithOffset(Text.size()),422CommandID,423TextBegin,424Text);425checkFunctionDeclVerbatimLine(VL);426checkContainerDeclVerbatimLine(VL);427return VL;428}429430HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,431StringRef TagName) {432return new (Allocator) HTMLStartTagComment(LocBegin, TagName);433}434435void Sema::actOnHTMLStartTagFinish(436HTMLStartTagComment *Tag,437ArrayRef<HTMLStartTagComment::Attribute> Attrs,438SourceLocation GreaterLoc,439bool IsSelfClosing) {440Tag->setAttrs(Attrs);441Tag->setGreaterLoc(GreaterLoc);442if (IsSelfClosing)443Tag->setSelfClosing();444else if (!isHTMLEndTagForbidden(Tag->getTagName()))445HTMLOpenTags.push_back(Tag);446}447448HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,449SourceLocation LocEnd,450StringRef TagName) {451HTMLEndTagComment *HET =452new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);453if (isHTMLEndTagForbidden(TagName)) {454Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)455<< TagName << HET->getSourceRange();456HET->setIsMalformed();457return HET;458}459460bool FoundOpen = false;461for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator462I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();463I != E; ++I) {464if ((*I)->getTagName() == TagName) {465FoundOpen = true;466break;467}468}469if (!FoundOpen) {470Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)471<< HET->getSourceRange();472HET->setIsMalformed();473return HET;474}475476while (!HTMLOpenTags.empty()) {477HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();478StringRef LastNotClosedTagName = HST->getTagName();479if (LastNotClosedTagName == TagName) {480// If the start tag is malformed, end tag is malformed as well.481if (HST->isMalformed())482HET->setIsMalformed();483break;484}485486if (isHTMLEndTagOptional(LastNotClosedTagName))487continue;488489bool OpenLineInvalid;490const unsigned OpenLine = SourceMgr.getPresumedLineNumber(491HST->getLocation(),492&OpenLineInvalid);493bool CloseLineInvalid;494const unsigned CloseLine = SourceMgr.getPresumedLineNumber(495HET->getLocation(),496&CloseLineInvalid);497498if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {499Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)500<< HST->getTagName() << HET->getTagName()501<< HST->getSourceRange() << HET->getSourceRange();502HST->setIsMalformed();503} else {504Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)505<< HST->getTagName() << HET->getTagName()506<< HST->getSourceRange();507Diag(HET->getLocation(), diag::note_doc_html_end_tag)508<< HET->getSourceRange();509HST->setIsMalformed();510}511}512513return HET;514}515516FullComment *Sema::actOnFullComment(517ArrayRef<BlockContentComment *> Blocks) {518FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);519resolveParamCommandIndexes(FC);520521// Complain about HTML tags that are not closed.522while (!HTMLOpenTags.empty()) {523HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();524if (isHTMLEndTagOptional(HST->getTagName()))525continue;526527Diag(HST->getLocation(), diag::warn_doc_html_missing_end_tag)528<< HST->getTagName() << HST->getSourceRange();529HST->setIsMalformed();530}531532return FC;533}534535void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {536if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)537return;538539ParagraphComment *Paragraph = Command->getParagraph();540if (Paragraph->isWhitespace()) {541SourceLocation DiagLoc;542if (Command->getNumArgs() > 0)543DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();544if (!DiagLoc.isValid())545DiagLoc = Command->getCommandNameRange(Traits).getEnd();546Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)547<< Command->getCommandMarker()548<< Command->getCommandName(Traits)549<< Command->getSourceRange();550}551}552553void Sema::checkReturnsCommand(const BlockCommandComment *Command) {554if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)555return;556557assert(ThisDeclInfo && "should not call this check on a bare comment");558559// We allow the return command for all @properties because it can be used560// to document the value that the property getter returns.561if (isObjCPropertyDecl())562return;563if (involvesFunctionType()) {564assert(!ThisDeclInfo->ReturnType.isNull() &&565"should have a valid return type");566if (ThisDeclInfo->ReturnType->isVoidType()) {567unsigned DiagKind;568switch (ThisDeclInfo->CommentDecl->getKind()) {569default:570if (ThisDeclInfo->IsObjCMethod)571DiagKind = 3;572else573DiagKind = 0;574break;575case Decl::CXXConstructor:576DiagKind = 1;577break;578case Decl::CXXDestructor:579DiagKind = 2;580break;581}582Diag(Command->getLocation(),583diag::warn_doc_returns_attached_to_a_void_function)584<< Command->getCommandMarker()585<< Command->getCommandName(Traits)586<< DiagKind587<< Command->getSourceRange();588}589return;590}591592Diag(Command->getLocation(),593diag::warn_doc_returns_not_attached_to_a_function_decl)594<< Command->getCommandMarker()595<< Command->getCommandName(Traits)596<< Command->getSourceRange();597}598599void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {600const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());601const BlockCommandComment *PrevCommand = nullptr;602if (Info->IsBriefCommand) {603if (!BriefCommand) {604BriefCommand = Command;605return;606}607PrevCommand = BriefCommand;608} else if (Info->IsHeaderfileCommand) {609if (!HeaderfileCommand) {610HeaderfileCommand = Command;611return;612}613PrevCommand = HeaderfileCommand;614} else {615// We don't want to check this command for duplicates.616return;617}618StringRef CommandName = Command->getCommandName(Traits);619StringRef PrevCommandName = PrevCommand->getCommandName(Traits);620Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)621<< Command->getCommandMarker()622<< CommandName623<< Command->getSourceRange();624if (CommandName == PrevCommandName)625Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)626<< PrevCommand->getCommandMarker()627<< PrevCommandName628<< PrevCommand->getSourceRange();629else630Diag(PrevCommand->getLocation(),631diag::note_doc_block_command_previous_alias)632<< PrevCommand->getCommandMarker()633<< PrevCommandName634<< CommandName;635}636637void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {638if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)639return;640641assert(ThisDeclInfo && "should not call this check on a bare comment");642643const Decl *D = ThisDeclInfo->CommentDecl;644if (!D)645return;646647if (D->hasAttr<DeprecatedAttr>() ||648D->hasAttr<AvailabilityAttr>() ||649D->hasAttr<UnavailableAttr>())650return;651652Diag(Command->getLocation(), diag::warn_doc_deprecated_not_sync)653<< Command->getSourceRange() << Command->getCommandMarker();654655// Try to emit a fixit with a deprecation attribute.656if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {657// Don't emit a Fix-It for non-member function definitions. GCC does not658// accept attributes on them.659const DeclContext *Ctx = FD->getDeclContext();660if ((!Ctx || !Ctx->isRecord()) &&661FD->doesThisDeclarationHaveABody())662return;663664const LangOptions &LO = FD->getLangOpts();665const bool DoubleSquareBracket = LO.CPlusPlus14 || LO.C23;666StringRef AttributeSpelling =667DoubleSquareBracket ? "[[deprecated]]" : "__attribute__((deprecated))";668if (PP) {669// Try to find a replacement macro:670// - In C23/C++14 we prefer [[deprecated]].671// - If not found or an older C/C++ look for __attribute__((deprecated)).672StringRef MacroName;673if (DoubleSquareBracket) {674TokenValue Tokens[] = {tok::l_square, tok::l_square,675PP->getIdentifierInfo("deprecated"),676tok::r_square, tok::r_square};677MacroName = PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);678if (!MacroName.empty())679AttributeSpelling = MacroName;680}681682if (MacroName.empty()) {683TokenValue Tokens[] = {684tok::kw___attribute, tok::l_paren,685tok::l_paren, PP->getIdentifierInfo("deprecated"),686tok::r_paren, tok::r_paren};687StringRef MacroName =688PP->getLastMacroWithSpelling(FD->getLocation(), Tokens);689if (!MacroName.empty())690AttributeSpelling = MacroName;691}692}693694SmallString<64> TextToInsert = AttributeSpelling;695TextToInsert += " ";696SourceLocation Loc = FD->getSourceRange().getBegin();697Diag(Loc, diag::note_add_deprecation_attr)698<< FixItHint::CreateInsertion(Loc, TextToInsert);699}700}701702void Sema::resolveParamCommandIndexes(const FullComment *FC) {703if (!involvesFunctionType()) {704// We already warned that \\param commands are not attached to a function705// decl.706return;707}708709SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;710711// Comment AST nodes that correspond to \c ParamVars for which we have712// found a \\param command or NULL if no documentation was found so far.713SmallVector<ParamCommandComment *, 8> ParamVarDocs;714715ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();716ParamVarDocs.resize(ParamVars.size(), nullptr);717718// First pass over all \\param commands: resolve all parameter names.719for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();720I != E; ++I) {721ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);722if (!PCC || !PCC->hasParamName())723continue;724StringRef ParamName = PCC->getParamNameAsWritten();725726// Check that referenced parameter name is in the function decl.727const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,728ParamVars);729if (ResolvedParamIndex == ParamCommandComment::VarArgParamIndex) {730PCC->setIsVarArgParam();731continue;732}733if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {734UnresolvedParamCommands.push_back(PCC);735continue;736}737PCC->setParamIndex(ResolvedParamIndex);738if (ParamVarDocs[ResolvedParamIndex]) {739SourceRange ArgRange = PCC->getParamNameRange();740Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)741<< ParamName << ArgRange;742ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];743Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)744<< PrevCommand->getParamNameRange();745}746ParamVarDocs[ResolvedParamIndex] = PCC;747}748749// Find parameter declarations that have no corresponding \\param.750SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;751for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {752if (!ParamVarDocs[i])753OrphanedParamDecls.push_back(ParamVars[i]);754}755756// Second pass over unresolved \\param commands: do typo correction.757// Suggest corrections from a set of parameter declarations that have no758// corresponding \\param.759for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {760const ParamCommandComment *PCC = UnresolvedParamCommands[i];761762SourceRange ArgRange = PCC->getParamNameRange();763StringRef ParamName = PCC->getParamNameAsWritten();764Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)765<< ParamName << ArgRange;766767// All parameters documented -- can't suggest a correction.768if (OrphanedParamDecls.size() == 0)769continue;770771unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;772if (OrphanedParamDecls.size() == 1) {773// If one parameter is not documented then that parameter is the only774// possible suggestion.775CorrectedParamIndex = 0;776} else {777// Do typo correction.778CorrectedParamIndex = correctTypoInParmVarReference(ParamName,779OrphanedParamDecls);780}781if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {782const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];783if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())784Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)785<< CorrectedII->getName()786<< FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());787}788}789}790791bool Sema::involvesFunctionType() {792if (!ThisDeclInfo)793return false;794if (!ThisDeclInfo->IsFilled)795inspectThisDecl();796return ThisDeclInfo->involvesFunctionType();797}798799bool Sema::isFunctionDecl() {800if (!ThisDeclInfo)801return false;802if (!ThisDeclInfo->IsFilled)803inspectThisDecl();804return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;805}806807bool Sema::isAnyFunctionDecl() {808return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&809isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);810}811812bool Sema::isFunctionOrMethodVariadic() {813if (!ThisDeclInfo)814return false;815if (!ThisDeclInfo->IsFilled)816inspectThisDecl();817return ThisDeclInfo->IsVariadic;818}819820bool Sema::isObjCMethodDecl() {821return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&822isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);823}824825bool Sema::isFunctionPointerVarDecl() {826if (!ThisDeclInfo)827return false;828if (!ThisDeclInfo->IsFilled)829inspectThisDecl();830if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {831if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {832QualType QT = VD->getType();833return QT->isFunctionPointerType();834}835}836return false;837}838839bool Sema::isObjCPropertyDecl() {840if (!ThisDeclInfo)841return false;842if (!ThisDeclInfo->IsFilled)843inspectThisDecl();844return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;845}846847bool Sema::isTemplateOrSpecialization() {848if (!ThisDeclInfo)849return false;850if (!ThisDeclInfo->IsFilled)851inspectThisDecl();852return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;853}854855bool Sema::isRecordLikeDecl() {856if (!ThisDeclInfo)857return false;858if (!ThisDeclInfo->IsFilled)859inspectThisDecl();860return isUnionDecl() || isClassOrStructDecl() || isObjCInterfaceDecl() ||861isObjCProtocolDecl();862}863864bool Sema::isUnionDecl() {865if (!ThisDeclInfo)866return false;867if (!ThisDeclInfo->IsFilled)868inspectThisDecl();869if (const RecordDecl *RD =870dyn_cast_or_null<RecordDecl>(ThisDeclInfo->CurrentDecl))871return RD->isUnion();872return false;873}874static bool isClassOrStructDeclImpl(const Decl *D) {875if (auto *record = dyn_cast_or_null<RecordDecl>(D))876return !record->isUnion();877878return false;879}880881bool Sema::isClassOrStructDecl() {882if (!ThisDeclInfo)883return false;884if (!ThisDeclInfo->IsFilled)885inspectThisDecl();886887if (!ThisDeclInfo->CurrentDecl)888return false;889890return isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl);891}892893bool Sema::isClassOrStructOrTagTypedefDecl() {894if (!ThisDeclInfo)895return false;896if (!ThisDeclInfo->IsFilled)897inspectThisDecl();898899if (!ThisDeclInfo->CurrentDecl)900return false;901902if (isClassOrStructDeclImpl(ThisDeclInfo->CurrentDecl))903return true;904905if (auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(ThisDeclInfo->CurrentDecl)) {906auto UnderlyingType = ThisTypedefDecl->getUnderlyingType();907if (auto ThisElaboratedType = dyn_cast<ElaboratedType>(UnderlyingType)) {908auto DesugaredType = ThisElaboratedType->desugar();909if (auto *DesugaredTypePtr = DesugaredType.getTypePtrOrNull()) {910if (auto *ThisRecordType = dyn_cast<RecordType>(DesugaredTypePtr)) {911return isClassOrStructDeclImpl(ThisRecordType->getAsRecordDecl());912}913}914}915}916917return false;918}919920bool Sema::isClassTemplateDecl() {921if (!ThisDeclInfo)922return false;923if (!ThisDeclInfo->IsFilled)924inspectThisDecl();925return ThisDeclInfo->CurrentDecl &&926(isa<ClassTemplateDecl>(ThisDeclInfo->CurrentDecl));927}928929bool Sema::isFunctionTemplateDecl() {930if (!ThisDeclInfo)931return false;932if (!ThisDeclInfo->IsFilled)933inspectThisDecl();934return ThisDeclInfo->CurrentDecl &&935(isa<FunctionTemplateDecl>(ThisDeclInfo->CurrentDecl));936}937938bool Sema::isObjCInterfaceDecl() {939if (!ThisDeclInfo)940return false;941if (!ThisDeclInfo->IsFilled)942inspectThisDecl();943return ThisDeclInfo->CurrentDecl &&944isa<ObjCInterfaceDecl>(ThisDeclInfo->CurrentDecl);945}946947bool Sema::isObjCProtocolDecl() {948if (!ThisDeclInfo)949return false;950if (!ThisDeclInfo->IsFilled)951inspectThisDecl();952return ThisDeclInfo->CurrentDecl &&953isa<ObjCProtocolDecl>(ThisDeclInfo->CurrentDecl);954}955956ArrayRef<const ParmVarDecl *> Sema::getParamVars() {957if (!ThisDeclInfo->IsFilled)958inspectThisDecl();959return ThisDeclInfo->ParamVars;960}961962void Sema::inspectThisDecl() {963ThisDeclInfo->fill();964}965966unsigned Sema::resolveParmVarReference(StringRef Name,967ArrayRef<const ParmVarDecl *> ParamVars) {968for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {969const IdentifierInfo *II = ParamVars[i]->getIdentifier();970if (II && II->getName() == Name)971return i;972}973if (Name == "..." && isFunctionOrMethodVariadic())974return ParamCommandComment::VarArgParamIndex;975return ParamCommandComment::InvalidParamIndex;976}977978namespace {979class SimpleTypoCorrector {980const NamedDecl *BestDecl;981982StringRef Typo;983const unsigned MaxEditDistance;984985unsigned BestEditDistance;986unsigned BestIndex;987unsigned NextIndex;988989public:990explicit SimpleTypoCorrector(StringRef Typo)991: BestDecl(nullptr), Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),992BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}993994void addDecl(const NamedDecl *ND);995996const NamedDecl *getBestDecl() const {997if (BestEditDistance > MaxEditDistance)998return nullptr;9991000return BestDecl;1001}10021003unsigned getBestDeclIndex() const {1004assert(getBestDecl());1005return BestIndex;1006}1007};10081009void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {1010unsigned CurrIndex = NextIndex++;10111012const IdentifierInfo *II = ND->getIdentifier();1013if (!II)1014return;10151016StringRef Name = II->getName();1017unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());1018if (MinPossibleEditDistance > 0 &&1019Typo.size() / MinPossibleEditDistance < 3)1020return;10211022unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);1023if (EditDistance < BestEditDistance) {1024BestEditDistance = EditDistance;1025BestDecl = ND;1026BestIndex = CurrIndex;1027}1028}1029} // end anonymous namespace10301031unsigned Sema::correctTypoInParmVarReference(1032StringRef Typo,1033ArrayRef<const ParmVarDecl *> ParamVars) {1034SimpleTypoCorrector Corrector(Typo);1035for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)1036Corrector.addDecl(ParamVars[i]);1037if (Corrector.getBestDecl())1038return Corrector.getBestDeclIndex();1039else1040return ParamCommandComment::InvalidParamIndex;1041}10421043namespace {1044bool ResolveTParamReferenceHelper(1045StringRef Name,1046const TemplateParameterList *TemplateParameters,1047SmallVectorImpl<unsigned> *Position) {1048for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {1049const NamedDecl *Param = TemplateParameters->getParam(i);1050const IdentifierInfo *II = Param->getIdentifier();1051if (II && II->getName() == Name) {1052Position->push_back(i);1053return true;1054}10551056if (const TemplateTemplateParmDecl *TTP =1057dyn_cast<TemplateTemplateParmDecl>(Param)) {1058Position->push_back(i);1059if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),1060Position))1061return true;1062Position->pop_back();1063}1064}1065return false;1066}1067} // end anonymous namespace10681069bool Sema::resolveTParamReference(1070StringRef Name,1071const TemplateParameterList *TemplateParameters,1072SmallVectorImpl<unsigned> *Position) {1073Position->clear();1074if (!TemplateParameters)1075return false;10761077return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);1078}10791080namespace {1081void CorrectTypoInTParamReferenceHelper(1082const TemplateParameterList *TemplateParameters,1083SimpleTypoCorrector &Corrector) {1084for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {1085const NamedDecl *Param = TemplateParameters->getParam(i);1086Corrector.addDecl(Param);10871088if (const TemplateTemplateParmDecl *TTP =1089dyn_cast<TemplateTemplateParmDecl>(Param))1090CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),1091Corrector);1092}1093}1094} // end anonymous namespace10951096StringRef Sema::correctTypoInTParamReference(1097StringRef Typo,1098const TemplateParameterList *TemplateParameters) {1099SimpleTypoCorrector Corrector(Typo);1100CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);1101if (const NamedDecl *ND = Corrector.getBestDecl()) {1102const IdentifierInfo *II = ND->getIdentifier();1103assert(II && "SimpleTypoCorrector should not return this decl");1104return II->getName();1105}1106return StringRef();1107}11081109InlineCommandRenderKind Sema::getInlineCommandRenderKind(StringRef Name) const {1110assert(Traits.getCommandInfo(Name)->IsInlineCommand);11111112return llvm::StringSwitch<InlineCommandRenderKind>(Name)1113.Case("b", InlineCommandRenderKind::Bold)1114.Cases("c", "p", InlineCommandRenderKind::Monospaced)1115.Cases("a", "e", "em", InlineCommandRenderKind::Emphasized)1116.Case("anchor", InlineCommandRenderKind::Anchor)1117.Default(InlineCommandRenderKind::Normal);1118}11191120} // end namespace comments1121} // end namespace clang112211231124