18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/ADT/StringSwitch.h"
25 #include "clang/AST/CommentHTMLTagsProperties.inc"
28 Sema::Sema(llvm::BumpPtrAllocator &Allocator,
const SourceManager &SourceMgr,
31 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
32 PP(PP), ThisDeclInfo(nullptr), BriefCommand(nullptr),
33 HeaderfileCommand(nullptr) {
40 ThisDeclInfo =
new (Allocator)
DeclInfo;
58 checkContainerDecl(BC);
70 checkBlockCommandEmptyParagraph(Command);
71 checkBlockCommandDuplicate(Command);
75 checkReturnsCommand(Command);
76 checkDeprecatedCommand(Command);
89 if (!involvesFunctionType())
91 diag::warn_doc_param_not_attached_to_a_function_decl)
104 switch (
Comment->getCommandID()) {
105 case CommandTraits::KCI_function:
106 DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 1 : 0;
108 case CommandTraits::KCI_functiongroup:
109 DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 2 : 0;
111 case CommandTraits::KCI_method:
112 DiagSelect = !isObjCMethodDecl() ? 3 : 0;
114 case CommandTraits::KCI_methodgroup:
115 DiagSelect = !isObjCMethodDecl() ? 4 : 0;
117 case CommandTraits::KCI_callback:
118 DiagSelect = !isFunctionPointerVarDecl() ? 5 : 0;
125 Diag(Comment->
getLocation(), diag::warn_doc_function_method_decl_mismatch)
127 << (DiagSelect-1) << (DiagSelect-1)
131 void Sema::checkContainerDeclVerbatimLine(
const BlockCommandComment *Comment) {
132 const CommandInfo *Info = Traits.
getCommandInfo(Comment->getCommandID());
133 if (!Info->IsRecordLikeDeclarationCommand)
136 switch (Comment->getCommandID()) {
137 case CommandTraits::KCI_class:
139 (!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl()) ? 1
144 if (DiagSelect && Comment->getCommandMarker() && isObjCInterfaceDecl())
147 case CommandTraits::KCI_interface:
148 DiagSelect = !isObjCInterfaceDecl() ? 2 : 0;
150 case CommandTraits::KCI_protocol:
151 DiagSelect = !isObjCProtocolDecl() ? 3 : 0;
153 case CommandTraits::KCI_struct:
154 DiagSelect = !isClassOrStructOrTagTypedefDecl() ? 4 : 0;
156 case CommandTraits::KCI_union:
157 DiagSelect = !isUnionDecl() ? 5 : 0;
164 Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
165 << Comment->getCommandMarker()
166 << (DiagSelect-1) << (DiagSelect-1)
167 << Comment->getSourceRange();
170 void Sema::checkContainerDecl(
const BlockCommandComment *Comment) {
171 const CommandInfo *Info = Traits.
getCommandInfo(Comment->getCommandID());
172 if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
175 switch (Comment->getCommandID()) {
176 case CommandTraits::KCI_classdesign:
179 case CommandTraits::KCI_coclass:
182 case CommandTraits::KCI_dependency:
185 case CommandTraits::KCI_helper:
188 case CommandTraits::KCI_helperclass:
191 case CommandTraits::KCI_helps:
194 case CommandTraits::KCI_instancesize:
197 case CommandTraits::KCI_ownership:
200 case CommandTraits::KCI_performance:
203 case CommandTraits::KCI_security:
206 case CommandTraits::KCI_superclass:
214 Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
215 << Comment->getCommandMarker()
217 << Comment->getSourceRange();
223 return llvm::StringSwitch<int>(Arg)
237 if (Direction == -1) {
243 if (Direction != -1) {
246 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
249 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange;
268 auto *A =
new (Allocator)
270 Command->
setArgs(llvm::makeArrayRef(A, 1));
276 checkBlockCommandEmptyParagraph(Command);
288 if (!isTemplateOrSpecialization())
290 diag::warn_doc_tparam_not_attached_to_a_template_decl)
304 auto *A =
new (Allocator)
306 Command->
setArgs(llvm::makeArrayRef(A, 1));
308 if (!isTemplateOrSpecialization()) {
316 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
321 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
323 Diag(PrevCommand->
getLocation(), diag::note_doc_tparam_previous)
326 PrevCommand = Command;
331 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
334 if (!TemplateParameters || TemplateParameters->
size() == 0)
337 StringRef CorrectedName;
338 if (TemplateParameters->
size() == 1) {
344 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
347 if (!CorrectedName.empty()) {
348 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
357 checkBlockCommandEmptyParagraph(Command);
366 return new (Allocator)
368 getInlineCommandRenderKind(CommandName), Args);
373 StringRef CommandName) {
380 unsigned CommandID) {
383 LocBegin, LocEnd, CommandID,
395 unsigned CommandID) {
413 Block->setCloseName(CloseName, CloseNameLocBegin);
414 Block->setLines(Lines);
427 checkFunctionDeclVerbatimLine(VL);
428 checkContainerDeclVerbatimLine(VL);
441 bool IsSelfClosing) {
446 else if (!isHTMLEndTagForbidden(Tag->
getTagName()))
447 HTMLOpenTags.push_back(Tag);
455 if (isHTMLEndTagForbidden(TagName)) {
456 Diag(HET->
getLocation(), diag::warn_doc_html_end_forbidden)
462 bool FoundOpen =
false;
464 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
466 if ((*I)->getTagName() == TagName) {
472 Diag(HET->
getLocation(), diag::warn_doc_html_end_unbalanced)
478 while (!HTMLOpenTags.empty()) {
480 StringRef LastNotClosedTagName = HST->
getTagName();
481 if (LastNotClosedTagName == TagName) {
488 if (isHTMLEndTagOptional(LastNotClosedTagName))
491 bool OpenLineInvalid;
495 bool CloseLineInvalid;
500 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
501 Diag(HST->
getLocation(), diag::warn_doc_html_start_end_mismatch)
506 Diag(HST->
getLocation(), diag::warn_doc_html_start_end_mismatch)
509 Diag(HET->
getLocation(), diag::note_doc_html_end_tag)
521 resolveParamCommandIndexes(FC);
524 while (!HTMLOpenTags.empty()) {
529 Diag(HST->
getLocation(), diag::warn_doc_html_missing_end_tag)
548 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
555 void Sema::checkReturnsCommand(
const BlockCommandComment *Command) {
559 assert(ThisDeclInfo &&
"should not call this check on a bare comment");
563 if (isObjCPropertyDecl())
565 if (involvesFunctionType()) {
567 "should have a valid return type");
577 case Decl::CXXConstructor:
580 case Decl::CXXDestructor:
584 Diag(Command->getLocation(),
585 diag::warn_doc_returns_attached_to_a_void_function)
586 << Command->getCommandMarker()
587 << Command->getCommandName(Traits)
589 << Command->getSourceRange();
594 Diag(Command->getLocation(),
595 diag::warn_doc_returns_not_attached_to_a_function_decl)
596 << Command->getCommandMarker()
597 << Command->getCommandName(Traits)
598 << Command->getSourceRange();
601 void Sema::checkBlockCommandDuplicate(
const BlockCommandComment *Command) {
602 const CommandInfo *Info = Traits.
getCommandInfo(Command->getCommandID());
603 const BlockCommandComment *PrevCommand =
nullptr;
604 if (Info->IsBriefCommand) {
606 BriefCommand = Command;
609 PrevCommand = BriefCommand;
610 }
else if (Info->IsHeaderfileCommand) {
611 if (!HeaderfileCommand) {
612 HeaderfileCommand = Command;
615 PrevCommand = HeaderfileCommand;
621 StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
622 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
623 << Command->getCommandMarker()
625 << Command->getSourceRange();
626 if (CommandName == PrevCommandName)
627 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
628 << PrevCommand->getCommandMarker()
630 << PrevCommand->getSourceRange();
632 Diag(PrevCommand->getLocation(),
633 diag::note_doc_block_command_previous_alias)
634 << PrevCommand->getCommandMarker()
639 void Sema::checkDeprecatedCommand(
const BlockCommandComment *Command) {
643 assert(ThisDeclInfo &&
"should not call this check on a bare comment");
649 if (D->hasAttr<DeprecatedAttr>() ||
650 D->hasAttr<AvailabilityAttr>() ||
651 D->hasAttr<UnavailableAttr>())
654 Diag(Command->getLocation(), diag::warn_doc_deprecated_not_sync)
655 << Command->getSourceRange() << Command->getCommandMarker();
658 if (
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
661 const DeclContext *Ctx = FD->getDeclContext();
662 if ((!Ctx || !Ctx->isRecord()) &&
663 FD->doesThisDeclarationHaveABody())
666 const LangOptions &LO = FD->getLangOpts();
667 const bool DoubleSquareBracket = LO.CPlusPlus14 || LO.C2x;
668 StringRef AttributeSpelling =
669 DoubleSquareBracket ?
"[[deprecated]]" :
"__attribute__((deprecated))";
675 if (DoubleSquareBracket) {
676 TokenValue Tokens[] = {tok::l_square, tok::l_square,
678 tok::r_square, tok::r_square};
680 if (!MacroName.empty())
681 AttributeSpelling = MacroName;
684 if (MacroName.empty()) {
685 TokenValue Tokens[] = {
686 tok::kw___attribute, tok::l_paren,
688 tok::r_paren, tok::r_paren};
689 StringRef MacroName =
691 if (!MacroName.empty())
692 AttributeSpelling = MacroName;
696 SmallString<64> TextToInsert = AttributeSpelling;
698 SourceLocation Loc = FD->getSourceRange().getBegin();
699 Diag(Loc, diag::note_add_deprecation_attr)
704 void Sema::resolveParamCommandIndexes(
const FullComment *FC) {
705 if (!involvesFunctionType()) {
711 SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
715 SmallVector<ParamCommandComment *, 8> ParamVarDocs;
717 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
718 ParamVarDocs.resize(ParamVars.size(),
nullptr);
723 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
724 if (!PCC || !PCC->hasParamName())
726 StringRef ParamName = PCC->getParamNameAsWritten();
729 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
732 PCC->setIsVarArgParam();
736 UnresolvedParamCommands.push_back(PCC);
739 PCC->setParamIndex(ResolvedParamIndex);
740 if (ParamVarDocs[ResolvedParamIndex]) {
741 SourceRange ArgRange = PCC->getParamNameRange();
742 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
743 << ParamName << ArgRange;
744 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
745 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
746 << PrevCommand->getParamNameRange();
748 ParamVarDocs[ResolvedParamIndex] = PCC;
752 SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
753 for (
unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
754 if (!ParamVarDocs[i])
755 OrphanedParamDecls.push_back(ParamVars[i]);
761 for (
unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
762 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
764 SourceRange ArgRange = PCC->getParamNameRange();
765 StringRef ParamName = PCC->getParamNameAsWritten();
766 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
767 << ParamName << ArgRange;
770 if (OrphanedParamDecls.size() == 0)
774 if (OrphanedParamDecls.size() == 1) {
777 CorrectedParamIndex = 0;
780 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
784 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
785 if (
const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
786 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
787 << CorrectedII->getName()
793 bool Sema::involvesFunctionType() {
801 bool Sema::isFunctionDecl() {
809 bool Sema::isAnyFunctionDecl() {
810 return isFunctionDecl() && ThisDeclInfo->
CurrentDecl &&
814 bool Sema::isFunctionOrMethodVariadic() {
822 bool Sema::isObjCMethodDecl() {
823 return isFunctionDecl() && ThisDeclInfo->
CurrentDecl &&
827 bool Sema::isFunctionPointerVarDecl() {
833 if (
const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->
CurrentDecl)) {
834 QualType QT = VD->getType();
835 return QT->isFunctionPointerType();
841 bool Sema::isObjCPropertyDecl() {
849 bool Sema::isTemplateOrSpecialization() {
857 bool Sema::isRecordLikeDecl() {
862 return isUnionDecl() || isClassOrStructDecl() || isObjCInterfaceDecl() ||
863 isObjCProtocolDecl();
866 bool Sema::isUnionDecl() {
871 if (
const RecordDecl *RD =
872 dyn_cast_or_null<RecordDecl>(ThisDeclInfo->
CurrentDecl))
873 return RD->isUnion();
877 if (
auto *record = dyn_cast_or_null<RecordDecl>(D))
878 return !record->isUnion();
883 bool Sema::isClassOrStructDecl() {
895 bool Sema::isClassOrStructOrTagTypedefDecl() {
907 if (
auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(ThisDeclInfo->
CurrentDecl)) {
908 auto UnderlyingType = ThisTypedefDecl->getUnderlyingType();
909 if (
auto ThisElaboratedType = dyn_cast<ElaboratedType>(UnderlyingType)) {
910 auto DesugaredType = ThisElaboratedType->desugar();
911 if (
auto *DesugaredTypePtr = DesugaredType.getTypePtrOrNull()) {
912 if (
auto *ThisRecordType = dyn_cast<RecordType>(DesugaredTypePtr)) {
922 bool Sema::isClassTemplateDecl() {
928 (isa<ClassTemplateDecl>(ThisDeclInfo->
CurrentDecl));
931 bool Sema::isFunctionTemplateDecl() {
937 (isa<FunctionTemplateDecl>(ThisDeclInfo->
CurrentDecl));
940 bool Sema::isObjCInterfaceDecl() {
949 bool Sema::isObjCProtocolDecl() {
958 ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
964 void Sema::inspectThisDecl() {
965 ThisDeclInfo->
fill();
968 unsigned Sema::resolveParmVarReference(StringRef Name,
969 ArrayRef<const ParmVarDecl *> ParamVars) {
970 for (
unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
971 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
972 if (II && II->getName() == Name)
975 if (Name ==
"..." && isFunctionOrMethodVariadic())
981 class SimpleTypoCorrector {
982 const NamedDecl *BestDecl;
985 const unsigned MaxEditDistance;
987 unsigned BestEditDistance;
992 explicit SimpleTypoCorrector(StringRef Typo)
993 : BestDecl(nullptr), Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
994 BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}
996 void addDecl(
const NamedDecl *ND);
998 const NamedDecl *getBestDecl()
const {
999 if (BestEditDistance > MaxEditDistance)
1005 unsigned getBestDeclIndex()
const {
1006 assert(getBestDecl());
1011 void SimpleTypoCorrector::addDecl(
const NamedDecl *ND) {
1012 unsigned CurrIndex = NextIndex++;
1014 const IdentifierInfo *II = ND->getIdentifier();
1018 StringRef Name = II->getName();
1019 unsigned MinPossibleEditDistance =
abs((
int)Name.size() - (
int)Typo.size());
1020 if (MinPossibleEditDistance > 0 &&
1021 Typo.size() / MinPossibleEditDistance < 3)
1024 unsigned EditDistance = Typo.edit_distance(Name,
true, MaxEditDistance);
1025 if (EditDistance < BestEditDistance) {
1026 BestEditDistance = EditDistance;
1028 BestIndex = CurrIndex;
1033 unsigned Sema::correctTypoInParmVarReference(
1035 ArrayRef<const ParmVarDecl *> ParamVars) {
1036 SimpleTypoCorrector Corrector(Typo);
1037 for (
unsigned i = 0, e = ParamVars.size(); i != e; ++i)
1038 Corrector.addDecl(ParamVars[i]);
1039 if (Corrector.getBestDecl())
1040 return Corrector.getBestDeclIndex();
1046 bool ResolveTParamReferenceHelper(
1048 const TemplateParameterList *TemplateParameters,
1049 SmallVectorImpl<unsigned> *Position) {
1050 for (
unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1051 const NamedDecl *Param = TemplateParameters->getParam(i);
1052 const IdentifierInfo *II = Param->getIdentifier();
1053 if (II && II->getName() == Name) {
1054 Position->push_back(i);
1058 if (
const TemplateTemplateParmDecl *TTP =
1059 dyn_cast<TemplateTemplateParmDecl>(Param)) {
1060 Position->push_back(i);
1061 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
1064 Position->pop_back();
1071 bool Sema::resolveTParamReference(
1073 const TemplateParameterList *TemplateParameters,
1074 SmallVectorImpl<unsigned> *Position) {
1076 if (!TemplateParameters)
1079 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
1083 void CorrectTypoInTParamReferenceHelper(
1084 const TemplateParameterList *TemplateParameters,
1085 SimpleTypoCorrector &Corrector) {
1086 for (
unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1087 const NamedDecl *Param = TemplateParameters->getParam(i);
1088 Corrector.addDecl(Param);
1090 if (
const TemplateTemplateParmDecl *TTP =
1091 dyn_cast<TemplateTemplateParmDecl>(Param))
1092 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
1098 StringRef Sema::correctTypoInTParamReference(
1100 const TemplateParameterList *TemplateParameters) {
1101 SimpleTypoCorrector Corrector(Typo);
1102 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
1103 if (
const NamedDecl *ND = Corrector.getBestDecl()) {
1104 const IdentifierInfo *II = ND->getIdentifier();
1105 assert(II &&
"SimpleTypoCorrector should not return this decl");
1106 return II->getName();
1112 Sema::getInlineCommandRenderKind(StringRef Name)
const {
1115 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)