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