Introduce AST node for structured documentation.

This commit is contained in:
Erik Kundt 2020-01-29 23:13:42 +01:00
parent e41155cf48
commit ec27c2e507
13 changed files with 147 additions and 59 deletions

View File

@ -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;

View File

@ -73,7 +73,7 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
void DocStringAnalyser::checkParameters(
CallableDeclaration const& _callable,
DocumentedAnnotation& _annotation
StructurallyDocumentedAnnotation& _annotation
)
{
set<string> 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<string> const validTags = set<string>{"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<string> const validTags = set<string>{"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<string> 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();
}

View File

@ -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<std::string> const& _validTags,
std::string const& _nodeName
);

View File

@ -345,6 +345,30 @@ private:
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.
*/
@ -362,6 +386,24 @@ protected:
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
*/
@ -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<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<StructuredDocumentation> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<ASTNode>> 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<ASTPointer<UserDefinedTypeName>> 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<OverrideSpecifier> const& _overrides,
ASTPointer<ASTString> const& _documentation,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> 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<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<Block> 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<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _anonymous = false
):
CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters),
Documented(_documentation),
StructurallyDocumented(_documentation),
m_anonymous(_anonymous)
{
}

View File

@ -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<std::string, DocTag> 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<FunctionDefinition const*> unimplementedFunctions;
@ -122,15 +122,15 @@ struct CallableDeclarationAnnotation: DeclarationAnnotation
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;
};
struct StatementAnnotation: ASTAnnotation, DocumentedAnnotation
struct StatementAnnotation: ASTAnnotation
{
};

View File

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

View File

@ -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

View File

@ -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);
}

View File

@ -3323,13 +3323,13 @@ Type const* FunctionType::selfType() const
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)
return function->documentation();
return ASTPointer<ASTString>();
return ASTPointer<StructuredDocumentation>();
}
bool FunctionType::padArguments() const

View File

@ -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<ASTString> documentation() const;
/// @return A shared pointer of StructuredDocumentation.
/// Can contain a nullptr in which case indicates absence of documentation.
ASTPointer<StructuredDocumentation> 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

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

View File

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

View File

@ -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);
}