From a9e4a9e5b51ab33df64f3989c710e08546dcd45d Mon Sep 17 00:00:00 2001 From: Dimitri van Heesch Date: Wed, 31 Mar 2021 16:44:36 +0200 Subject: C++20 concepts: added support for parsing requires-clauses --- src/classdef.cpp | 17 ++++++++ src/classdef.h | 3 ++ src/doxygen.cpp | 13 ++++++ src/entry.cpp | 2 + src/entry.h | 1 + src/memberdef.cpp | 85 +++++++++++++++++++++++++----------- src/memberdef.h | 4 ++ src/scanner.l | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 225 insertions(+), 27 deletions(-) diff --git a/src/classdef.cpp b/src/classdef.cpp index 542147a..6432e2a 100644 --- a/src/classdef.cpp +++ b/src/classdef.cpp @@ -239,6 +239,7 @@ class ClassDefImpl : public DefinitionMixin virtual bool subGrouping() const; virtual bool isSliceLocal() const; virtual bool hasNonReferenceSuperClass() const; + virtual QCString requiresClause() const; virtual ClassDef *insertTemplateInstance(const QCString &fileName,int startLine,int startColumn, const QCString &templSpec,bool &freshInstance) const; @@ -309,6 +310,7 @@ class ClassDefImpl : public DefinitionMixin MemberListType lt,const QCString &title, const char *subTitle=0,bool showInline=FALSE,const ClassDef *inheritedFrom=0, int lt2=-1,bool invert=FALSE,bool showAlways=FALSE) const; + virtual void setRequiresClause(const char *req); private: void addUsedInterfaceClasses(MemberDef *md,const char *typeStr); @@ -527,6 +529,8 @@ class ClassDefAliasImpl : public DefinitionAliasMixin { return getCdAlias()->isSliceLocal(); } virtual bool hasNonReferenceSuperClass() const { return getCdAlias()->hasNonReferenceSuperClass(); } + virtual QCString requiresClause() const + { return getCdAlias()->requiresClause(); } virtual int countMembersIncludingGrouped(MemberListType lt,const ClassDef *inheritedFrom,bool additional) const { return getCdAlias()->countMembersIncludingGrouped(lt,inheritedFrom,additional); } @@ -704,6 +708,9 @@ class ClassDefImpl::IMPL uint64 spec = 0; QCString metaData; + + /** C++20 requires clause */ + QCString requiresClause; }; void ClassDefImpl::IMPL::init(const char *defFileName, const char *name, @@ -3184,6 +3191,16 @@ bool ClassDefImpl::hasNonReferenceSuperClass() const return found; } +QCString ClassDefImpl::requiresClause() const +{ + return m_impl->requiresClause; +} + +void ClassDefImpl::setRequiresClause(const char *req) +{ + m_impl->requiresClause = req; +} + /*! called from MemberDef::writeDeclaration() to (recursively) write the * definition of an anonymous struct, union or class. */ diff --git a/src/classdef.h b/src/classdef.h index d6c515d..b3e3325 100644 --- a/src/classdef.h +++ b/src/classdef.h @@ -358,6 +358,8 @@ class ClassDef : public Definition virtual bool isSliceLocal() const = 0; virtual bool hasNonReferenceSuperClass() const = 0; + virtual QCString requiresClause() const = 0; + //----------------------------------------------------------------------------------- // --- count members ---- //----------------------------------------------------------------------------------- @@ -405,6 +407,7 @@ class ClassDefMutable : public DefinitionMutable, public ClassDef virtual void setTagLessReference(const ClassDef *cd) = 0; virtual void setName(const char *name) = 0; virtual void setMetaData(const char *md) = 0; + virtual void setRequiresClause(const char *req) = 0; //----------------------------------------------------------------------------------- // --- actions ---- diff --git a/src/doxygen.cpp b/src/doxygen.cpp index d5e082d..9a9342b 100644 --- a/src/doxygen.cpp +++ b/src/doxygen.cpp @@ -1011,6 +1011,10 @@ static void addClassToContext(const Entry *root) cd->setTemplateArguments(*tArgList); } } + if (cd->requiresClause().isEmpty() && !root->req.isEmpty()) + { + cd->setRequiresClause(root->req); + } cd->setCompoundType(convertToCompoundType(root->section,root->spec)); @@ -1081,6 +1085,7 @@ static void addClassToContext(const Entry *root) { cd->setTemplateArguments(*tArgList); } + cd->setRequiresClause(root->req); cd->setProtection(root->protection); cd->setIsStatic(root->stat); @@ -1365,6 +1370,7 @@ static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const Class imd->setMemberSpecifiers(md->getMemberSpecifiers()); imd->setMemberGroupId(md->getMemberGroupId()); imd->setInitializer(md->initializer()); + imd->setRequiresClause(md->requiresClause()); imd->setMaxInitLines(md->initializerLines()); imd->setBitfields(md->bitfieldString()); imd->setLanguage(md->getLanguage()); @@ -1982,6 +1988,7 @@ static void findUsingDeclImports(const Entry *root) newMd->setBodySegment(md->getDefLine(),md->getStartBodyLine(),md->getEndBodyLine()); newMd->setBodyDef(md->getBodyDef()); newMd->setInitializer(md->initializer()); + newMd->setRequiresClause(md->requiresClause()); newMd->setMaxInitLines(md->initializerLines()); newMd->setMemberGroupId(root->mGrpId); newMd->setMemberSpecifiers(md->getMemberSpecifiers()); @@ -3104,6 +3111,7 @@ static void addMethodToClass(const Entry *root,ClassDefMutable *cd, md->setMemberGroupId(root->mGrpId); md->setTypeConstraints(root->typeConstr); md->setLanguage(root->lang); + md->setRequiresClause(root->req); md->setId(root->id); md->setBodyDef(fd); md->setFileDef(fd); @@ -3213,6 +3221,7 @@ static void addGlobalFunction(const Entry *root,const QCString &rname,const QCSt md->addSectionsToDefinition(root->anchors); md->setMemberSpecifiers(root->spec); md->setMemberGroupId(root->mGrpId); + md->setRequiresClause(root->req); NamespaceDefMutable *nd = 0; // see if the function is inside a namespace that was not part of @@ -5026,6 +5035,10 @@ static void addMemberDocs(const Entry *root, //printf("setInitializer\n"); md->setInitializer(rootInit.c_str()); } + if (md->requiresClause().isEmpty() && !root->req.isEmpty()) + { + md->setRequiresClause(root->req); + } md->setMaxInitLines(root->initLines); diff --git a/src/entry.cpp b/src/entry.cpp index a0f8fa3..a58362c 100644 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -108,6 +108,7 @@ Entry::Entry(const Entry &e) id = e.id; extends = e.extends; groups = e.groups; + req = e.req; m_fileDef = e.m_fileDef; m_parent = e.m_parent; @@ -242,6 +243,7 @@ void Entry::reset() tArgLists.clear(); typeConstr.reset(); sli.clear(); + req.resize(0); m_fileDef = 0; } diff --git a/src/entry.h b/src/entry.h index dd2b157..6ba88e8 100644 --- a/src/entry.h +++ b/src/entry.h @@ -295,6 +295,7 @@ class Entry QCString id; //!< libclang id LocalToc localToc; QCString metaData; //!< Slice metadata + QCString req; //!< C++20 requires clause /// return the command name used to define GROUPDOC_SEC const char *groupDocCmd() const diff --git a/src/memberdef.cpp b/src/memberdef.cpp index 48291cb..38164ef 100644 --- a/src/memberdef.cpp +++ b/src/memberdef.cpp @@ -229,6 +229,7 @@ class MemberDefImpl : public DefinitionMixin virtual QCString getDeclType() const; virtual StringVector getLabels(const Definition *container) const; virtual const ArgumentList &typeConstraints() const; + virtual QCString requiresClause() const; virtual QCString documentation() const; virtual QCString briefDescription(bool abbr=FALSE) const; virtual QCString fieldType() const; @@ -304,6 +305,7 @@ class MemberDefImpl : public DefinitionMixin virtual void setBriefDescription(const char *b,const char *briefFile,int briefLine); virtual void setInbodyDocumentation(const char *d,const char *inbodyFile,int inbodyLine); virtual void setHidden(bool b); + virtual void setRequiresClause(const char *req); virtual void incrementFlowKeyWordCount(); virtual void writeDeclaration(OutputList &ol, const ClassDef *cd,const NamespaceDef *nd,const FileDef *fd,const GroupDef *gd, @@ -344,6 +346,8 @@ class MemberDefImpl : public DefinitionMixin const QCString &cname) const; void _writeCategoryRelation(OutputList &ol) const; void _writeTagData(const DefType) const; + void _writeTemplatePrefix(OutputList &ol, const Definition *def, + const ArgumentList &al, bool writeReqClause=true) const; static int s_indentLevel; @@ -735,6 +739,8 @@ class MemberDefAliasImpl : public DefinitionAliasMixin { return getMdAlias()->getDeclLine(); } virtual int getDeclColumn() const { return getMdAlias()->getDeclColumn(); } + virtual QCString requiresClause() const + { return getMdAlias()->requiresClause(); } virtual void warnIfUndocumented() const {} virtual void warnIfUndocumentedParams() const {} @@ -1119,26 +1125,6 @@ static void writeExceptionList(OutputList &ol, const ClassDef *cd, const MemberD } } -static void writeTemplatePrefix(OutputList &ol,const ArgumentList &al) -{ - ol.docify("template<"); - for (auto it = al.begin(); it!=al.end();) - { - Argument a = *it; - ol.docify(a.type); - ol.docify(" "); - ol.docify(a.name); - if (a.defval.length()!=0) - { - ol.docify(" = "); - ol.docify(a.defval); - } - ++it; - if (it!=al.end()) ol.docify(", "); - } - ol.docify("> "); -} - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -1189,6 +1175,7 @@ class MemberDefImpl::IMPL QCString initializer; // initializer QCString extraTypeChars; // extra type info found after the argument list QCString enumBaseType; // base type of the enum (C++11) + QCString requiresClause; // requires clause (C++20) int initLines = 0; // number of lines in the initializer uint64 memSpec = 0; // The specifiers present for this member @@ -2008,6 +1995,46 @@ QCString MemberDefImpl::getDeclType() const return ltype; } +void MemberDefImpl::_writeTemplatePrefix(OutputList &ol, const Definition *def, + const ArgumentList &al, bool writeReqClause) const +{ + ol.docify("template<"); + for (auto it = al.begin(); it!=al.end();) + { + Argument a = *it; + linkifyText(TextGeneratorOLImpl(ol), // out + def, // scope + getFileDef(), // fileScope + this, // self + a.type, // text + FALSE // autoBreak + ); + ol.docify(" "); + ol.docify(a.name); + if (a.defval.length()!=0) + { + ol.docify(" = "); + ol.docify(a.defval); + } + ++it; + if (it!=al.end()) ol.docify(", "); + } + ol.docify("> "); + if (writeReqClause && !m_impl->requiresClause.isEmpty()) + { + ol.lineBreak(); + ol.docify("requires "); + linkifyText(TextGeneratorOLImpl(ol), // out + def, // scope + getFileDef(), // fileScope + this, // self + m_impl->requiresClause, // text + FALSE // autoBreak + ); + } +} + + void MemberDefImpl::writeDeclaration(OutputList &ol, const ClassDef *cd,const NamespaceDef *nd,const FileDef *fd,const GroupDef *gd, bool inGroup, const ClassDef *inheritedFrom,const char *inheritId) const @@ -2086,10 +2113,11 @@ void MemberDefImpl::writeDeclaration(OutputList &ol, if (m_impl->tArgList.hasParameters() && getLanguage()==SrcLangExt_Cpp) { if (!isAnonType) ol.startMemberTemplateParams(); - writeTemplatePrefix(ol,m_impl->tArgList); + _writeTemplatePrefix(ol,d,m_impl->tArgList); if (!isAnonType) ol.endMemberTemplateParams(anchor(),inheritId); } + // *** write type QCString ltype(m_impl->type); if (isTypedef() && getLanguage() != SrcLangExt_Slice) @@ -3280,7 +3308,7 @@ void MemberDefImpl::writeDocumentation(const MemberList *ml, { if (!first) ol.docify(" "); ol.startMemberDocPrefixItem(); - writeTemplatePrefix(ol,tal); + _writeTemplatePrefix(ol,scopedContainer,tal); ol.endMemberDocPrefixItem(); } } @@ -3296,7 +3324,7 @@ void MemberDefImpl::writeDocumentation(const MemberList *ml, { if (!first) ol.docify(" "); ol.startMemberDocPrefixItem(); - writeTemplatePrefix(ol,tal); + _writeTemplatePrefix(ol,scopedContainer,tal,false); ol.endMemberDocPrefixItem(); } } @@ -3304,7 +3332,7 @@ void MemberDefImpl::writeDocumentation(const MemberList *ml, if (m_impl->tArgList.hasParameters() && lang==SrcLangExt_Cpp) // function template prefix { ol.startMemberDocPrefixItem(); - writeTemplatePrefix(ol,m_impl->tArgList); + _writeTemplatePrefix(ol,scopedContainer,m_impl->tArgList); ol.endMemberDocPrefixItem(); } } @@ -5604,6 +5632,15 @@ QCString MemberDefImpl::enumBaseType() const return m_impl->enumBaseType; } +void MemberDefImpl::setRequiresClause(const char *req) +{ + m_impl->requiresClause = req; +} + +QCString MemberDefImpl::requiresClause() const +{ + return m_impl->requiresClause; +} void MemberDefImpl::cacheTypedefVal(const ClassDef*val, const QCString & templSpec, const QCString &resolvedType) { diff --git a/src/memberdef.h b/src/memberdef.h index d2d6e1d..d51fbc0 100644 --- a/src/memberdef.h +++ b/src/memberdef.h @@ -261,6 +261,8 @@ class MemberDef : public Definition virtual const ArgumentList &typeConstraints() const = 0; + virtual QCString requiresClause() const = 0; + // overrules virtual QCString documentation() const = 0; virtual QCString briefDescription(bool abbr=FALSE) const = 0; @@ -393,6 +395,8 @@ class MemberDefMutable : public DefinitionMutable, public MemberDef virtual void setHidden(bool b) = 0; + virtual void setRequiresClause(const char *req) = 0; + //----------------------------------------------------------------------------------- // --- actions ---- //----------------------------------------------------------------------------------- diff --git a/src/scanner.l b/src/scanner.l index 78afdab..9e719d0 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -74,6 +74,7 @@ struct scannerYY_state int lastStringContext = 0; int lastCurlyContext = 0; int lastRoundContext = 0; + int lastSharpContext = 0; int lastSquareContext = 0; int lastInitializerContext = 0; int lastClassTemplSpecContext = 0; @@ -139,6 +140,7 @@ struct scannerYY_state int currentArgumentContext = 0; int lastCopyArgStringContext = 0; int lastCopyArgContext = 0; + int requiresContext = 0; QCString *copyArgString = 0; QCString fullArgString; QCString dummyRawString; @@ -149,6 +151,7 @@ struct scannerYY_state QCString *pCopyQuotedString = 0; QCString *pCopyRoundString = 0; QCString *pCopyCurlyString = 0; + QCString *pCopySharpString = 0; QCString *pCopyRawString = 0; TextStream *pCopyCurlyGString = 0; TextStream *pCopyRoundGString = 0; @@ -383,6 +386,7 @@ NONLopt [^\n]* %x CopyGString %x CopyPHPGString %x CopyRound +%x CopySharp %x CopyCurly %x GCopyRound %x GCopySquare @@ -438,6 +442,10 @@ NONLopt [^\n]* %x DocBlock %x DocCopyBlock + /** C++20 concepts */ + +%x RequiresClause + %% "{" { @@ -2069,6 +2077,54 @@ NONLopt [^\n]* yyextra->current->name=yytext; BEGIN(FindMembers); } +"requires" { // C++20 requires clause + yyextra->current->req.resize(0); + yyextra->requiresContext = YY_START; + BEGIN(RequiresClause); + } +"(" { // requires "(A && B)" + yyextra->current->req+=yytext; + yyextra->lastRoundContext=RequiresClause; + yyextra->pCopyRoundString=&yyextra->current->req; + yyextra->roundCount=0; + BEGIN( CopyRound ) ; + } +{ID} { // something like "requires true" + if (yyextra->current->req.stripWhiteSpace().isEmpty()) + { + yyextra->current->req=yytext; + BEGIN(yyextra->requiresContext); + } + else + { + REJECT; + } + } +{SCOPENAME}{BNopt}"(" { // "requires func(x)" + yyextra->current->req+=yytext; + yyextra->lastRoundContext=RequiresClause; + yyextra->pCopyRoundString=&yyextra->current->req; + yyextra->roundCount=0; + BEGIN( CopyRound ); + } +{SCOPENAME}{BNopt}"<" { // "requires C" + yyextra->current->req+=yytext; + yyextra->lastSharpContext=RequiresClause; + yyextra->pCopySharpString=&yyextra->current->req; + yyextra->sharpCount=0; + BEGIN( CopySharp ); + } +"||"|"&&" { // "requires A || B" or "requires A && B" + yyextra->current->req+=yytext; + } +{BN}+ { + yyextra->current->req+=' '; + } +. { + unput(*yytext); + yyextra->current->req=yyextra->current->req.simplifyWhiteSpace(); + BEGIN(yyextra->requiresContext); + } {SCOPENAME} { if (yyextra->clangParser && (yyextra->insideCpp || yyextra->insideObjC)) @@ -2948,6 +3004,59 @@ NONLopt [^\n]* *yyextra->pCopyRoundString+=*yytext; } + /* generic sharp bracket list copy rules */ +\" { + *yyextra->pCopySharpString += *yytext; + yyextra->pCopyQuotedString=yyextra->pCopySharpString; + yyextra->lastStringContext=YY_START; + BEGIN(CopyString); + } +"<" { + *yyextra->pCopySharpString += *yytext; + yyextra->sharpCount++; + } +">" { + *yyextra->pCopySharpString += *yytext; + if (--yyextra->sharpCount<0) + { + BEGIN(yyextra->lastSharpContext); + } + } +\n { + lineCount(yyscanner); + *yyextra->pCopySharpString += *yytext; + } +\' { + if (yyextra->insidePHP) + { + yyextra->current->initializer << yytext; + yyextra->pCopyQuotedString = yyextra->pCopySharpString; + yyextra->lastStringContext=YY_START; + BEGIN(CopyPHPString); + } + else + { + *yyextra->pCopySharpString += yytext; + } + } +{CHARLIT} { + if (yyextra->insidePHP) + { + REJECT; + } + else + { + *yyextra->pCopySharpString+=yytext; + } + } +[^"'<>\n,]+ { + *yyextra->pCopySharpString+=yytext; + } +. { + *yyextra->pCopySharpString+=*yytext; + } + + /* generic round bracket list copy rules for growable strings */ \" { *yyextra->pCopyRoundGString << *yytext; @@ -4627,6 +4736,11 @@ NONLopt [^\n]* unput(*yytext); BEGIN(FuncQual); } +"requires"{BN}+ { + yyextra->requiresContext = FuncQual; + yyextra->current->req+=' '; + BEGIN(RequiresClause); + } "(" { yyextra->roundCount++; yyextra->current->argList.setTrailingReturnType(yyextra->current->argList.trailingReturnType()+yytext); @@ -4734,8 +4848,15 @@ NONLopt [^\n]* yyextra->current->args += *yytext; lineCount(yyscanner); } -{ID} { // typically a K&R style C function - if (yyextra->insideCS && qstrcmp(yytext,"where")==0) +{ID} { + if (yyextra->insideCpp && qstrcmp(yytext,"requires")==0) + { + // c++20 trailing requires clause + yyextra->requiresContext = YY_START; + yyextra->current->req+=' '; + BEGIN(RequiresClause); + } + else if (yyextra->insideCS && qstrcmp(yytext,"where")==0) { // type constraint for a method yyextra->current->typeConstr.clear(); @@ -4743,7 +4864,7 @@ NONLopt [^\n]* yyextra->lastCSConstraint = YY_START; BEGIN( CSConstraintName ); } - else if (checkForKnRstyleC(yyscanner)) + else if (checkForKnRstyleC(yyscanner)) // K&R style C function { yyextra->current->args = yytext; yyextra->oldStyleArgType.resize(0); -- cgit v0.12