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