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