Merge pull request #7834 from ethereum/docstring-ast-node

[natspec] Introduce AST node for structured documentation
This commit is contained in:
chriseth 2020-01-30 19:04:52 +01:00 committed by GitHub
commit 2098dbade9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 327 additions and 157 deletions

View File

@ -6,6 +6,7 @@ Language Features:
Compiler Features: Compiler Features:
* Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input.
* AST: Add a new node for doxygen-style, structured documentation that can be received by contract, function, event and modifier definitions.
Bugfixes: Bugfixes:

View File

@ -428,6 +428,7 @@ Token Scanner::scanSlash()
// doxygen style /// comment // doxygen style /// comment
Token comment; Token comment;
m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source;
comment = scanSingleLineDocComment(); comment = scanSingleLineDocComment();
m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].location.end = sourcePos();
m_skippedComments[NextNext].token = comment; m_skippedComments[NextNext].token = comment;
@ -454,6 +455,7 @@ Token Scanner::scanSlash()
// we actually have a multiline documentation comment // we actually have a multiline documentation comment
Token comment; Token comment;
m_skippedComments[NextNext].location.start = firstSlashPosition; m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source;
comment = scanMultiLineDocComment(); comment = scanMultiLineDocComment();
m_skippedComments[NextNext].location.end = sourcePos(); m_skippedComments[NextNext].location.end = sourcePos();
m_skippedComments[NextNext].token = comment; m_skippedComments[NextNext].token = comment;

View File

