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