From ec27c2e507a264ac052fc8f05112890f7cf92adb Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 29 Jan 2020 23:13:42 +0100 Subject: [PATCH] Introduce AST node for structured documentation. --- liblangutil/Scanner.cpp | 2 + libsolidity/analysis/DocStringAnalyser.cpp | 18 +++--- libsolidity/analysis/DocStringAnalyser.h | 14 ++--- libsolidity/ast/AST.h | 66 ++++++++++++++++++---- libsolidity/ast/ASTAnnotations.h | 14 ++--- libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/ASTVisitor.h | 4 ++ libsolidity/ast/AST_accept.h | 32 +++++++++++ libsolidity/ast/Types.cpp | 6 +- libsolidity/ast/Types.h | 6 +- libsolidity/parsing/Parser.cpp | 40 +++++++------ libsolidity/parsing/Parser.h | 1 + test/libsolidity/SolidityParser.cpp | 2 +- 13 files changed, 147 insertions(+), 59 deletions(-) diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index f9a8aab12..f98327405 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -428,6 +428,7 @@ Token Scanner::scanSlash() // doxygen style /// comment Token comment; m_skippedComments[NextNext].location.start = firstSlashPosition; + m_skippedComments[NextNext].location.source = m_source; comment = scanSingleLineDocComment(); m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].token = comment; @@ -454,6 +455,7 @@ Token Scanner::scanSlash() // we actually have a multiline documentation comment Token comment; m_skippedComments[NextNext].location.start = firstSlashPosition; + m_skippedComments[NextNext].location.source = m_source; comment = scanMultiLineDocComment(); m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].token = comment; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 1b3bd598b..cadd12fc3 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -73,7 +73,7 @@ bool DocStringAnalyser::visit(EventDefinition const& _event) void DocStringAnalyser::checkParameters( CallableDeclaration const& _callable, - DocumentedAnnotation& _annotation + StructurallyDocumentedAnnotation& _annotation ) { set validParams; @@ -95,8 +95,8 @@ void DocStringAnalyser::checkParameters( void DocStringAnalyser::handleConstructor( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ) { static set const validTags = set{"author", "dev", "notice", "param"}; @@ -106,8 +106,8 @@ void DocStringAnalyser::handleConstructor( void DocStringAnalyser::handleCallable( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ) { static set const validTags = set{"author", "dev", "notice", "return", "param"}; @@ -116,16 +116,16 @@ void DocStringAnalyser::handleCallable( } void DocStringAnalyser::parseDocStrings( - Documented const& _node, - DocumentedAnnotation& _annotation, + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation, set const& _validTags, string const& _nodeName ) { DocStringParser parser; - if (_node.documentation() && !_node.documentation()->empty()) + if (_node.documentation() && !_node.documentation()->text()->empty()) { - if (!parser.parse(*_node.documentation(), m_errorReporter)) + if (!parser.parse(*_node.documentation()->text(), m_errorReporter)) m_errorOccured = true; _annotation.docTags = parser.tags(); } diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 653413346..2b9e23195 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -51,24 +51,24 @@ private: void checkParameters( CallableDeclaration const& _callable, - DocumentedAnnotation& _annotation + StructurallyDocumentedAnnotation& _annotation ); void handleConstructor( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ); void handleCallable( CallableDeclaration const& _callable, - Documented const& _node, - DocumentedAnnotation& _annotation + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation ); void parseDocStrings( - Documented const& _node, - DocumentedAnnotation& _annotation, + StructurallyDocumented const& _node, + StructurallyDocumentedAnnotation& _annotation, std::set const& _validTags, std::string const& _nodeName ); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d5ef041f9..5a5e561c9 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -345,6 +345,30 @@ private: std::vector m_localVariables; }; +/** + * The doxygen-style, structured documentation class that represents an AST node. + */ +class StructuredDocumentation: public ASTNode +{ +public: + StructuredDocumentation( + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _text + ): ASTNode(_id, _location), m_text(_text) + {} + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + /// @return A shared pointer of an ASTString. + /// Contains doxygen-style, structured documentation that is parsed later on. + ASTPointer const& text() const { return m_text; } + +private: + ASTPointer m_text; +}; + /** * Abstract class that is added to each AST node that can receive documentation. */ @@ -362,6 +386,24 @@ protected: ASTPointer m_documentation; }; +/** + * Abstract class that is added to each AST node that can receive a structured documentation. + */ +class StructurallyDocumented +{ +public: + virtual ~StructurallyDocumented() = default; + explicit StructurallyDocumented(ASTPointer const& _documentation): m_documentation(_documentation) {} + + /// @return A shared pointer of a FormalDocumentation. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& documentation() const { return m_documentation; } + +protected: + ASTPointer m_documentation; +}; + + /** * Abstract class that is added to AST nodes that can be marked as not being fully implemented */ @@ -385,21 +427,21 @@ protected: * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public Documented +class ContractDefinition: public Declaration, public StructurallyDocumented { public: ContractDefinition( int64_t _id, SourceLocation const& _location, ASTPointer const& _name, - ASTPointer const& _documentation, + ASTPointer const& _documentation, std::vector> const& _baseContracts, std::vector> const& _subNodes, ContractKind _contractKind = ContractKind::Contract, bool _abstract = false ): Declaration(_id, _location, _name), - Documented(_documentation), + StructurallyDocumented(_documentation), m_baseContracts(_baseContracts), m_subNodes(_subNodes), m_contractKind(_contractKind), @@ -681,7 +723,7 @@ protected: std::vector> m_overrides; }; -class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional +class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional { public: FunctionDefinition( @@ -693,14 +735,14 @@ public: Token _kind, bool _isVirtual, ASTPointer const& _overrides, - ASTPointer const& _documentation, + ASTPointer const& _documentation, ASTPointer const& _parameters, std::vector> const& _modifiers, ASTPointer const& _returnParameters, ASTPointer const& _body ): CallableDeclaration(_id, _location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters), - Documented(_documentation), + StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), m_kind(_kind), @@ -870,21 +912,21 @@ private: /** * Definition of a function modifier. */ -class ModifierDefinition: public CallableDeclaration, public Documented +class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented { public: ModifierDefinition( int64_t _id, SourceLocation const& _location, ASTPointer const& _name, - ASTPointer const& _documentation, + ASTPointer const& _documentation, ASTPointer const& _parameters, bool _isVirtual, ASTPointer const& _overrides, ASTPointer const& _body ): CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides), - Documented(_documentation), + StructurallyDocumented(_documentation), m_body(_body) { } @@ -935,19 +977,19 @@ private: /** * Definition of a (loggable) event. */ -class EventDefinition: public CallableDeclaration, public Documented +class EventDefinition: public CallableDeclaration, public StructurallyDocumented { public: EventDefinition( int64_t _id, SourceLocation const& _location, ASTPointer const& _name, - ASTPointer const& _documentation, + ASTPointer const& _documentation, ASTPointer const& _parameters, bool _anonymous = false ): CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters), - Documented(_documentation), + StructurallyDocumented(_documentation), m_anonymous(_anonymous) { } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index ae447f884..86636684c 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -56,9 +56,9 @@ struct DocTag std::string paramName; ///< Only used for @param, stores the parameter name. }; -struct DocumentedAnnotation +struct StructurallyDocumentedAnnotation { - virtual ~DocumentedAnnotation() = default; + virtual ~StructurallyDocumentedAnnotation() = default; /// Mapping docstring tag name -> content. std::multimap docTags; }; @@ -101,7 +101,7 @@ struct TypeDeclarationAnnotation: DeclarationAnnotation std::string canonicalName; }; -struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation +struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation { /// List of functions without a body. Can also contain functions from base classes. std::vector unimplementedFunctions; @@ -122,15 +122,15 @@ struct CallableDeclarationAnnotation: DeclarationAnnotation std::set baseFunctions; }; -struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, DocumentedAnnotation +struct FunctionDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; -struct EventDefinitionAnnotation: CallableDeclarationAnnotation, DocumentedAnnotation +struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; -struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, DocumentedAnnotation +struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; @@ -142,7 +142,7 @@ struct VariableDeclarationAnnotation: DeclarationAnnotation std::set baseFunctions; }; -struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation +struct StatementAnnotation: ASTAnnotation { }; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index a455f30b2..38da35218 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -93,6 +93,7 @@ class PrimaryExpression; class Identifier; class ElementaryTypeNameExpression; class Literal; +class StructuredDocumentation; class VariableScope; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 0ffbbd31a..90dad003a 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -92,6 +92,7 @@ public: virtual bool visit(Identifier& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } virtual bool visit(Literal& _node) { return visitNode(_node); } + virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } @@ -143,6 +144,7 @@ public: virtual void endVisit(Identifier& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } virtual void endVisit(Literal& _node) { endVisitNode(_node); } + virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); } protected: /// Generic function called by default for each node, to be overridden by derived classes @@ -207,6 +209,7 @@ public: virtual bool visit(Identifier const& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); } virtual bool visit(Literal const& _node) { return visitNode(_node); } + virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } @@ -258,6 +261,7 @@ public: virtual void endVisit(Identifier const& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } virtual void endVisit(Literal const& _node) { endVisitNode(_node); } + virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); } protected: /// Generic function called by default for each node, to be overridden by derived classes diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 4ca48e514..bc0123d51 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -67,10 +67,24 @@ void ImportDirective::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void StructuredDocumentation::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void StructuredDocumentation::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void ContractDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); listAccept(m_baseContracts, _visitor); listAccept(m_subNodes, _visitor); } @@ -81,6 +95,8 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); listAccept(m_baseContracts, _visitor); listAccept(m_subNodes, _visitor); } @@ -203,6 +219,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); m_parameters->accept(_visitor); @@ -219,6 +237,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); m_parameters->accept(_visitor); @@ -263,6 +283,8 @@ void ModifierDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); @@ -275,6 +297,8 @@ void ModifierDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); @@ -308,14 +332,22 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const void EventDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) + { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); + } _visitor.endVisit(*this); } void EventDefinition::accept(ASTConstVisitor& _visitor) const { if (_visitor.visit(*this)) + { + if (m_documentation) + m_documentation->accept(_visitor); m_parameters->accept(_visitor); + } _visitor.endVisit(*this); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f23ae8a19..dbeb63033 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3323,13 +3323,13 @@ Type const* FunctionType::selfType() const return m_parameterTypes.at(0); } -ASTPointer FunctionType::documentation() const +ASTPointer FunctionType::documentation() const { - auto function = dynamic_cast(m_declaration); + auto function = dynamic_cast(m_declaration); if (function) return function->documentation(); - return ASTPointer(); + return ASTPointer(); } bool FunctionType::padArguments() const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7ad662dbb..669250970 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1208,9 +1208,9 @@ public: /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; bool isPayable() const { return m_stateMutability == StateMutability::Payable; } - /// @return A shared pointer of an ASTString. - /// Can contain a nullptr in which case indicates absence of documentation - ASTPointer documentation() const; + /// @return A shared pointer of StructuredDocumentation. + /// Can contain a nullptr in which case indicates absence of documentation. + ASTPointer documentation() const; /// true iff arguments are to be padded to multiples of 32 bytes for external calls /// The only functions that do not pad are hash functions, the low-level call functions diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6de3a3e5b..2b0e9131f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -135,6 +135,19 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector c ); } +ASTPointer Parser::parseStructuredDocumentation() +{ + if (m_scanner->currentCommentLiteral() != "") + { + ASTNodeFactory nodeFactory{*this}; + nodeFactory.setLocation(m_scanner->currentCommentLocation()); + return nodeFactory.createNode( + make_shared(m_scanner->currentCommentLiteral()) + ); + } + return nullptr; +} + ASTPointer Parser::parsePragmaDirective() { RecursionGuard recursionGuard(*this); @@ -276,14 +289,13 @@ ASTPointer Parser::parseContractDefinition() RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name = nullptr; - ASTPointer docString; + ASTPointer documentation; vector> baseContracts; vector> subNodes; std::pair contractKind{}; try { - if (m_scanner->currentCommentLiteral() != "") - docString = make_shared(m_scanner->currentCommentLiteral()); + documentation = parseStructuredDocumentation(); contractKind = parseContractKind(); name = expectIdentifierToken(); if (m_scanner->currentToken() == Token::Is) @@ -350,7 +362,7 @@ ASTPointer Parser::parseContractDefinition() expectToken(Token::RBrace); return nodeFactory.createNode( name, - docString, + documentation, baseContracts, subNodes, contractKind.first, @@ -538,9 +550,7 @@ ASTPointer Parser::parseFunctionDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->currentCommentLiteral() != "") - docstring = make_shared(m_scanner->currentCommentLiteral()); + ASTPointer documentation = parseStructuredDocumentation(); Token kind = m_scanner->currentToken(); ASTPointer name; @@ -598,7 +608,7 @@ ASTPointer Parser::parseFunctionDefinition() kind, header.isVirtual, header.overrides, - docstring, + documentation, header.parameters, header.modifiers, header.returnParameters, @@ -792,9 +802,7 @@ ASTPointer Parser::parseModifierDefinition() m_insideModifier = true; ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->currentCommentLiteral() != "") - docstring = make_shared(m_scanner->currentCommentLiteral()); + ASTPointer documentation = parseStructuredDocumentation(); expectToken(Token::Modifier); ASTPointer name(expectIdentifierToken()); @@ -835,16 +843,14 @@ ASTPointer Parser::parseModifierDefinition() ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); - return nodeFactory.createNode(name, docstring, parameters, isVirtual, overrides, block); + return nodeFactory.createNode(name, documentation, parameters, isVirtual, overrides, block); } ASTPointer Parser::parseEventDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - ASTPointer docstring; - if (m_scanner->currentCommentLiteral() != "") - docstring = make_shared(m_scanner->currentCommentLiteral()); + ASTPointer documentation = parseStructuredDocumentation(); expectToken(Token::Event); ASTPointer name(expectIdentifierToken()); @@ -861,7 +867,7 @@ ASTPointer Parser::parseEventDefinition() } nodeFactory.markEndPosition(); expectToken(Token::Semicolon); - return nodeFactory.createNode(name, docstring, parameters, anonymous); + return nodeFactory.createNode(name, documentation, parameters, anonymous); } ASTPointer Parser::parseUsingDirective() @@ -1355,7 +1361,7 @@ ASTPointer Parser::parseEmitStatement(ASTPointer const if (m_scanner->currentToken() != Token::Period) break; m_scanner->next(); - }; + } auto eventName = expressionFromIndexAccessStructure(iap); expectToken(Token::LParen); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index e6d244fdc..25bb2b93f 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -80,6 +80,7 @@ private: ///@{ ///@name Parsing functions for the AST nodes void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector const& _tokens, std::vector const& _literals); + ASTPointer parseStructuredDocumentation(); ASTPointer parsePragmaDirective(); ASTPointer parseImportDirective(); /// @returns an std::pair, where diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 3087b4b0f..3f70f9160 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -98,7 +98,7 @@ void checkFunctionNatspec( std::string const& _expectedDoc ) { - auto doc = _function->documentation(); + auto doc = _function->documentation()->text(); BOOST_CHECK_MESSAGE(doc != nullptr, "Function does not have Natspec Doc as expected"); BOOST_CHECK_EQUAL(*doc, _expectedDoc); }