@ -73,7 +73,7 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
void DocStringAnalyser::checkParameters( void DocStringAnalyser::checkParameters(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
DocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
) )
{ {
set<string> validParams; set<string> validParams;
@ -95,8 +95,8 @@ void DocStringAnalyser::checkParameters(
void DocStringAnalyser::handleConstructor( void DocStringAnalyser::handleConstructor(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
Documented const& _node, StructurallyDocumented const& _node,
DocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
) )
{ {
static set<string> const validTags = set<string>{"author", "dev", "notice", "param"}; static set<string> const validTags = set<string>{"author", "dev", "notice", "param"};
@ -106,8 +106,8 @@ void DocStringAnalyser::handleConstructor(
void DocStringAnalyser::handleCallable( void DocStringAnalyser::handleCallable(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
Documented const& _node, StructurallyDocumented const& _node,
DocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
) )
{ {
static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"}; static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"};
@ -116,16 +116,16 @@ void DocStringAnalyser::handleCallable(
} }
void DocStringAnalyser::parseDocStrings( void DocStringAnalyser::parseDocStrings(
Documented const& _node, StructurallyDocumented const& _node,
DocumentedAnnotation& _annotation, StructurallyDocumentedAnnotation& _annotation,
set<string> const& _validTags, set<string> const& _validTags,
string const& _nodeName string const& _nodeName
) )
{ {
DocStringParser parser; 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; m_errorOccured = true;
_annotation.docTags = parser.tags(); _annotation.docTags = parser.tags();
} }

View File

@ -51,24 +51,24 @@ private:
void checkParameters( void checkParameters(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
DocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
); );
void handleConstructor( void handleConstructor(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
Documented const& _node, StructurallyDocumented const& _node,
DocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
); );
void handleCallable( void handleCallable(
CallableDeclaration const& _callable, CallableDeclaration const& _callable,
Documented const& _node, StructurallyDocumented const& _node,
DocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
); );
void parseDocStrings( void parseDocStrings(
Documented const& _node, StructurallyDocumented const& _node,
DocumentedAnnotation& _annotation, StructurallyDocumentedAnnotation& _annotation,
std::set<std::string> const& _validTags, std::set<std::string> const& _validTags,
std::string const& _nodeName std::string const& _nodeName
); );

View File

@ -345,6 +345,30 @@ private:
std::vector<VariableDeclaration const*> m_localVariables; std::vector<VariableDeclaration const*> 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<ASTString> 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<ASTString> const& text() const { return m_text; }
private:
ASTPointer<ASTString> m_text;
};
/** /**
* Abstract class that is added to each AST node that can receive documentation. * Abstract class that is added to each AST node that can receive documentation.
*/ */
@ -362,6 +386,24 @@ protected:
ASTPointer<ASTString> m_documentation; ASTPointer<ASTString> 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<StructuredDocumentation> const& _documentation): m_documentation(_documentation) {}
/// @return A shared pointer of a FormalDocumentation.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<StructuredDocumentation> const& documentation() const { return m_documentation; }
protected:
ASTPointer<StructuredDocumentation> m_documentation;
};
/** /**
* Abstract class that is added to AST nodes that can be marked as not being fully implemented * 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 * document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations. * finally all function declarations.
*/ */
class ContractDefinition: public Declaration, public Documented class ContractDefinition: public Declaration, public StructurallyDocumented
{ {
public: public:
ContractDefinition( ContractDefinition(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<StructuredDocumentation> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts, std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<ASTNode>> const& _subNodes, std::vector<ASTPointer<ASTNode>> const& _subNodes,
ContractKind _contractKind = ContractKind::Contract, ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false bool _abstract = false
): ):
Declaration(_id, _location, _name), Declaration(_id, _location, _name),
Documented(_documentation), StructurallyDocumented(_documentation),
m_baseContracts(_baseContracts), m_baseContracts(_baseContracts),
m_subNodes(_subNodes), m_subNodes(_subNodes),
m_contractKind(_contractKind), m_contractKind(_contractKind),
@ -681,7 +723,7 @@ protected:
std::vector<ASTPointer<UserDefinedTypeName>> m_overrides; std::vector<ASTPointer<UserDefinedTypeName>> m_overrides;
}; };
class FunctionDefinition: public CallableDeclaration, public Documented, public ImplementationOptional class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional
{ {
public: public:
FunctionDefinition( FunctionDefinition(
@ -693,14 +735,14 @@ public:
Token _kind, Token _kind,
bool _isVirtual, bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides, ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<ASTString> const& _documentation, ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body ASTPointer<Block> const& _body
): ):
CallableDeclaration(_id, _location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters), CallableDeclaration(_id, _location, _name, _visibility, _parameters, _isVirtual, _overrides, _returnParameters),
Documented(_documentation), StructurallyDocumented(_documentation),
ImplementationOptional(_body != nullptr), ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability), m_stateMutability(_stateMutability),
m_kind(_kind), m_kind(_kind),
@ -870,21 +912,21 @@ private:
/** /**
* Definition of a function modifier. * Definition of a function modifier.
*/ */
class ModifierDefinition: public CallableDeclaration, public Documented class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented
{ {
public: public:
ModifierDefinition( ModifierDefinition(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isVirtual, bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides, ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<Block> const& _body ASTPointer<Block> const& _body
): ):
CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides), CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides),
Documented(_documentation), StructurallyDocumented(_documentation),
m_body(_body) m_body(_body)
{ {
} }
@ -935,19 +977,19 @@ private:
/** /**
* Definition of a (loggable) event. * Definition of a (loggable) event.
*/ */
class EventDefinition: public CallableDeclaration, public Documented class EventDefinition: public CallableDeclaration, public StructurallyDocumented
{ {
public: public:
EventDefinition( EventDefinition(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _anonymous = false bool _anonymous = false
): ):
CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters), CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters),
Documented(_documentation), StructurallyDocumented(_documentation),
m_anonymous(_anonymous) m_anonymous(_anonymous)
{ {
} }

View File

@ -56,9 +56,9 @@ struct DocTag
std::string paramName; ///< Only used for @param, stores the parameter name. 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. /// Mapping docstring tag name -> content.
std::multimap<std::string, DocTag> docTags; std::multimap<std::string, DocTag> docTags;
}; };
@ -101,7 +101,7 @@ struct TypeDeclarationAnnotation: DeclarationAnnotation
std::string canonicalName; std::string canonicalName;
}; };
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnotation struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
{ {
/// List of functions without a body. Can also contain functions from base classes. /// List of functions without a body. Can also contain functions from base classes.
std::vector<FunctionDefinition const*> unimplementedFunctions; std::vector<FunctionDefinition const*> unimplementedFunctions;
@ -122,15 +122,15 @@ struct CallableDeclarationAnnotation: DeclarationAnnotation
std::set<CallableDeclaration const*> baseFunctions; std::set<CallableDeclaration const*> 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<CallableDeclaration const*> baseFunctions; std::set<CallableDeclaration const*> baseFunctions;
}; };
struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation struct StatementAnnotation: ASTAnnotation
{ {
}; };

