19#include "llvm/ADT/StringSwitch.h"
25#include "clang/AST/CommentHTMLTagsProperties.inc"
28Sema::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)
131void Sema::checkContainerDeclVerbatimLine(
const BlockCommandComment *Comment) {
132 const CommandInfo *Info = Traits.
getCommandInfo(Comment->getCommandID());
133 if (!Info->IsRecordLikeDeclarationCommand)
135 std::optional<unsigned> DiagSelect;
136 switch (Comment->getCommandID()) {
137 case CommandTraits::KCI_class:
138 if (!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl())
139 DiagSelect = diag::DeclContainerKind::Class;
144 if (DiagSelect && Comment->getCommandMarker() && isObjCInterfaceDecl())
145 DiagSelect = std::nullopt;
147 case CommandTraits::KCI_interface:
148 if (!isObjCInterfaceDecl())
149 DiagSelect = diag::DeclContainerKind::Interface;
151 case CommandTraits::KCI_protocol:
152 if (!isObjCProtocolDecl())
153 DiagSelect = diag::DeclContainerKind::Protocol;
155 case CommandTraits::KCI_struct:
156 if (!isClassOrStructOrTagTypedefDecl())
157 DiagSelect = diag::DeclContainerKind::Struct;
159 case CommandTraits::KCI_union:
161 DiagSelect = diag::DeclContainerKind::Union;
164 DiagSelect = std::nullopt;
168 Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
169 << Comment->getCommandMarker() << (*DiagSelect) << (*DiagSelect)
170 << Comment->getSourceRange();
173void Sema::checkContainerDecl(
const BlockCommandComment *Comment) {
174 const CommandInfo *Info = Traits.
getCommandInfo(Comment->getCommandID());
175 if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
177 std::optional<unsigned> DiagSelect;
178 switch (Comment->getCommandID()) {
179 case CommandTraits::KCI_classdesign:
180 DiagSelect = diag::DocCommandKind::ClassDesign;
182 case CommandTraits::KCI_coclass:
183 DiagSelect = diag::DocCommandKind::CoClass;
185 case CommandTraits::KCI_dependency:
186 DiagSelect = diag::DocCommandKind::Dependency;
188 case CommandTraits::KCI_helper:
189 DiagSelect = diag::DocCommandKind::Helper;
191 case CommandTraits::KCI_helperclass:
192 DiagSelect = diag::DocCommandKind::HelperClass;
194 case CommandTraits::KCI_helps:
195 DiagSelect = diag::DocCommandKind::Helps;
197 case CommandTraits::KCI_instancesize:
198 DiagSelect = diag::DocCommandKind::InstanceSize;
200 case CommandTraits::KCI_ownership:
201 DiagSelect = diag::DocCommandKind::Ownership;
203 case CommandTraits::KCI_performance:
204 DiagSelect = diag::DocCommandKind::Performance;
206 case CommandTraits::KCI_security:
207 DiagSelect = diag::DocCommandKind::Security;
209 case CommandTraits::KCI_superclass:
210 DiagSelect = diag::DocCommandKind::Superclass;
213 DiagSelect = std::nullopt;
217 Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
218 << Comment->getCommandMarker() << (*DiagSelect)
219 << Comment->getSourceRange();
225 return llvm::StringSwitch<ParamCommandPassDirection>(Arg)
236 std::string ArgLower = Arg.lower();
246 const char *FixedName =
248 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
251 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange;
271 auto *A =
new (Allocator)
279 checkBlockCommandEmptyParagraph(Command);
291 if (!isTemplateOrSpecialization())
293 diag::warn_doc_tparam_not_attached_to_a_template_decl)
307 auto *A =
new (Allocator)
311 if (!isTemplateOrSpecialization()) {
319 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
324 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
326 Diag(PrevCommand->
getLocation(), diag::note_doc_tparam_previous)
329 PrevCommand = Command;
334 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
337 if (!TemplateParameters || TemplateParameters->
size() == 0)
340 StringRef CorrectedName;
341 if (TemplateParameters->
size() == 1) {
347 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
350 if (!CorrectedName.empty()) {
351 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
360 checkBlockCommandEmptyParagraph(Command);
371 CommandLocBegin, CommandLocEnd, CommandID,
372 getInlineCommandRenderKind(CommandName), CommandMarker, Args);
377 StringRef CommandName) {
384 unsigned CommandID) {
397 unsigned CommandID) {
401 Loc.getLocWithOffset(1 + CommandName.size()),
415 Block->setCloseName(CloseName, CloseNameLocBegin);
416 Block->setLines(Lines);
429 checkFunctionDeclVerbatimLine(VL);
430 checkContainerDeclVerbatimLine(VL);
443 bool IsSelfClosing) {
444 Tag->setAttrs(Attrs);
445 Tag->setGreaterLoc(GreaterLoc);
447 Tag->setSelfClosing();
448 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
449 HTMLOpenTags.push_back(Tag);
457 if (isHTMLEndTagForbidden(TagName)) {
458 Diag(HET->
getLocation(), diag::warn_doc_html_end_forbidden)
464 bool FoundOpen =
false;
466 I = HTMLOpenTags.rbegin(),
E = HTMLOpenTags.rend();
468 if ((*I)->getTagName() == TagName) {
474 Diag(HET->
getLocation(), diag::warn_doc_html_end_unbalanced)
480 while (!HTMLOpenTags.empty()) {
482 StringRef LastNotClosedTagName = HST->
getTagName();
483 if (LastNotClosedTagName == TagName) {
490 if (isHTMLEndTagOptional(LastNotClosedTagName))
493 bool OpenLineInvalid;
497 bool CloseLineInvalid;
502 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
503 Diag(HST->
getLocation(), diag::warn_doc_html_start_end_mismatch)
508 Diag(HST->
getLocation(), diag::warn_doc_html_start_end_mismatch)
511 Diag(HET->
getLocation(), diag::note_doc_html_end_tag)
523 resolveParamCommandIndexes(FC);
526 while (!HTMLOpenTags.empty()) {
531 Diag(HST->
getLocation(), diag::warn_doc_html_missing_end_tag)
550 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
557void Sema::checkReturnsCommand(
const BlockCommandComment *Command) {
561 assert(ThisDeclInfo &&
"should not call this check on a bare comment");
565 if (isObjCPropertyDecl())
567 if (involvesFunctionType()) {
569 "should have a valid return type");
579 case Decl::CXXConstructor:
582 case Decl::CXXDestructor:
586 Diag(Command->getLocation(),
587 diag::warn_doc_returns_attached_to_a_void_function)
588 << Command->getCommandMarker()
589 << Command->getCommandName(Traits)
591 << Command->getSourceRange();
596 Diag(Command->getLocation(),
597 diag::warn_doc_returns_not_attached_to_a_function_decl)
598 << Command->getCommandMarker()
599 << Command->getCommandName(Traits)
600 << Command->getSourceRange();
603void Sema::checkBlockCommandDuplicate(
const BlockCommandComment *Command) {
604 const CommandInfo *Info = Traits.
getCommandInfo(Command->getCommandID());
605 const BlockCommandComment *PrevCommand =
nullptr;
606 if (Info->IsBriefCommand) {
608 BriefCommand = Command;
611 PrevCommand = BriefCommand;
612 }
else if (Info->IsHeaderfileCommand) {
613 if (!HeaderfileCommand) {
614 HeaderfileCommand = Command;
617 PrevCommand = HeaderfileCommand;
623 StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
624 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
625 << Command->getCommandMarker()
627 << Command->getSourceRange();
628 if (CommandName == PrevCommandName)
629 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
630 << PrevCommand->getCommandMarker()
632 << PrevCommand->getSourceRange();
634 Diag(PrevCommand->getLocation(),
635 diag::note_doc_block_command_previous_alias)
636 << PrevCommand->getCommandMarker()
641void Sema::checkDeprecatedCommand(
const BlockCommandComment *Command) {
645 assert(ThisDeclInfo &&
"should not call this check on a bare comment");
651 if (
D->hasAttr<DeprecatedAttr>() ||
652 D->hasAttr<AvailabilityAttr>() ||
653 D->hasAttr<UnavailableAttr>())
656 Diag(Command->getLocation(), diag::warn_doc_deprecated_not_sync)
657 << Command->getSourceRange() << Command->getCommandMarker();
660 if (
const FunctionDecl *FD = dyn_cast<FunctionDecl>(
D)) {
663 const DeclContext *Ctx = FD->getDeclContext();
664 if ((!Ctx || !Ctx->isRecord()) &&
665 FD->doesThisDeclarationHaveABody())
668 const LangOptions &LO = FD->getLangOpts();
669 const bool DoubleSquareBracket = LO.CPlusPlus14 || LO.C23;
670 StringRef AttributeSpelling =
671 DoubleSquareBracket ?
"[[deprecated]]" :
"__attribute__((deprecated))";
677 if (DoubleSquareBracket) {
678 TokenValue Tokens[] = {tok::l_square, tok::l_square,
680 tok::r_square, tok::r_square};
682 if (!MacroName.empty())
683 AttributeSpelling = MacroName;
686 if (MacroName.empty()) {
687 TokenValue Tokens[] = {
688 tok::kw___attribute, tok::l_paren,
690 tok::r_paren, tok::r_paren};
691 StringRef MacroName =
693 if (!MacroName.empty())
694 AttributeSpelling = MacroName;
698 SmallString<64> TextToInsert = AttributeSpelling;
700 SourceLocation
Loc = FD->getSourceRange().getBegin();
701 Diag(
Loc, diag::note_add_deprecation_attr)
706void Sema::resolveParamCommandIndexes(
const FullComment *FC) {
707 if (!involvesFunctionType()) {
713 SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
717 SmallVector<ParamCommandComment *, 8> ParamVarDocs;
719 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
720 ParamVarDocs.resize(ParamVars.size(),
nullptr);
725 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
726 if (!PCC || !PCC->hasParamName())
728 StringRef ParamName = PCC->getParamNameAsWritten();
731 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
734 PCC->setIsVarArgParam();
738 UnresolvedParamCommands.push_back(PCC);
741 PCC->setParamIndex(ResolvedParamIndex);
742 if (ParamVarDocs[ResolvedParamIndex]) {
743 SourceRange ArgRange = PCC->getParamNameRange();
744 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
745 << ParamName << ArgRange;
746 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
747 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
748 << PrevCommand->getParamNameRange();
750 ParamVarDocs[ResolvedParamIndex] = PCC;
754 SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
755 for (
unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
756 if (!ParamVarDocs[i])
757 OrphanedParamDecls.push_back(ParamVars[i]);
763 for (
unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
764 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
766 SourceRange ArgRange = PCC->getParamNameRange();
767 StringRef ParamName = PCC->getParamNameAsWritten();
768 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
769 << ParamName << ArgRange;
772 if (OrphanedParamDecls.size() == 0)
776 if (OrphanedParamDecls.size() == 1) {
779 CorrectedParamIndex = 0;
782 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
786 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
787 if (
const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
788 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
789 << CorrectedII->getName()
795bool Sema::involvesFunctionType() {
803bool Sema::isFunctionDecl() {
811bool Sema::isAnyFunctionDecl() {
812 return isFunctionDecl() && ThisDeclInfo->
CurrentDecl &&
816bool Sema::isFunctionOrMethodVariadic() {
824bool Sema::isObjCMethodDecl() {
825 return isFunctionDecl() && ThisDeclInfo->
CurrentDecl &&
829bool Sema::isFunctionPointerVarDecl() {
835 if (
const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->
CurrentDecl)) {
836 QualType QT = VD->getType();
837 return QT->isFunctionPointerType();
843bool Sema::isObjCPropertyDecl() {
851bool Sema::isTemplateOrSpecialization() {
859bool Sema::isRecordLikeDecl() {
864 return isUnionDecl() || isClassOrStructDecl() || isObjCInterfaceDecl() ||
865 isObjCProtocolDecl();
868bool Sema::isUnionDecl() {
873 if (
const RecordDecl *RD =
874 dyn_cast_or_null<RecordDecl>(ThisDeclInfo->
CurrentDecl))
875 return RD->isUnion();
879 if (
auto *record = dyn_cast_or_null<RecordDecl>(
D))
880 return !record->isUnion();
885bool Sema::isClassOrStructDecl() {
897bool Sema::isClassOrStructOrTagTypedefDecl() {
909 if (
auto *ThisTypedefDecl = dyn_cast<TypedefDecl>(ThisDeclInfo->
CurrentDecl))
910 if (
auto *
D = ThisTypedefDecl->getUnderlyingType()->getAsRecordDecl())
916bool Sema::isClassTemplateDecl() {
922 (isa<ClassTemplateDecl>(ThisDeclInfo->
CurrentDecl));
925bool Sema::isFunctionTemplateDecl() {
931 (isa<FunctionTemplateDecl>(ThisDeclInfo->
CurrentDecl));
934bool Sema::isObjCInterfaceDecl() {
943bool Sema::isObjCProtocolDecl() {
952ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
958void Sema::inspectThisDecl() {
959 ThisDeclInfo->
fill();
962unsigned Sema::resolveParmVarReference(StringRef Name,
963 ArrayRef<const ParmVarDecl *> ParamVars) {
964 for (
unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
965 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
966 if (II && II->getName() == Name)
969 if (Name ==
"..." && isFunctionOrMethodVariadic())
975Sema::correctTypoInParmVarReference(StringRef Typo,
976 ArrayRef<const ParmVarDecl *> ParamVars) {
977 SimpleTypoCorrection STC(Typo);
978 for (
unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
979 const ParmVarDecl *Param = ParamVars[i];
983 STC.add(Param->getIdentifier());
986 if (STC.hasCorrection())
987 return STC.getCorrectionIndex();
993bool ResolveTParamReferenceHelper(
995 const TemplateParameterList *TemplateParameters,
996 SmallVectorImpl<unsigned> *Position) {
997 for (
unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
998 const NamedDecl *Param = TemplateParameters->getParam(i);
999 const IdentifierInfo *II = Param->getIdentifier();
1000 if (II && II->getName() == Name) {
1001 Position->push_back(i);
1005 if (
const TemplateTemplateParmDecl *TTP =
1006 dyn_cast<TemplateTemplateParmDecl>(Param)) {
1007 Position->push_back(i);
1008 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
1011 Position->pop_back();
1018bool Sema::resolveTParamReference(
1020 const TemplateParameterList *TemplateParameters,
1021 SmallVectorImpl<unsigned> *Position) {
1023 if (!TemplateParameters)
1026 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
1030void CorrectTypoInTParamReferenceHelper(
1031 const TemplateParameterList *TemplateParameters,
1032 SimpleTypoCorrection &STC) {
1033 for (
unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
1034 const NamedDecl *Param = TemplateParameters->getParam(i);
1038 STC.add(Param->getIdentifier());
1040 if (
const TemplateTemplateParmDecl *TTP =
1041 dyn_cast<TemplateTemplateParmDecl>(Param))
1042 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), STC);
1047StringRef Sema::correctTypoInTParamReference(
1049 const TemplateParameterList *TemplateParameters) {
1050 SimpleTypoCorrection STC(Typo);
1051 CorrectTypoInTParamReferenceHelper(TemplateParameters, STC);
1053 if (
auto CorrectedTParamReference = STC.getCorrection())
1054 return *CorrectedTParamReference;
1062 return llvm::StringSwitch<InlineCommandRenderKind>(Name)
Defines the C++ template declaration subclasses.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::Preprocessor interface.
Defines the SourceManager interface.
Decl - This represents one declaration (or definition), e.g.
Concrete class used by the front-end to report problems and issues.
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
This represents a decl that may have a name.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
IdentifierInfo * getIdentifierInfo(StringRef Name) const
Return information about the specified preprocessor identifier token.
StringRef getLastMacroWithSpelling(SourceLocation Loc, ArrayRef< TokenValue > Tokens) const
Return the name of the macro defined before Loc that has spelling Tokens.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Stores a list of template parameters for a TemplateDecl and its derived classes.
NamedDecl * getParam(unsigned Idx)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...