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 18 namespace clang { 19 namespace comments { 20 21 namespace { 22 #include "clang/AST/CommentHTMLTagsProperties.inc" 23 } // unnamed namespace 24 25 Sema::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 31 void 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 40 ParagraphComment *Sema::actOnParagraphComment( 41 ArrayRef<InlineContentComment *> Content) { 42 return new (Allocator) ParagraphComment(Content); 43 } 44 45 BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin, 46 SourceLocation LocEnd, 47 unsigned CommandID) { 48 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID); 49 } 50 51 void Sema::actOnBlockCommandArgs(BlockCommandComment *Command, 52 ArrayRef<BlockCommandComment::Argument> Args) { 53 Command->setArgs(Args); 54 } 55 56 void Sema::actOnBlockCommandFinish(BlockCommandComment *Command, 57 ParagraphComment *Paragraph) { 58 Command->setParagraph(Paragraph); 59 checkBlockCommandEmptyParagraph(Command); 60 checkBlockCommandDuplicate(Command); 61 checkReturnsCommand(Command); 62 } 63 64 ParamCommandComment *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 78 void 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 133 void 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 151 void Sema::actOnParamCommandFinish(ParamCommandComment *Command, 152 ParagraphComment *Paragraph) { 153 Command->setParagraph(Paragraph); 154 checkBlockCommandEmptyParagraph(Command); 155 } 156 157 TParamCommandComment *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 171 void 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 234 void Sema::actOnTParamCommandFinish(TParamCommandComment *Command, 235 ParagraphComment *Paragraph) { 236 Command->setParagraph(Paragraph); 237 checkBlockCommandEmptyParagraph(Command); 238 } 239 240 InlineCommandComment *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 253 InlineCommandComment *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 273 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, 274 SourceLocation LocEnd, 275 StringRef CommandName) { 276 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID(); 277 return actOnUnknownCommand(LocBegin, LocEnd, CommandID); 278 } 279 280 InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, 281 SourceLocation LocEnd, 282 unsigned CommandID) { 283 ArrayRef<InlineCommandComment::Argument> Args; 284 return new (Allocator) InlineCommandComment( 285 LocBegin, LocEnd, CommandID, 286 InlineCommandComment::RenderNormal, 287 Args); 288 } 289 290 TextComment *Sema::actOnText(SourceLocation LocBegin, 291 SourceLocation LocEnd, 292 StringRef Text) { 293 return new (Allocator) TextComment(LocBegin, LocEnd, Text); 294 } 295 296 VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc, 297 unsigned CommandID) { 298 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name; 299 return new (Allocator) VerbatimBlockComment( 300 Loc, 301 Loc.getLocWithOffset(1 + CommandName.size()), 302 CommandID); 303 } 304 305 VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc, 306 StringRef Text) { 307 return new (Allocator) VerbatimBlockLineComment(Loc, Text); 308 } 309 310 void Sema::actOnVerbatimBlockFinish( 311 VerbatimBlockComment *Block, 312 SourceLocation CloseNameLocBegin, 313 StringRef CloseName, 314 ArrayRef<VerbatimBlockLineComment *> Lines) { 315 Block->setCloseName(CloseName, CloseNameLocBegin); 316 Block->setLines(Lines); 317 } 318 319 VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin, 320 unsigned CommandID, 321 SourceLocation TextBegin, 322 StringRef Text) { 323 return new (Allocator) VerbatimLineComment( 324 LocBegin, 325 TextBegin.getLocWithOffset(Text.size()), 326 CommandID, 327 TextBegin, 328 Text); 329 } 330 331 HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin, 332 StringRef TagName) { 333 return new (Allocator) HTMLStartTagComment(LocBegin, TagName); 334 } 335 336 void Sema::actOnHTMLStartTagFinish( 337 HTMLStartTagComment *Tag, 338 ArrayRef<HTMLStartTagComment::Attribute> Attrs, 339 SourceLocation GreaterLoc, 340 bool IsSelfClosing) { 341 Tag->setAttrs(Attrs); 342 Tag->setGreaterLoc(GreaterLoc); 343 if (IsSelfClosing) 344 Tag->setSelfClosing(); 345 else if (!isHTMLEndTagForbidden(Tag->getTagName())) 346 HTMLOpenTags.push_back(Tag); 347 } 348 349 HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin, 350 SourceLocation LocEnd, 351 StringRef TagName) { 352 HTMLEndTagComment *HET = 353 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName); 354 if (isHTMLEndTagForbidden(TagName)) { 355 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden) 356 << TagName << HET->getSourceRange(); 357 return HET; 358 } 359 360 bool FoundOpen = false; 361 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator 362 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend(); 363 I != E; ++I) { 364 if ((*I)->getTagName() == TagName) { 365 FoundOpen = true; 366 break; 367 } 368 } 369 if (!FoundOpen) { 370 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced) 371 << HET->getSourceRange(); 372 return HET; 373 } 374 375 while (!HTMLOpenTags.empty()) { 376 const HTMLStartTagComment *HST = HTMLOpenTags.back(); 377 HTMLOpenTags.pop_back(); 378 StringRef LastNotClosedTagName = HST->getTagName(); 379 if (LastNotClosedTagName == TagName) 380 break; 381 382 if (isHTMLEndTagOptional(LastNotClosedTagName)) 383 continue; 384 385 bool OpenLineInvalid; 386 const unsigned OpenLine = SourceMgr.getPresumedLineNumber( 387 HST->getLocation(), 388 &OpenLineInvalid); 389 bool CloseLineInvalid; 390 const unsigned CloseLine = SourceMgr.getPresumedLineNumber( 391 HET->getLocation(), 392 &CloseLineInvalid); 393 394 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) 395 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) 396 << HST->getTagName() << HET->getTagName() 397 << HST->getSourceRange() << HET->getSourceRange(); 398 else { 399 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) 400 << HST->getTagName() << HET->getTagName() 401 << HST->getSourceRange(); 402 Diag(HET->getLocation(), diag::note_doc_html_end_tag) 403 << HET->getSourceRange(); 404 } 405 } 406 407 return HET; 408 } 409 410 FullComment *Sema::actOnFullComment( 411 ArrayRef<BlockContentComment *> Blocks) { 412 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo); 413 resolveParamCommandIndexes(FC); 414 return FC; 415 } 416 417 void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) { 418 if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed) 419 return; 420 421 ParagraphComment *Paragraph = Command->getParagraph(); 422 if (Paragraph->isWhitespace()) { 423 SourceLocation DiagLoc; 424 if (Command->getNumArgs() > 0) 425 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd(); 426 if (!DiagLoc.isValid()) 427 DiagLoc = Command->getCommandNameRange(Traits).getEnd(); 428 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph) 429 << Command->getCommandName(Traits) 430 << Command->getSourceRange(); 431 } 432 } 433 434 void Sema::checkReturnsCommand(const BlockCommandComment *Command) { 435 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand) 436 return; 437 if (isFunctionDecl()) { 438 if (ThisDeclInfo->ResultType->isVoidType()) { 439 unsigned DiagKind; 440 switch (ThisDeclInfo->ThisDecl->getKind()) { 441 default: 442 if (ThisDeclInfo->IsObjCMethod) 443 DiagKind = 3; 444 else 445 DiagKind = 0; 446 break; 447 case Decl::CXXConstructor: 448 DiagKind = 1; 449 break; 450 case Decl::CXXDestructor: 451 DiagKind = 2; 452 break; 453 } 454 Diag(Command->getLocation(), 455 diag::warn_doc_returns_attached_to_a_void_function) 456 << Command->getCommandName(Traits) 457 << DiagKind 458 << Command->getSourceRange(); 459 } 460 return; 461 } 462 Diag(Command->getLocation(), 463 diag::warn_doc_returns_not_attached_to_a_function_decl) 464 << Command->getCommandName(Traits) 465 << Command->getSourceRange(); 466 } 467 468 void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) { 469 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID()); 470 const BlockCommandComment *PrevCommand = NULL; 471 if (Info->IsBriefCommand) { 472 if (!BriefCommand) { 473 BriefCommand = Command; 474 return; 475 } 476 PrevCommand = BriefCommand; 477 } else if (Info->IsReturnsCommand) { 478 if (!ReturnsCommand) { 479 ReturnsCommand = Command; 480 return; 481 } 482 PrevCommand = ReturnsCommand; 483 } else { 484 // We don't want to check this command for duplicates. 485 return; 486 } 487 StringRef CommandName = Command->getCommandName(Traits); 488 StringRef PrevCommandName = PrevCommand->getCommandName(Traits); 489 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate) 490 << CommandName 491 << Command->getSourceRange(); 492 if (CommandName == PrevCommandName) 493 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous) 494 << PrevCommandName 495 << PrevCommand->getSourceRange(); 496 else 497 Diag(PrevCommand->getLocation(), 498 diag::note_doc_block_command_previous_alias) 499 << PrevCommandName 500 << CommandName; 501 } 502 503 void Sema::resolveParamCommandIndexes(const FullComment *FC) { 504 if (!isFunctionDecl()) { 505 // We already warned that \\param commands are not attached to a function 506 // decl. 507 return; 508 } 509 510 llvm::SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands; 511 512 // Comment AST nodes that correspond to \c ParamVars for which we have 513 // found a \\param command or NULL if no documentation was found so far. 514 llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs; 515 516 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars(); 517 ParamVarDocs.resize(ParamVars.size(), NULL); 518 519 // First pass over all \\param commands: resolve all parameter names. 520 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end(); 521 I != E; ++I) { 522 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I); 523 if (!PCC || !PCC->hasParamName()) 524 continue; 525 StringRef ParamName = PCC->getParamName(); 526 527 // Check that referenced parameter name is in the function decl. 528 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName, 529 ParamVars); 530 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) { 531 UnresolvedParamCommands.push_back(PCC); 532 continue; 533 } 534 PCC->setParamIndex(ResolvedParamIndex); 535 if (ParamVarDocs[ResolvedParamIndex]) { 536 SourceRange ArgRange = PCC->getParamNameRange(); 537 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate) 538 << ParamName << ArgRange; 539 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex]; 540 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous) 541 << PrevCommand->getParamNameRange(); 542 } 543 ParamVarDocs[ResolvedParamIndex] = PCC; 544 } 545 546 // Find parameter declarations that have no corresponding \\param. 547 llvm::SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls; 548 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) { 549 if (!ParamVarDocs[i]) 550 OrphanedParamDecls.push_back(ParamVars[i]); 551 } 552 553 // Second pass over unresolved \\param commands: do typo correction. 554 // Suggest corrections from a set of parameter declarations that have no 555 // corresponding \\param. 556 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) { 557 const ParamCommandComment *PCC = UnresolvedParamCommands[i]; 558 559 SourceRange ArgRange = PCC->getParamNameRange(); 560 StringRef ParamName = PCC->getParamName(); 561 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found) 562 << ParamName << ArgRange; 563 564 // All parameters documented -- can't suggest a correction. 565 if (OrphanedParamDecls.size() == 0) 566 continue; 567 568 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex; 569 if (OrphanedParamDecls.size() == 1) { 570 // If one parameter is not documented then that parameter is the only 571 // possible suggestion. 572 CorrectedParamIndex = 0; 573 } else { 574 // Do typo correction. 575 CorrectedParamIndex = correctTypoInParmVarReference(ParamName, 576 OrphanedParamDecls); 577 } 578 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) { 579 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex]; 580 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier()) 581 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion) 582 << CorrectedII->getName() 583 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName()); 584 } 585 } 586 } 587 588 bool Sema::isFunctionDecl() { 589 if (!ThisDeclInfo) 590 return false; 591 if (!ThisDeclInfo->IsFilled) 592 inspectThisDecl(); 593 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind; 594 } 595 596 bool Sema::isTemplateOrSpecialization() { 597 if (!ThisDeclInfo) 598 return false; 599 if (!ThisDeclInfo->IsFilled) 600 inspectThisDecl(); 601 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate; 602 } 603 604 ArrayRef<const ParmVarDecl *> Sema::getParamVars() { 605 if (!ThisDeclInfo->IsFilled) 606 inspectThisDecl(); 607 return ThisDeclInfo->ParamVars; 608 } 609 610 void Sema::inspectThisDecl() { 611 ThisDeclInfo->fill(); 612 } 613 614 unsigned Sema::resolveParmVarReference(StringRef Name, 615 ArrayRef<const ParmVarDecl *> ParamVars) { 616 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { 617 const IdentifierInfo *II = ParamVars[i]->getIdentifier(); 618 if (II && II->getName() == Name) 619 return i; 620 } 621 return ParamCommandComment::InvalidParamIndex; 622 } 623 624 namespace { 625 class SimpleTypoCorrector { 626 StringRef Typo; 627 const unsigned MaxEditDistance; 628 629 const NamedDecl *BestDecl; 630 unsigned BestEditDistance; 631 unsigned BestIndex; 632 unsigned NextIndex; 633 634 public: 635 SimpleTypoCorrector(StringRef Typo) : 636 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3), 637 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1), 638 BestIndex(0), NextIndex(0) 639 { } 640 641 void addDecl(const NamedDecl *ND); 642 643 const NamedDecl *getBestDecl() const { 644 if (BestEditDistance > MaxEditDistance) 645 return NULL; 646 647 return BestDecl; 648 } 649 650 unsigned getBestDeclIndex() const { 651 assert(getBestDecl()); 652 return BestIndex; 653 } 654 }; 655 656 void SimpleTypoCorrector::addDecl(const NamedDecl *ND) { 657 unsigned CurrIndex = NextIndex++; 658 659 const IdentifierInfo *II = ND->getIdentifier(); 660 if (!II) 661 return; 662 663 StringRef Name = II->getName(); 664 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size()); 665 if (MinPossibleEditDistance > 0 && 666 Typo.size() / MinPossibleEditDistance < 3) 667 return; 668 669 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); 670 if (EditDistance < BestEditDistance) { 671 BestEditDistance = EditDistance; 672 BestDecl = ND; 673 BestIndex = CurrIndex; 674 } 675 } 676 } // unnamed namespace 677 678 unsigned Sema::correctTypoInParmVarReference( 679 StringRef Typo, 680 ArrayRef<const ParmVarDecl *> ParamVars) { 681 SimpleTypoCorrector Corrector(Typo); 682 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) 683 Corrector.addDecl(ParamVars[i]); 684 if (Corrector.getBestDecl()) 685 return Corrector.getBestDeclIndex(); 686 else 687 return ParamCommandComment::InvalidParamIndex; 688 } 689 690 namespace { 691 bool ResolveTParamReferenceHelper( 692 StringRef Name, 693 const TemplateParameterList *TemplateParameters, 694 SmallVectorImpl<unsigned> *Position) { 695 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { 696 const NamedDecl *Param = TemplateParameters->getParam(i); 697 const IdentifierInfo *II = Param->getIdentifier(); 698 if (II && II->getName() == Name) { 699 Position->push_back(i); 700 return true; 701 } 702 703 if (const TemplateTemplateParmDecl *TTP = 704 dyn_cast<TemplateTemplateParmDecl>(Param)) { 705 Position->push_back(i); 706 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), 707 Position)) 708 return true; 709 Position->pop_back(); 710 } 711 } 712 return false; 713 } 714 } // unnamed namespace 715 716 bool Sema::resolveTParamReference( 717 StringRef Name, 718 const TemplateParameterList *TemplateParameters, 719 SmallVectorImpl<unsigned> *Position) { 720 Position->clear(); 721 if (!TemplateParameters) 722 return false; 723 724 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position); 725 } 726 727 namespace { 728 void CorrectTypoInTParamReferenceHelper( 729 const TemplateParameterList *TemplateParameters, 730 SimpleTypoCorrector &Corrector) { 731 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { 732 const NamedDecl *Param = TemplateParameters->getParam(i); 733 Corrector.addDecl(Param); 734 735 if (const TemplateTemplateParmDecl *TTP = 736 dyn_cast<TemplateTemplateParmDecl>(Param)) 737 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), 738 Corrector); 739 } 740 } 741 } // unnamed namespace 742 743 StringRef Sema::correctTypoInTParamReference( 744 StringRef Typo, 745 const TemplateParameterList *TemplateParameters) { 746 SimpleTypoCorrector Corrector(Typo); 747 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector); 748 if (const NamedDecl *ND = Corrector.getBestDecl()) { 749 const IdentifierInfo *II = ND->getIdentifier(); 750 assert(II && "SimpleTypoCorrector should not return this decl"); 751 return II->getName(); 752 } 753 return StringRef(); 754 } 755 756 InlineCommandComment::RenderKind 757 Sema::getInlineCommandRenderKind(StringRef Name) const { 758 assert(Traits.getCommandInfo(Name)->IsInlineCommand); 759 760 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name) 761 .Case("b", InlineCommandComment::RenderBold) 762 .Cases("c", "p", InlineCommandComment::RenderMonospaced) 763 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized) 764 .Default(InlineCommandComment::RenderNormal); 765 } 766 767 } // end namespace comments 768 } // end namespace clang 769 770