View File

@ -93,6 +93,7 @@ class PrimaryExpression;
class Identifier; class Identifier;
class ElementaryTypeNameExpression; class ElementaryTypeNameExpression;
class Literal; class Literal;
class StructuredDocumentation;
class VariableScope; class VariableScope;

View File

@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
{ {
setJsonNode(_node, "ContractDefinition", { setJsonNode(_node, "ContractDefinition", {
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("contractKind", contractKind(_node.contractKind())), make_pair("contractKind", contractKind(_node.contractKind())),
make_pair("abstract", _node.abstract()), make_pair("abstract", _node.abstract()),
make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()), make_pair("fullyImplemented", _node.annotation().unimplementedFunctions.empty()),
@ -349,7 +349,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{ {
std::vector<pair<string, Json::Value>> attributes = { std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("kind", TokenTraits::toString(_node.kind())), make_pair("kind", TokenTraits::toString(_node.kind())),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
@ -400,7 +400,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node)
{ {
std::vector<pair<string, Json::Value>> attributes = { std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("parameters", toJson(_node.parameterList())), make_pair("parameters", toJson(_node.parameterList())),
make_pair("virtual", _node.markedVirtual()), make_pair("virtual", _node.markedVirtual()),
@ -427,7 +427,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
m_inEvent = true; m_inEvent = true;
setJsonNode(_node, "EventDefinition", { setJsonNode(_node, "EventDefinition", {
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("parameters", toJson(_node.parameterList())), make_pair("parameters", toJson(_node.parameterList())),
make_pair("anonymous", _node.isAnonymous()) make_pair("anonymous", _node.isAnonymous())
}); });
@ -833,6 +833,17 @@ bool ASTJsonConverter::visit(Literal const& _node)
return false; return false;
} }
bool ASTJsonConverter::visit(StructuredDocumentation const& _node)
{
Json::Value text{*_node.text()};
std::vector<pair<string, Json::Value>> attributes = {
make_pair("text", text)
};
setJsonNode(_node, "StructuredDocumentation", std::move(attributes));
return false;
}
void ASTJsonConverter::endVisit(EventDefinition const&) void ASTJsonConverter::endVisit(EventDefinition const&)
{ {

View File

@ -119,6 +119,7 @@ public:
bool visit(Identifier const& _node) override; bool visit(Identifier const& _node) override;
bool visit(ElementaryTypeNameExpression const& _node) override; bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override; bool visit(Literal const& _node) override;
bool visit(StructuredDocumentation const& _node) override;
void endVisit(EventDefinition const&) override; void endVisit(EventDefinition const&) override;

View File

@ -208,6 +208,8 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
return createElementaryTypeNameExpression(_json); return createElementaryTypeNameExpression(_json);
if (nodeType == "Literal") if (nodeType == "Literal")
return createLiteral(_json); return createLiteral(_json);
if (nodeType == "StructuredDocumentation")
return createDocumentation(_json);
else else
astAssert(false, "Unknown type of ASTNode: " + nodeType); astAssert(false, "Unknown type of ASTNode: " + nodeType);
} }
@ -283,7 +285,7 @@ ASTPointer<ContractDefinition> ASTJsonImporter::createContractDefinition(Json::V
return createASTNode<ContractDefinition>( return createASTNode<ContractDefinition>(
_node, _node,
make_shared<ASTString>(_node["name"].asString()), make_shared<ASTString>(_node["name"].asString()),
nullOrASTString(_node, "documentation"), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
baseContracts, baseContracts,
subNodes, subNodes,
contractKind(_node), contractKind(_node),
@ -397,7 +399,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
kind, kind,
memberAsBool(_node, "virtual"), memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
nullOrASTString(_node, "documentation"), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")), createParameterList(member(_node, "parameters")),
modifiers, modifiers,
createParameterList(member(_node, "returnParameters")), createParameterList(member(_node, "returnParameters")),
@ -428,7 +430,7 @@ ASTPointer<ModifierDefinition> ASTJsonImporter::createModifierDefinition(Json::V
return createASTNode<ModifierDefinition>( return createASTNode<ModifierDefinition>(
_node, _node,
memberAsASTString(_node, "name"), memberAsASTString(_node, "name"),
nullOrASTString(_node,"documentation"), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")), createParameterList(member(_node, "parameters")),
memberAsBool(_node, "virtual"), memberAsBool(_node, "virtual"),
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
@ -453,7 +455,7 @@ ASTPointer<EventDefinition> ASTJsonImporter::createEventDefinition(Json::Value c
return createASTNode<EventDefinition>( return createASTNode<EventDefinition>(
_node, _node,
memberAsASTString(_node, "name"), memberAsASTString(_node, "name"),
nullOrASTString(_node, "documentation"), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
createParameterList(member(_node, "parameters")), createParameterList(member(_node, "parameters")),
memberAsBool(_node, "anonymous") memberAsBool(_node, "anonymous")
); );
@ -842,6 +844,18 @@ ASTPointer<ASTNode> ASTJsonImporter::createLiteral(Json::Value const& _node)
); );
} }
ASTPointer<StructuredDocumentation> ASTJsonImporter::createDocumentation(Json::Value const& _node)
{
static string const textString = "text";
astAssert(member(_node, textString).isString(), "'text' must be a string");
return createASTNode<StructuredDocumentation>(
_node,
make_shared<ASTString>(_node[textString].asString())
);
}
// ===== helper functions ========== // ===== helper functions ==========
Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name) Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name)

View File

@ -119,6 +119,7 @@ private:
ASTPointer<Identifier> createIdentifier(Json::Value const& _node); ASTPointer<Identifier> createIdentifier(Json::Value const& _node);
ASTPointer<ElementaryTypeNameExpression> createElementaryTypeNameExpression(Json::Value const& _node); ASTPointer<ElementaryTypeNameExpression> createElementaryTypeNameExpression(Json::Value const& _node);
ASTPointer<ASTNode> createLiteral(Json::Value const& _node); ASTPointer<ASTNode> createLiteral(Json::Value const& _node);
ASTPointer<StructuredDocumentation> createDocumentation(Json::Value const& _node);
///@} ///@}
// =============== general helper functions =================== // =============== general helper functions ===================

View File

@ -92,6 +92,7 @@ public:
virtual bool visit(Identifier& _node) { return visitNode(_node); } virtual bool visit(Identifier& _node) { return visitNode(_node); }
virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
virtual bool visit(Literal& _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(SourceUnit& _node) { endVisitNode(_node); }
virtual void endVisit(PragmaDirective& _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(Identifier& _node) { endVisitNode(_node); }
virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
virtual void endVisit(Literal& _node) { endVisitNode(_node); } virtual void endVisit(Literal& _node) { endVisitNode(_node); }
virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); }
protected: protected:
/// Generic function called by default for each node, to be overridden by derived classes /// 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(Identifier const& _node) { return visitNode(_node); }
virtual bool visit(ElementaryTypeNameExpression 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(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(SourceUnit const& _node) { endVisitNode(_node); }
virtual void endVisit(PragmaDirective 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(Identifier const& _node) { endVisitNode(_node); }
virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
virtual void endVisit(Literal const& _node) { endVisitNode(_node); } virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); }
protected: protected:
/// Generic function called by default for each node, to be overridden by derived classes /// Generic function called by default for each node, to be overridden by derived classes

View File

@ -67,10 +67,24 @@ void ImportDirective::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _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) void ContractDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
if (m_documentation)
m_documentation->accept(_visitor);
listAccept(m_baseContracts, _visitor); listAccept(m_baseContracts, _visitor);
listAccept(m_subNodes, _visitor); listAccept(m_subNodes, _visitor);
} }
@ -81,6 +95,8 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
if (m_documentation)
m_documentation->accept(_visitor);
listAccept(m_baseContracts, _visitor); listAccept(m_baseContracts, _visitor);
listAccept(m_subNodes, _visitor); listAccept(m_subNodes, _visitor);
} }
@ -203,6 +219,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
if (m_documentation)
m_documentation->accept(_visitor);
if (m_overrides) if (m_overrides)
m_overrides->accept(_visitor); m_overrides->accept(_visitor);
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
@ -219,6 +237,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
if (m_documentation)
m_documentation->accept(_visitor);
if (m_overrides) if (m_overrides)
m_overrides->accept(_visitor); m_overrides->accept(_visitor);
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
@ -263,6 +283,8 @@ void ModifierDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
if (m_documentation)
m_documentation->accept(_visitor);
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
if (m_overrides) if (m_overrides)
m_overrides->accept(_visitor); m_overrides->accept(_visitor);
@ -275,6 +297,8 @@ void ModifierDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
if (m_documentation)
m_documentation->accept(_visitor);
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
if (m_overrides) if (m_overrides)
m_overrides->accept(_visitor); m_overrides->accept(_visitor);
@ -308,14 +332,22 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
void EventDefinition::accept(ASTVisitor& _visitor) void EventDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
if (m_documentation)
m_documentation->accept(_visitor);
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void EventDefinition::accept(ASTConstVisitor& _visitor) const void EventDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
if (m_documentation)
m_documentation->accept(_visitor);
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

View File

@ -3323,13 +3323,13 @@ Type const* FunctionType::selfType() const
return m_parameterTypes.at(0); return m_parameterTypes.at(0);
} }
ASTPointer<ASTString> FunctionType::documentation() const ASTPointer<StructuredDocumentation> FunctionType::documentation() const
{ {
auto function = dynamic_cast<Documented const*>(m_declaration); auto function = dynamic_cast<StructurallyDocumented const*>(m_declaration);
if (function) if (function)
return function->documentation(); return function->documentation();
return ASTPointer<ASTString>(); return ASTPointer<StructuredDocumentation>();
} }
bool FunctionType::padArguments() const bool FunctionType::padArguments() const

View File

@ -1208,9 +1208,9 @@ public:
/// Currently, this will only return true for internal functions like keccak and ecrecover. /// Currently, this will only return true for internal functions like keccak and ecrecover.
bool isPure() const; bool isPure() const;
bool isPayable() const { return m_stateMutability == StateMutability::Payable; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
/// @return A shared pointer of an ASTString. /// @return A shared pointer of StructuredDocumentation.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation.
ASTPointer<ASTString> documentation() const; ASTPointer<StructuredDocumentation> documentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls /// 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 /// The only functions that do not pad are hash functions, the low-level call functions

View File

@ -135,6 +135,19 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> c
); );
} }
ASTPointer<StructuredDocumentation> Parser::parseStructuredDocumentation()
{
if (m_scanner->currentCommentLiteral() != "")
{
ASTNodeFactory nodeFactory{*this};
nodeFactory.setLocation(m_scanner->currentCommentLocation());
return nodeFactory.createNode<StructuredDocumentation>(
make_shared<ASTString>(m_scanner->currentCommentLiteral())
);
}
return nullptr;
}
ASTPointer<PragmaDirective> Parser::parsePragmaDirective() ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
@ -276,14 +289,13 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> name = nullptr; ASTPointer<ASTString> name = nullptr;
ASTPointer<ASTString> docString; ASTPointer<StructuredDocumentation> documentation;
vector<ASTPointer<InheritanceSpecifier>> baseContracts; vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<ASTNode>> subNodes; vector<ASTPointer<ASTNode>> subNodes;
std::pair<ContractKind, bool> contractKind{}; std::pair<ContractKind, bool> contractKind{};
try try
{ {
if (m_scanner->currentCommentLiteral() != "") documentation = parseStructuredDocumentation();
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
contractKind = parseContractKind(); contractKind = parseContractKind();
name = expectIdentifierToken(); name = expectIdentifierToken();
if (m_scanner->currentToken() == Token::Is) if (m_scanner->currentToken() == Token::Is)
@ -350,7 +362,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
expectToken(Token::RBrace); expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>( return nodeFactory.createNode<ContractDefinition>(
name, name,
docString, documentation,
baseContracts, baseContracts,
subNodes, subNodes,
contractKind.first, contractKind.first,
@ -538,9 +550,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<StructuredDocumentation> documentation = parseStructuredDocumentation();
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
Token kind = m_scanner->currentToken(); Token kind = m_scanner->currentToken();
ASTPointer<ASTString> name; ASTPointer<ASTString> name;
@ -598,7 +608,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
kind, kind,
header.isVirtual, header.isVirtual,
header.overrides, header.overrides,
docstring, documentation,
header.parameters, header.parameters,
header.modifiers, header.modifiers,
header.returnParameters, header.returnParameters,
@ -792,9 +802,7 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
m_insideModifier = true; m_insideModifier = true;
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<StructuredDocumentation> documentation = parseStructuredDocumentation();
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
expectToken(Token::Modifier); expectToken(Token::Modifier);
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
@ -835,16 +843,14 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, isVirtual, overrides, block); return nodeFactory.createNode<ModifierDefinition>(name, documentation, parameters, isVirtual, overrides, block);
} }
ASTPointer<EventDefinition> Parser::parseEventDefinition() ASTPointer<EventDefinition> Parser::parseEventDefinition()
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring; ASTPointer<StructuredDocumentation> documentation = parseStructuredDocumentation();
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
expectToken(Token::Event); expectToken(Token::Event);
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
@ -861,7 +867,7 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters, anonymous); return nodeFactory.createNode<EventDefinition>(name, documentation, parameters, anonymous);
} }
ASTPointer<UsingForDirective> Parser::parseUsingDirective() ASTPointer<UsingForDirective> Parser::parseUsingDirective()
@ -1355,7 +1361,7 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
if (m_scanner->currentToken() != Token::Period) if (m_scanner->currentToken() != Token::Period)
break; break;
m_scanner->next(); m_scanner->next();
}; }
auto eventName = expressionFromIndexAccessStructure(iap); auto eventName = expressionFromIndexAccessStructure(iap);
expectToken(Token::LParen); expectToken(Token::LParen);

