CommentSema.cpp revision 245431
1//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "clang/AST/CommentSema.h" 11#include "clang/AST/CommentDiagnostic.h" 12#include "clang/AST/CommentCommandTraits.h" 13#include "clang/AST/Decl.h" 14#include "clang/AST/DeclTemplate.h" 15#include "clang/Basic/SourceManager.h" 16#include "clang/Lex/Preprocessor.h" 17#include "llvm/ADT/StringSwitch.h" 18#include "llvm/ADT/SmallString.h" 19 20namespace clang { 21namespace comments { 22 23namespace { 24#include "clang/AST/CommentHTMLTagsProperties.inc" 25} // unnamed namespace 26 27Sema::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(NULL), BriefCommand(NULL), ReturnsCommand(NULL) { 32} 33 34void Sema::setDecl(const Decl *D) { 35 if (!D) 36 return; 37 38 ThisDeclInfo = new (Allocator) DeclInfo; 39 ThisDeclInfo->CommentDecl = D; 40 ThisDeclInfo->IsFilled = false; 41} 42 43ParagraphComment *Sema::actOnParagraphComment( 44 ArrayRef<InlineContentComment *> Content) { 45 return new (Allocator) ParagraphComment(Content); 46} 47 48BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin, 49 SourceLocation LocEnd, 50 unsigned CommandID) { 51 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID); 52} 53 54void Sema::actOnBlockCommandArgs(BlockCommandComment *Command, 55 ArrayRef<BlockCommandComment::Argument> Args) { 56 Command->setArgs(Args); 57} 58 59void Sema::actOnBlockCommandFinish(BlockCommandComment *Command, 60 ParagraphComment *Paragraph) { 61 Command->setParagraph(Paragraph); 62 checkBlockCommandEmptyParagraph(Command); 63 checkBlockCommandDuplicate(Command); 64 checkReturnsCommand(Command); 65 checkDeprecatedCommand(Command); 66} 67 68ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin, 69 SourceLocation LocEnd, 70 unsigned CommandID) { 71 ParamCommandComment *Command = 72 new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID); 73 74 if (!isFunctionDecl()) 75 Diag(Command->getLocation(), 76 diag::warn_doc_param_not_attached_to_a_function_decl) 77 << Command->getCommandNameRange(Traits); 78 79 return Command; 80} 81 82void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command, 83 SourceLocation ArgLocBegin, 84 SourceLocation ArgLocEnd, 85 StringRef Arg) { 86 ParamCommandComment::PassDirection Direction; 87 std::string ArgLower = Arg.lower(); 88 // TODO: optimize: lower Name first (need an API in SmallString for that), 89 // after that StringSwitch. 90 if (ArgLower == "[in]") 91 Direction = ParamCommandComment::In; 92 else if (ArgLower == "[out]") 93 Direction = ParamCommandComment::Out; 94 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") 95 Direction = ParamCommandComment::InOut; 96 else { 97 // Remove spaces. 98 std::string::iterator O = ArgLower.begin(); 99 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end(); 100 I != E; ++I) { 101 const char C = *I; 102 if (C != ' ' && C != '\n' && C != '\r' && 103 C != '\t' && C != '\v' && C != '\f') 104 *O++ = C; 105 } 106 ArgLower.resize(O - ArgLower.begin()); 107 108 bool RemovingWhitespaceHelped = false; 109 if (ArgLower == "[in]") { 110 Direction = ParamCommandComment::In; 111 RemovingWhitespaceHelped = true; 112 } else if (ArgLower == "[out]") { 113 Direction = ParamCommandComment::Out; 114 RemovingWhitespaceHelped = true; 115 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") { 116 Direction = ParamCommandComment::InOut; 117 RemovingWhitespaceHelped = true; 118 } else { 119 Direction = ParamCommandComment::In; 120 RemovingWhitespaceHelped = false; 121 } 122 123 SourceRange ArgRange(ArgLocBegin, ArgLocEnd); 124 if (RemovingWhitespaceHelped) 125 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction) 126 << ArgRange 127 << FixItHint::CreateReplacement( 128 ArgRange, 129 ParamCommandComment::getDirectionAsString(Direction)); 130 else 131 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) 132 << ArgRange; 133 } 134 Command->setDirection(Direction, /* Explicit = */ true); 135} 136 137void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command, 138 SourceLocation ArgLocBegin, 139 SourceLocation ArgLocEnd, 140 StringRef Arg) { 141 // Parser will not feed us more arguments than needed. 142 assert(Command->getNumArgs() == 0); 143 144 if (!Command->isDirectionExplicit()) { 145 // User didn't provide a direction argument. 146 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false); 147 } 148 typedef BlockCommandComment::Argument Argument; 149 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, 150 ArgLocEnd), 151 Arg); 152 Command->setArgs(llvm::makeArrayRef(A, 1)); 153} 154 155void Sema::actOnParamCommandFinish(ParamCommandComment *Command, 156 ParagraphComment *Paragraph) { 157 Command->setParagraph(Paragraph); 158 checkBlockCommandEmptyParagraph(Command); 159} 160 161TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin, 162 SourceLocation LocEnd, 163 unsigned CommandID) { 164 TParamCommandComment *Command = 165 new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID); 166 167 if (!isTemplateOrSpecialization()) 168 Diag(Command->getLocation(), 169 diag::warn_doc_tparam_not_attached_to_a_template_decl) 170 << Command->getCommandNameRange(Traits); 171 172 return Command; 173} 174 175void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command, 176 SourceLocation ArgLocBegin, 177 SourceLocation ArgLocEnd, 178 StringRef Arg) { 179 // Parser will not feed us more arguments than needed. 180 assert(Command->getNumArgs() == 0); 181 182 typedef BlockCommandComment::Argument Argument; 183 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, 184 ArgLocEnd), 185 Arg); 186 Command->setArgs(llvm::makeArrayRef(A, 1)); 187 188 if (!isTemplateOrSpecialization()) { 189 // We already warned that this \\tparam is not attached to a template decl. 190 return; 191 } 192 193 const TemplateParameterList *TemplateParameters = 194 ThisDeclInfo->TemplateParameters; 195 SmallVector<unsigned, 2> Position; 196 if (resolveTParamReference(Arg, TemplateParameters, &Position)) { 197 Command->setPosition(copyArray(llvm::makeArrayRef(Position))); 198 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt = 199 TemplateParameterDocs.find(Arg); 200 if (PrevCommandIt != TemplateParameterDocs.end()) { 201 SourceRange ArgRange(ArgLocBegin, ArgLocEnd); 202 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate) 203 << Arg << ArgRange; 204 TParamCommandComment *PrevCommand = PrevCommandIt->second; 205 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous) 206 << PrevCommand->getParamNameRange(); 207 } 208 TemplateParameterDocs[Arg] = Command; 209 return; 210 } 211 212 SourceRange ArgRange(ArgLocBegin, ArgLocEnd); 213 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found) 214 << Arg << ArgRange; 215 216 if (!TemplateParameters || TemplateParameters->size() == 0) 217 return; 218 219 StringRef CorrectedName; 220 if (TemplateParameters->size() == 1) { 221 const NamedDecl *Param = TemplateParameters->getParam(0); 222 const IdentifierInfo *II = Param->getIdentifier(); 223 if (II) 224 CorrectedName = II->getName(); 225 } else { 226 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters); 227 } 228 229 if (!CorrectedName.empty()) { 230 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion) 231 << CorrectedName 232 << FixItHint::CreateReplacement(ArgRange, CorrectedName); 233 } 234 235 return; 236} 237 238void Sema::actOnTParamCommandFinish(TParamCommandComment *Command, 239 ParagraphComment *Paragraph) { 240 Command->setParagraph(Paragraph); 241 checkBlockCommandEmptyParagraph(Command); 242} 243 244InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, 245 SourceLocation CommandLocEnd, 246 unsigned CommandID) { 247 ArrayRef<InlineCommandComment::Argument> Args; 248 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name; 249 return new (Allocator) InlineCommandComment( 250 CommandLocBegin, 251 CommandLocEnd, 252 CommandID, 253 getInlineCommandRenderKind(CommandName), 254 Args); 255} 256 257InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, 258 SourceLocation CommandLocEnd, 259 unsigned CommandID, 260 SourceLocation ArgLocBegin, 261 SourceLocation ArgLocEnd, 262 StringRef Arg) { 263 typedef InlineCommandComment::Argument Argument; 264 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, 265 ArgLocEnd), 266 Arg); 267 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name; 268 269 return new (Allocator) InlineCommandComment( 270 CommandLocBegin, 271 CommandLocEnd, 272 CommandID, 273 getInlineCommandRenderKind(CommandName), 274 llvm::makeArrayRef(A, 1)); 275} 276 277InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, 278 SourceLocation LocEnd, 279 StringRef CommandName) { 280 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID(); 281 return actOnUnknownCommand(LocBegin, LocEnd, CommandID); 282} 283 284InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, 285 SourceLocation LocEnd, 286 unsigned CommandID) { 287 ArrayRef<InlineCommandComment::Argument> Args; 288 return new (Allocator) InlineCommandComment( 289 LocBegin, LocEnd, CommandID, 290 InlineCommandComment::RenderNormal, 291 Args); 292} 293 294TextComment *Sema::actOnText(SourceLocation LocBegin, 295 SourceLocation LocEnd, 296 StringRef Text) { 297 return new (Allocator) TextComment(LocBegin, LocEnd, Text); 298} 299 300VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc, 301 unsigned CommandID) { 302 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name; 303 return new (Allocator) VerbatimBlockComment( 304 Loc, 305 Loc.getLocWithOffset(1 + CommandName.size()), 306 CommandID); 307} 308 309VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc, 310 StringRef Text) { 311 return new (Allocator) VerbatimBlockLineComment(Loc, Text); 312} 313 314void Sema::actOnVerbatimBlockFinish( 315 VerbatimBlockComment *Block, 316 SourceLocation CloseNameLocBegin, 317 StringRef CloseName, 318 ArrayRef<VerbatimBlockLineComment *> Lines) { 319 Block->setCloseName(CloseName, CloseNameLocBegin); 320 Block->setLines(Lines); 321} 322 323VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin, 324 unsigned CommandID, 325 SourceLocation TextBegin, 326 StringRef Text) { 327 return new (Allocator) VerbatimLineComment( 328 LocBegin, 329 TextBegin.getLocWithOffset(Text.size()), 330 CommandID, 331 TextBegin, 332 Text); 333} 334 335HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin, 336 StringRef TagName) { 337 return new (Allocator) HTMLStartTagComment(LocBegin, TagName); 338} 339 340void Sema::actOnHTMLStartTagFinish( 341 HTMLStartTagComment *Tag, 342 ArrayRef<HTMLStartTagComment::Attribute> Attrs, 343 SourceLocation GreaterLoc, 344 bool IsSelfClosing) { 345 Tag->setAttrs(Attrs); 346 Tag->setGreaterLoc(GreaterLoc); 347 if (IsSelfClosing) 348 Tag->setSelfClosing(); 349 else if (!isHTMLEndTagForbidden(Tag->getTagName())) 350 HTMLOpenTags.push_back(Tag); 351} 352 353HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin, 354 SourceLocation LocEnd, 355 StringRef TagName) { 356 HTMLEndTagComment *HET = 357 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName); 358 if (isHTMLEndTagForbidden(TagName)) { 359 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden) 360 << TagName << HET->getSourceRange(); 361 return HET; 362 } 363 364 bool FoundOpen = false; 365 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator 366 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend(); 367 I != E; ++I) { 368 if ((*I)->getTagName() == TagName) { 369 FoundOpen = true; 370 break; 371 } 372 } 373 if (!FoundOpen) { 374 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced) 375 << HET->getSourceRange(); 376 return HET; 377 } 378 379 while (!HTMLOpenTags.empty()) { 380 const HTMLStartTagComment *HST = HTMLOpenTags.back(); 381 HTMLOpenTags.pop_back(); 382 StringRef LastNotClosedTagName = HST->getTagName(); 383 if (LastNotClosedTagName == TagName) 384 break; 385 386 if (isHTMLEndTagOptional(LastNotClosedTagName)) 387 continue; 388 389 bool OpenLineInvalid; 390 const unsigned OpenLine = SourceMgr.getPresumedLineNumber( 391 HST->getLocation(), 392 &OpenLineInvalid); 393 bool CloseLineInvalid; 394 const unsigned CloseLine = SourceMgr.getPresumedLineNumber( 395 HET->getLocation(), 396 &CloseLineInvalid); 397 398 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) 399 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) 400 << HST->getTagName() << HET->getTagName() 401 << HST->getSourceRange() << HET->getSourceRange(); 402 else { 403 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) 404 << HST->getTagName() << HET->getTagName() 405 << HST->getSourceRange(); 406 Diag(HET->getLocation(), diag::note_doc_html_end_tag) 407 << HET->getSourceRange(); 408 } 409 } 410 411 return HET; 412} 413 414FullComment *Sema::actOnFullComment( 415 ArrayRef<BlockContentComment *> Blocks) { 416 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo); 417 resolveParamCommandIndexes(FC); 418 return FC; 419} 420 421void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) { 422 if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed) 423 return; 424 425 ParagraphComment *Paragraph = Command->getParagraph(); 426 if (Paragraph->isWhitespace()) { 427 SourceLocation DiagLoc; 428 if (Command->getNumArgs() > 0) 429 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd(); 430 if (!DiagLoc.isValid()) 431 DiagLoc = Command->getCommandNameRange(Traits).getEnd(); 432 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph) 433 << Command->getCommandName(Traits) 434 << Command->getSourceRange(); 435 } 436} 437 438void Sema::checkReturnsCommand(const BlockCommandComment *Command) { 439 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand) 440 return; 441 if (isFunctionDecl()) { 442 if (ThisDeclInfo->ResultType->isVoidType()) { 443 unsigned DiagKind; 444 switch (ThisDeclInfo->CommentDecl->getKind()) { 445 default: 446 if (ThisDeclInfo->IsObjCMethod) 447 DiagKind = 3; 448 else 449 DiagKind = 0; 450 break; 451 case Decl::CXXConstructor: 452 DiagKind = 1; 453 break; 454 case Decl::CXXDestructor: 455 DiagKind = 2; 456 break; 457 } 458 Diag(Command->getLocation(), 459 diag::warn_doc_returns_attached_to_a_void_function) 460 << Command->getCommandName(Traits) 461 << DiagKind 462 << Command->getSourceRange(); 463 } 464 return; 465 } 466 Diag(Command->getLocation(), 467 diag::warn_doc_returns_not_attached_to_a_function_decl) 468 << Command->getCommandName(Traits) 469 << Command->getSourceRange(); 470} 471 472void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) { 473 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID()); 474 const BlockCommandComment *PrevCommand = NULL; 475 if (Info->IsBriefCommand) { 476 if (!BriefCommand) { 477 BriefCommand = Command; 478 return; 479 } 480 PrevCommand = BriefCommand; 481 } else if (Info->IsReturnsCommand) { 482 if (!ReturnsCommand) { 483 ReturnsCommand = Command; 484 return; 485 } 486 PrevCommand = ReturnsCommand; 487 } else { 488 // We don't want to check this command for duplicates. 489 return; 490 } 491 StringRef CommandName = Command->getCommandName(Traits); 492 StringRef PrevCommandName = PrevCommand->getCommandName(Traits); 493 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate) 494 << CommandName 495 << Command->getSourceRange(); 496 if (CommandName == PrevCommandName) 497 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous) 498 << PrevCommandName 499 << PrevCommand->getSourceRange(); 500 else 501 Diag(PrevCommand->getLocation(), 502 diag::note_doc_block_command_previous_alias) 503 << PrevCommandName 504 << CommandName; 505} 506 507void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) { 508 if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand) 509 return; 510 511 const Decl *D = ThisDeclInfo->CommentDecl; 512 if (!D) 513 return; 514 515 if (D->hasAttr<DeprecatedAttr>() || 516 D->hasAttr<AvailabilityAttr>() || 517 D->hasAttr<UnavailableAttr>()) 518 return; 519 520 Diag(Command->getLocation(), 521 diag::warn_doc_deprecated_not_sync) 522 << Command->getSourceRange(); 523 524 // Try to emit a fixit with a deprecation attribute. 525 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 526 // Don't emit a Fix-It for non-member function definitions. GCC does not 527 // accept attributes on them. 528 const DeclContext *Ctx = FD->getDeclContext(); 529 if ((!Ctx || !Ctx->isRecord()) && 530 FD->doesThisDeclarationHaveABody()) 531 return; 532 533 StringRef AttributeSpelling = "__attribute__((deprecated))"; 534 if (PP) { 535 TokenValue Tokens[] = { 536 tok::kw___attribute, tok::l_paren, tok::l_paren, 537 PP->getIdentifierInfo("deprecated"), 538 tok::r_paren, tok::r_paren 539 }; 540 StringRef MacroName = PP->getLastMacroWithSpelling(FD->getLocation(), 541 Tokens); 542 if (!MacroName.empty()) 543 AttributeSpelling = MacroName; 544 } 545 546 SmallString<64> TextToInsert(" "); 547 TextToInsert += AttributeSpelling; 548 Diag(FD->getLocEnd(), 549 diag::note_add_deprecation_attr) 550 << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1), 551 TextToInsert); 552 } 553} 554 555void Sema::resolveParamCommandIndexes(const FullComment *FC) { 556 if (!isFunctionDecl()) { 557 // We already warned that \\param commands are not attached to a function 558 // decl. 559 return; 560 } 561 562 llvm::SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands; 563 564 // Comment AST nodes that correspond to \c ParamVars for which we have 565 // found a \\param command or NULL if no documentation was found so far. 566 llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs; 567 568 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars(); 569 ParamVarDocs.resize(ParamVars.size(), NULL); 570 571 // First pass over all \\param commands: resolve all parameter names. 572 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end(); 573 I != E; ++I) { 574 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I); 575 if (!PCC || !PCC->hasParamName()) 576 continue; 577 StringRef ParamName = PCC->getParamNameAsWritten(); 578 579 // Check that referenced parameter name is in the function decl. 580 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName, 581 ParamVars); 582 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) { 583 UnresolvedParamCommands.push_back(PCC); 584 continue; 585 } 586 PCC->setParamIndex(ResolvedParamIndex); 587 if (ParamVarDocs[ResolvedParamIndex]) { 588 SourceRange ArgRange = PCC->getParamNameRange(); 589 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate) 590 << ParamName << ArgRange; 591 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex]; 592 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous) 593 << PrevCommand->getParamNameRange(); 594 } 595 ParamVarDocs[ResolvedParamIndex] = PCC; 596 } 597 598 // Find parameter declarations that have no corresponding \\param. 599 llvm::SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls; 600 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) { 601 if (!ParamVarDocs[i]) 602 OrphanedParamDecls.push_back(ParamVars[i]); 603 } 604 605 // Second pass over unresolved \\param commands: do typo correction. 606 // Suggest corrections from a set of parameter declarations that have no 607 // corresponding \\param. 608 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) { 609 const ParamCommandComment *PCC = UnresolvedParamCommands[i]; 610 611 SourceRange ArgRange = PCC->getParamNameRange(); 612 StringRef ParamName = PCC->getParamNameAsWritten(); 613 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found) 614 << ParamName << ArgRange; 615 616 // All parameters documented -- can't suggest a correction. 617 if (OrphanedParamDecls.size() == 0) 618 continue; 619 620 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex; 621 if (OrphanedParamDecls.size() == 1) { 622 // If one parameter is not documented then that parameter is the only 623 // possible suggestion. 624 CorrectedParamIndex = 0; 625 } else { 626 // Do typo correction. 627 CorrectedParamIndex = correctTypoInParmVarReference(ParamName, 628 OrphanedParamDecls); 629 } 630 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) { 631 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex]; 632 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier()) 633 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion) 634 << CorrectedII->getName() 635 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName()); 636 } 637 } 638} 639 640bool Sema::isFunctionDecl() { 641 if (!ThisDeclInfo) 642 return false; 643 if (!ThisDeclInfo->IsFilled) 644 inspectThisDecl(); 645 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind; 646} 647 648bool Sema::isTemplateOrSpecialization() { 649 if (!ThisDeclInfo) 650 return false; 651 if (!ThisDeclInfo->IsFilled) 652 inspectThisDecl(); 653 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate; 654} 655 656ArrayRef<const ParmVarDecl *> Sema::getParamVars() { 657 if (!ThisDeclInfo->IsFilled) 658 inspectThisDecl(); 659 return ThisDeclInfo->ParamVars; 660} 661 662void Sema::inspectThisDecl() { 663 ThisDeclInfo->fill(); 664} 665 666unsigned Sema::resolveParmVarReference(StringRef Name, 667 ArrayRef<const ParmVarDecl *> ParamVars) { 668 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { 669 const IdentifierInfo *II = ParamVars[i]->getIdentifier(); 670 if (II && II->getName() == Name) 671 return i; 672 } 673 return ParamCommandComment::InvalidParamIndex; 674} 675 676namespace { 677class SimpleTypoCorrector { 678 StringRef Typo; 679 const unsigned MaxEditDistance; 680 681 const NamedDecl *BestDecl; 682 unsigned BestEditDistance; 683 unsigned BestIndex; 684 unsigned NextIndex; 685 686public: 687 SimpleTypoCorrector(StringRef Typo) : 688 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3), 689 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1), 690 BestIndex(0), NextIndex(0) 691 { } 692 693 void addDecl(const NamedDecl *ND); 694 695 const NamedDecl *getBestDecl() const { 696 if (BestEditDistance > MaxEditDistance) 697 return NULL; 698 699 return BestDecl; 700 } 701 702 unsigned getBestDeclIndex() const { 703 assert(getBestDecl()); 704 return BestIndex; 705 } 706}; 707 708void SimpleTypoCorrector::addDecl(const NamedDecl *ND) { 709 unsigned CurrIndex = NextIndex++; 710 711 const IdentifierInfo *II = ND->getIdentifier(); 712 if (!II) 713 return; 714 715 StringRef Name = II->getName(); 716 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size()); 717 if (MinPossibleEditDistance > 0 && 718 Typo.size() / MinPossibleEditDistance < 3) 719 return; 720 721 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); 722 if (EditDistance < BestEditDistance) { 723 BestEditDistance = EditDistance; 724 BestDecl = ND; 725 BestIndex = CurrIndex; 726 } 727} 728} // unnamed namespace 729 730unsigned Sema::correctTypoInParmVarReference( 731 StringRef Typo, 732 ArrayRef<const ParmVarDecl *> ParamVars) { 733 SimpleTypoCorrector Corrector(Typo); 734 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) 735 Corrector.addDecl(ParamVars[i]); 736 if (Corrector.getBestDecl()) 737 return Corrector.getBestDeclIndex(); 738 else 739 return ParamCommandComment::InvalidParamIndex; 740} 741 742namespace { 743bool ResolveTParamReferenceHelper( 744 StringRef Name, 745 const TemplateParameterList *TemplateParameters, 746 SmallVectorImpl<unsigned> *Position) { 747 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { 748 const NamedDecl *Param = TemplateParameters->getParam(i); 749 const IdentifierInfo *II = Param->getIdentifier(); 750 if (II && II->getName() == Name) { 751 Position->push_back(i); 752 return true; 753 } 754 755 if (const TemplateTemplateParmDecl *TTP = 756 dyn_cast<TemplateTemplateParmDecl>(Param)) { 757 Position->push_back(i); 758 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), 759 Position)) 760 return true; 761 Position->pop_back(); 762 } 763 } 764 return false; 765} 766} // unnamed namespace 767 768bool Sema::resolveTParamReference( 769 StringRef Name, 770 const TemplateParameterList *TemplateParameters, 771 SmallVectorImpl<unsigned> *Position) { 772 Position->clear(); 773 if (!TemplateParameters) 774 return false; 775 776 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position); 777} 778 779namespace { 780void CorrectTypoInTParamReferenceHelper( 781 const TemplateParameterList *TemplateParameters, 782 SimpleTypoCorrector &Corrector) { 783 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { 784 const NamedDecl *Param = TemplateParameters->getParam(i); 785 Corrector.addDecl(Param); 786 787 if (const TemplateTemplateParmDecl *TTP = 788 dyn_cast<TemplateTemplateParmDecl>(Param)) 789 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), 790 Corrector); 791 } 792} 793} // unnamed namespace 794 795StringRef Sema::correctTypoInTParamReference( 796 StringRef Typo, 797 const TemplateParameterList *TemplateParameters) { 798 SimpleTypoCorrector Corrector(Typo); 799 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector); 800 if (const NamedDecl *ND = Corrector.getBestDecl()) { 801 const IdentifierInfo *II = ND->getIdentifier(); 802 assert(II && "SimpleTypoCorrector should not return this decl"); 803 return II->getName(); 804 } 805 return StringRef(); 806} 807 808InlineCommandComment::RenderKind 809Sema::getInlineCommandRenderKind(StringRef Name) const { 810 assert(Traits.getCommandInfo(Name)->IsInlineCommand); 811 812 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name) 813 .Case("b", InlineCommandComment::RenderBold) 814 .Cases("c", "p", InlineCommandComment::RenderMonospaced) 815 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized) 816 .Default(InlineCommandComment::RenderNormal); 817} 818 819} // end namespace comments 820} // end namespace clang 821 822