brintos

brintos / llvm-project-archived public Read only

0
0
Text · 36.6 KiB · d5ba240 Raw
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