View File

@ -80,6 +80,7 @@ private:
///@{ ///@{
///@name Parsing functions for the AST nodes ///@name Parsing functions for the AST nodes
void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector<Token> const& _tokens, std::vector<std::string> const& _literals); void parsePragmaVersion(langutil::SourceLocation const& _location, std::vector<Token> const& _tokens, std::vector<std::string> const& _literals);
ASTPointer<StructuredDocumentation> parseStructuredDocumentation();
ASTPointer<PragmaDirective> parsePragmaDirective(); ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ImportDirective> parseImportDirective();
/// @returns an std::pair<ContractKind, bool>, where /// @returns an std::pair<ContractKind, bool>, where

View File

@ -4,10 +4,10 @@
{ {
"C": "C":
[ [
1 2
] ]
}, },
"id": 2, "id": 3,
"nodeType": "SourceUnit", "nodeType": "SourceUnit",
"nodes": "nodes":
[ [
@ -16,17 +16,23 @@
"baseContracts": [], "baseContracts": [],
"contractDependencies": [], "contractDependencies": [],
"contractKind": "contract", "contractKind": "contract",
"documentation": "This contract is empty", "documentation":
"fullyImplemented": true, {
"id": 1, "id": 1,
"nodeType": "StructuredDocumentation",
"src": "0:27:1",
"text": "This contract is empty"
},
"fullyImplemented": true,
"id": 2,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
1 2
], ],
"name": "C", "name": "C",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
"nodes": [], "nodes": [],
"scope": 2, "scope": 3,
"src": "28:13:1" "src": "28:13:1"
} }
], ],
@ -38,10 +44,10 @@
{ {
"C": "C":
[ [
3 5
] ]
}, },
"id": 4, "id": 6,
"nodeType": "SourceUnit", "nodeType": "SourceUnit",
"nodes": "nodes":
[ [
@ -50,17 +56,23 @@
"baseContracts": [], "baseContracts": [],
"contractDependencies": [], "contractDependencies": [],
"contractKind": "contract", "contractKind": "contract",
"documentation": "This contract is empty\nand has a line-breaking comment.", "documentation":
{
"id": 4,
"nodeType": "StructuredDocumentation",
"src": "0:61:2",
"text": "This contract is empty\nand has a line-breaking comment."
},
"fullyImplemented": true, "fullyImplemented": true,
"id": 3, "id": 5,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
3 5
], ],
"name": "C", "name": "C",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
"nodes": [], "nodes": [],
"scope": 4, "scope": 6,
"src": "62:13:2" "src": "62:13:2"
} }
], ],
@ -72,10 +84,10 @@
{ {
"C": "C":
[ [
15 20
] ]
}, },
"id": 16, "id": 21,
"nodeType": "SourceUnit", "nodeType": "SourceUnit",
"nodes": "nodes":
[ [
@ -86,10 +98,10 @@
"contractKind": "contract", "contractKind": "contract",
"documentation": null, "documentation": null,
"fullyImplemented": true, "fullyImplemented": true,
"id": 15, "id": 20,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
15 20
], ],
"name": "C", "name": "C",
"nodeType": "ContractDefinition", "nodeType": "ContractDefinition",
@ -97,13 +109,19 @@
[ [
{ {
"anonymous": false, "anonymous": false,
"documentation": "Some comment on Evt.", "documentation":
"id": 6, {
"id": 7,
"nodeType": "StructuredDocumentation",
"src": "15:26:3",
"text": "Some comment on Evt."
},
"id": 9,
"name": "Evt", "name": "Evt",
"nodeType": "EventDefinition", "nodeType": "EventDefinition",
"parameters": "parameters":
{ {
"id": 5, "id": 8,
"nodeType": "ParameterList", "nodeType": "ParameterList",
"parameters": [], "parameters": [],
"src": "51:2:3" "src": "51:2:3"
@ -113,26 +131,32 @@
{ {
"body": "body":
{ {
"id": 9, "id": 13,
"nodeType": "Block", "nodeType": "Block",
"src": "99:6:3", "src": "99:6:3",
"statements": "statements":
[ [
{ {
"id": 8, "id": 12,
"nodeType": "PlaceholderStatement", "nodeType": "PlaceholderStatement",
"src": "101:1:3" "src": "101:1:3"
} }
] ]
}, },
"documentation": "Some comment on mod.", "documentation":
{
"id": 10, "id": 10,
"nodeType": "StructuredDocumentation",
"src": "57:26:3",
"text": "Some comment on mod."
},
"id": 14,
"name": "mod", "name": "mod",
"nodeType": "ModifierDefinition", "nodeType": "ModifierDefinition",
"overrides": null, "overrides": null,
"parameters": "parameters":
{ {
"id": 7, "id": 11,
"nodeType": "ParameterList", "nodeType": "ParameterList",
"parameters": [], "parameters": [],
"src": "96:2:3" "src": "96:2:3"
@ -144,14 +168,20 @@
{ {
"body": "body":
{ {
"id": 13, "id": 18,
"nodeType": "Block", "nodeType": "Block",
"src": "155:2:3", "src": "155:2:3",
"statements": [] "statements": []
}, },
"documentation": "Some comment on fn.", "documentation":
{
"id": 15,
"nodeType": "StructuredDocumentation",
"src": "108:25:3",
"text": "Some comment on fn."
},
"functionSelector": "a4a2c40b", "functionSelector": "a4a2c40b",
"id": 14, "id": 19,
"implemented": true, "implemented": true,
"kind": "function", "kind": "function",
"modifiers": [], "modifiers": [],
@ -160,26 +190,26 @@
"overrides": null, "overrides": null,
"parameters": "parameters":
{ {
"id": 11, "id": 16,
"nodeType": "ParameterList", "nodeType": "ParameterList",
"parameters": [], "parameters": [],
"src": "145:2:3" "src": "145:2:3"
}, },
"returnParameters": "returnParameters":
{ {
"id": 12, "id": 17,
"nodeType": "ParameterList", "nodeType": "ParameterList",
"parameters": [], "parameters": [],
"src": "155:0:3" "src": "155:0:3"
}, },
"scope": 15, "scope": 20,
"src": "134:23:3", "src": "134:23:3",
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"virtual": false, "virtual": false,
"visibility": "public" "visibility": "public"
} }
], ],
"scope": 16, "scope": 21,
"src": "0:159:3" "src": "0:159:3"
} }
], ],

View File

@ -6,7 +6,7 @@
{ {
"C": "C":
[ [
15 20
] ]
} }
}, },
@ -29,10 +29,10 @@
"fullyImplemented": true, "fullyImplemented": true,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
15 20
], ],
"name": "C", "name": "C",
"scope": 16 "scope": 21
}, },
"children": "children":
[ [
@ -40,11 +40,19 @@
"attributes": "attributes":
{ {
"anonymous": false, "anonymous": false,
"documentation": "Some comment on Evt.",
"name": "Evt" "name": "Evt"
}, },
"children": "children":
[ [
{
"attributes":
{
"text": "Some comment on Evt."
},
"id": 7,
"name": "StructuredDocumentation",
"src": "15:26:3"
},
{ {
"attributes": "attributes":
{ {
@ -54,19 +62,18 @@
] ]
}, },
"children": [], "children": [],
"id": 5, "id": 8,
"name": "ParameterList", "name": "ParameterList",
"src": "51:2:3" "src": "51:2:3"
} }
], ],
"id": 6, "id": 9,
"name": "EventDefinition", "name": "EventDefinition",
"src": "42:12:3" "src": "42:12:3"
}, },
{ {
"attributes": "attributes":
{ {
"documentation": "Some comment on mod.",
"name": "mod", "name": "mod",
"overrides": null, "overrides": null,
"virtual": false, "virtual": false,
@ -77,55 +84,12 @@
{ {
"attributes": "attributes":
{ {
"parameters": "text": "Some comment on mod."
[
null
]
}, },
"children": [],
"id": 7,
"name": "ParameterList",
"src": "96:2:3"
},
{
"children":
[
{
"id": 8,
"name": "PlaceholderStatement",
"src": "101:1:3"
}
],
"id": 9,
"name": "Block",
"src": "99:6:3"
}
],
"id": 10, "id": 10,
"name": "ModifierDefinition", "name": "StructuredDocumentation",
"src": "84:21:3" "src": "57:26:3"
}, },
{
"attributes":
{
"documentation": "Some comment on fn.",
"functionSelector": "a4a2c40b",
"implemented": true,
"isConstructor": false,
"kind": "function",
"modifiers":
[
null
],
"name": "fn",
"overrides": null,
"scope": 15,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
"children":
[
{ {
"attributes": "attributes":
{ {
@ -137,6 +101,66 @@
"children": [], "children": [],
"id": 11, "id": 11,
"name": "ParameterList", "name": "ParameterList",
"src": "96:2:3"
},
{
"children":
[
{
"id": 12,
"name": "PlaceholderStatement",
"src": "101:1:3"
}
],
"id": 13,
"name": "Block",
"src": "99:6:3"
}
],
"id": 14,
"name": "ModifierDefinition",
"src": "84:21:3"
},
{
"attributes":
{
"functionSelector": "a4a2c40b",
"implemented": true,
"isConstructor": false,
"kind": "function",
"modifiers":
[
null
],
"name": "fn",
"overrides": null,
"scope": 20,
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
"children":
[
{
"attributes":
{
"text": "Some comment on fn."
},
"id": 15,
"name": "StructuredDocumentation",
"src": "108:25:3"
},
{
"attributes":
{
"parameters":
[
null
]
},
"children": [],
"id": 16,
"name": "ParameterList",
"src": "145:2:3" "src": "145:2:3"
}, },
{ {
@ -148,7 +172,7 @@
] ]
}, },
"children": [], "children": [],
"id": 12, "id": 17,
"name": "ParameterList", "name": "ParameterList",
"src": "155:0:3" "src": "155:0:3"
}, },
@ -161,22 +185,22 @@
] ]
}, },
"children": [], "children": [],
"id": 13, "id": 18,
"name": "Block", "name": "Block",
"src": "155:2:3" "src": "155:2:3"
} }
], ],
"id": 14, "id": 19,
"name": "FunctionDefinition", "name": "FunctionDefinition",
"src": "134:23:3" "src": "134:23:3"
} }
], ],
"id": 15, "id": 20,
"name": "ContractDefinition", "name": "ContractDefinition",
"src": "0:159:3" "src": "0:159:3"
} }
], ],
"id": 16, "id": 21,
"name": "SourceUnit", "name": "SourceUnit",
"src": "0:160:3" "src": "0:160:3"
} }

View File

@ -98,7 +98,7 @@ void checkFunctionNatspec(
std::string const& _expectedDoc 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_MESSAGE(doc != nullptr, "Function does not have Natspec Doc as expected");
BOOST_CHECK_EQUAL(*doc, _expectedDoc); BOOST_CHECK_EQUAL(*doc, _expectedDoc);
} }