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