mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8532 from aarlt/structured-docs-variables-aarlt
Allow NatSpec comments for state variables
This commit is contained in:
commit
22d5caa979
@ -7,7 +7,7 @@ Compiler Features:
|
|||||||
* Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism.
|
* Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism.
|
||||||
* SMTChecker: Support array ``length``.
|
* SMTChecker: Support array ``length``.
|
||||||
* SMTChecker: Support array ``push`` and ``pop``.
|
* SMTChecker: Support array ``push`` and ``pop``.
|
||||||
|
* Add support for natspec comments on state variables.
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Optimizer: Fixed a bug in BlockDeDuplicator.
|
* Optimizer: Fixed a bug in BlockDeDuplicator.
|
||||||
|
@ -255,9 +255,9 @@ which only need to be created if there is a dispute.
|
|||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
function createDSalted(bytes32 salt, uint arg) public {
|
function createDSalted(bytes32 salt, uint arg) public {
|
||||||
/// This complicated expression just tells you how the address
|
// This complicated expression just tells you how the address
|
||||||
/// can be pre-computed. It is just there for illustration.
|
// can be pre-computed. It is just there for illustration.
|
||||||
/// You actually only need ``new D{salt: salt}(arg)``.
|
// You actually only need ``new D{salt: salt}(arg)``.
|
||||||
address predictedAddress = address(uint(keccak256(abi.encodePacked(
|
address predictedAddress = address(uint(keccak256(abi.encodePacked(
|
||||||
byte(0xff),
|
byte(0xff),
|
||||||
address(this),
|
address(this),
|
||||||
|
@ -28,6 +28,9 @@ Documentation Example
|
|||||||
Documentation is inserted above each ``class``, ``interface`` and
|
Documentation is inserted above each ``class``, ``interface`` and
|
||||||
``function`` using the doxygen notation format.
|
``function`` using the doxygen notation format.
|
||||||
|
|
||||||
|
Note: a ``public`` state variable is equivalent to a ``function``
|
||||||
|
for the purposes of NatSpec.
|
||||||
|
|
||||||
- For Solidity you may choose ``///`` for single or multi-line
|
- For Solidity you may choose ``///`` for single or multi-line
|
||||||
comments, or ``/**`` and ending with ``*/``.
|
comments, or ``/**`` and ending with ``*/``.
|
||||||
|
|
||||||
@ -82,10 +85,10 @@ Tag
|
|||||||
=========== =============================================================================== =============================
|
=========== =============================================================================== =============================
|
||||||
``@title`` A title that should describe the contract/interface contract, interface
|
``@title`` A title that should describe the contract/interface contract, interface
|
||||||
``@author`` The name of the author contract, interface, function
|
``@author`` The name of the author contract, interface, function
|
||||||
``@notice`` Explain to an end user what this does contract, interface, function
|
``@notice`` Explain to an end user what this does contract, interface, function, public state variable
|
||||||
``@dev`` Explain to a developer any extra details contract, interface, function
|
``@dev`` Explain to a developer any extra details contract, interface, function, state variable
|
||||||
``@param`` Documents a parameter just like in doxygen (must be followed by parameter name) function
|
``@param`` Documents a parameter just like in doxygen (must be followed by parameter name) function
|
||||||
``@return`` Documents the return variables of a contract's function function
|
``@return`` Documents the return variables of a contract's function function, public state variable
|
||||||
=========== =============================================================================== =============================
|
=========== =============================================================================== =============================
|
||||||
|
|
||||||
If your function returns multiple values, like ``(int quotient, int remainder)``
|
If your function returns multiple values, like ``(int quotient, int remainder)``
|
||||||
|
@ -63,7 +63,7 @@ complete contract):
|
|||||||
|
|
||||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||||
contract Fund {
|
contract Fund {
|
||||||
/// Mapping of ether shares of the contract.
|
/// @dev Mapping of ether shares of the contract.
|
||||||
mapping(address => uint) shares;
|
mapping(address => uint) shares;
|
||||||
/// Withdraw your share.
|
/// Withdraw your share.
|
||||||
function withdraw() public {
|
function withdraw() public {
|
||||||
@ -87,7 +87,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
|||||||
|
|
||||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||||
contract Fund {
|
contract Fund {
|
||||||
/// Mapping of ether shares of the contract.
|
/// @dev Mapping of ether shares of the contract.
|
||||||
mapping(address => uint) shares;
|
mapping(address => uint) shares;
|
||||||
/// Withdraw your share.
|
/// Withdraw your share.
|
||||||
function withdraw() public {
|
function withdraw() public {
|
||||||
@ -106,7 +106,7 @@ outlined further below:
|
|||||||
pragma solidity >=0.4.11 <0.7.0;
|
pragma solidity >=0.4.11 <0.7.0;
|
||||||
|
|
||||||
contract Fund {
|
contract Fund {
|
||||||
/// Mapping of ether shares of the contract.
|
/// @dev Mapping of ether shares of the contract.
|
||||||
mapping(address => uint) shares;
|
mapping(address => uint) shares;
|
||||||
/// Withdraw your share.
|
/// Withdraw your share.
|
||||||
function withdraw() public {
|
function withdraw() public {
|
||||||
|
@ -415,7 +415,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete
|
|||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Proxy {
|
contract Proxy {
|
||||||
/// Address of the client contract managed by proxy i.e., this contract
|
/// @dev Address of the client contract managed by proxy i.e., this contract
|
||||||
address client;
|
address client;
|
||||||
|
|
||||||
constructor(address _client) public {
|
constructor(address _client) public {
|
||||||
|
@ -57,6 +57,31 @@ bool DocStringAnalyser::visit(FunctionDefinition const& _function)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
||||||
|
{
|
||||||
|
if (_variable.isStateVariable())
|
||||||
|
{
|
||||||
|
static set<string> const validPublicTags = set<string>{"dev", "notice", "return", "title", "author"};
|
||||||
|
if (_variable.isPublic())
|
||||||
|
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "non-public state variables");
|
||||||
|
if (_variable.annotation().docTags.count("notice") > 0)
|
||||||
|
m_errorReporter.warning(
|
||||||
|
9098_error, _variable.documentation()->location(),
|
||||||
|
"Documentation tag on non-public state variables will be disallowed in 0.7.0. You will need to use the @dev tag explicitly."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (_variable.annotation().docTags.count("title") > 0 || _variable.annotation().docTags.count("author") > 0)
|
||||||
|
m_errorReporter.warning(
|
||||||
|
4822_error, _variable.documentation()->location(),
|
||||||
|
"Documentation tag @title and @author is only allowed on contract definitions. It will be disallowed in 0.7.0."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool DocStringAnalyser::visit(ModifierDefinition const& _modifier)
|
bool DocStringAnalyser::visit(ModifierDefinition const& _modifier)
|
||||||
{
|
{
|
||||||
handleCallable(_modifier, _modifier, _modifier.annotation());
|
handleCallable(_modifier, _modifier, _modifier.annotation());
|
||||||
@ -144,7 +169,20 @@ void DocStringAnalyser::parseDocStrings(
|
|||||||
if (docTag.first == "return")
|
if (docTag.first == "return")
|
||||||
{
|
{
|
||||||
returnTagsVisited++;
|
returnTagsVisited++;
|
||||||
if (auto* function = dynamic_cast<FunctionDefinition const*>(&_node))
|
if (auto* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
|
||||||
|
{
|
||||||
|
if (!varDecl->isPublic())
|
||||||
|
appendError(
|
||||||
|
_node.documentation()->location(),
|
||||||
|
"Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables."
|
||||||
|
);
|
||||||
|
if (returnTagsVisited > 1)
|
||||||
|
appendError(
|
||||||
|
_node.documentation()->location(),
|
||||||
|
"Documentation tag \"@" + docTag.first + "\" is only allowed once on state-variables."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (auto* function = dynamic_cast<FunctionDefinition const*>(&_node))
|
||||||
{
|
{
|
||||||
string content = docTag.second.content;
|
string content = docTag.second.content;
|
||||||
string firstWord = content.substr(0, content.find_first_of(" \t"));
|
string firstWord = content.substr(0, content.find_first_of(" \t"));
|
||||||
|
@ -46,6 +46,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool visit(ContractDefinition const& _contract) override;
|
bool visit(ContractDefinition const& _contract) override;
|
||||||
bool visit(FunctionDefinition const& _function) override;
|
bool visit(FunctionDefinition const& _function) override;
|
||||||
|
bool visit(VariableDeclaration const& _variable) override;
|
||||||
bool visit(ModifierDefinition const& _modifier) override;
|
bool visit(ModifierDefinition const& _modifier) override;
|
||||||
bool visit(EventDefinition const& _event) override;
|
bool visit(EventDefinition const& _event) override;
|
||||||
|
|
||||||
@ -67,6 +68,12 @@ private:
|
|||||||
StructurallyDocumentedAnnotation& _annotation
|
StructurallyDocumentedAnnotation& _annotation
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void handleDeclaration(
|
||||||
|
Declaration const& _declaration,
|
||||||
|
StructurallyDocumented const& _node,
|
||||||
|
StructurallyDocumentedAnnotation& _annotation
|
||||||
|
);
|
||||||
|
|
||||||
void parseDocStrings(
|
void parseDocStrings(
|
||||||
StructurallyDocumented const& _node,
|
StructurallyDocumented const& _node,
|
||||||
StructurallyDocumentedAnnotation& _annotation,
|
StructurallyDocumentedAnnotation& _annotation,
|
||||||
|
@ -859,7 +859,7 @@ private:
|
|||||||
* Declaration of a variable. This can be used in various places, e.g. in function parameter
|
* Declaration of a variable. This can be used in various places, e.g. in function parameter
|
||||||
* lists, struct definitions and even function bodies.
|
* lists, struct definitions and even function bodies.
|
||||||
*/
|
*/
|
||||||
class VariableDeclaration: public Declaration
|
class VariableDeclaration: public Declaration, public StructurallyDocumented
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Location { Unspecified, Storage, Memory, CallData };
|
enum Location { Unspecified, Storage, Memory, CallData };
|
||||||
@ -882,6 +882,7 @@ public:
|
|||||||
ASTPointer<ASTString> const& _name,
|
ASTPointer<ASTString> const& _name,
|
||||||
ASTPointer<Expression> _value,
|
ASTPointer<Expression> _value,
|
||||||
Visibility _visibility,
|
Visibility _visibility,
|
||||||
|
ASTPointer<StructuredDocumentation> const _documentation = nullptr,
|
||||||
bool _isStateVar = false,
|
bool _isStateVar = false,
|
||||||
bool _isIndexed = false,
|
bool _isIndexed = false,
|
||||||
Mutability _mutability = Mutability::Mutable,
|
Mutability _mutability = Mutability::Mutable,
|
||||||
@ -889,6 +890,7 @@ public:
|
|||||||
Location _referenceLocation = Location::Unspecified
|
Location _referenceLocation = Location::Unspecified
|
||||||
):
|
):
|
||||||
Declaration(_id, _location, _name, _visibility),
|
Declaration(_id, _location, _name, _visibility),
|
||||||
|
StructurallyDocumented(std::move(_documentation)),
|
||||||
m_typeName(std::move(_type)),
|
m_typeName(std::move(_type)),
|
||||||
m_value(std::move(_value)),
|
m_value(std::move(_value)),
|
||||||
m_isStateVariable(_isStateVar),
|
m_isStateVariable(_isStateVar),
|
||||||
|
@ -171,7 +171,7 @@ struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, Structurally
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VariableDeclarationAnnotation: DeclarationAnnotation
|
struct VariableDeclarationAnnotation: DeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||||
{
|
{
|
||||||
/// Type of variable (type of identifier referencing this variable).
|
/// Type of variable (type of identifier referencing this variable).
|
||||||
TypePointer type = nullptr;
|
TypePointer type = nullptr;
|
||||||
|
@ -391,6 +391,8 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
|
|||||||
};
|
};
|
||||||
if (_node.isStateVariable() && _node.isPublic())
|
if (_node.isStateVariable() && _node.isPublic())
|
||||||
attributes.emplace_back("functionSelector", _node.externalIdentifierHex());
|
attributes.emplace_back("functionSelector", _node.externalIdentifierHex());
|
||||||
|
if (_node.isStateVariable() && _node.documentation())
|
||||||
|
attributes.emplace_back("documentation", toJson(*_node.documentation()));
|
||||||
if (m_inEvent)
|
if (m_inEvent)
|
||||||
attributes.emplace_back("indexed", _node.isIndexed());
|
attributes.emplace_back("indexed", _node.isIndexed());
|
||||||
if (!_node.annotation().baseFunctions.empty())
|
if (!_node.annotation().baseFunctions.empty())
|
||||||
|
@ -441,6 +441,7 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
|
|||||||
make_shared<ASTString>(member(_node, "name").asString()),
|
make_shared<ASTString>(member(_node, "name").asString()),
|
||||||
nullOrCast<Expression>(member(_node, "value")),
|
nullOrCast<Expression>(member(_node, "value")),
|
||||||
visibility(_node),
|
visibility(_node),
|
||||||
|
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
|
||||||
memberAsBool(_node, "stateVariable"),
|
memberAsBool(_node, "stateVariable"),
|
||||||
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
|
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
|
||||||
mutability,
|
mutability,
|
||||||
|
@ -40,7 +40,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
|||||||
auto constructorDefinition(_contractDef.constructor());
|
auto constructorDefinition(_contractDef.constructor());
|
||||||
if (constructorDefinition)
|
if (constructorDefinition)
|
||||||
{
|
{
|
||||||
string value = extractDoc(constructorDefinition->annotation().docTags, "notice");
|
string const value = extractDoc(constructorDefinition->annotation().docTags, "notice");
|
||||||
if (!value.empty())
|
if (!value.empty())
|
||||||
// add the constructor, only if we have any documentation to add
|
// add the constructor, only if we have any documentation to add
|
||||||
methods["constructor"] = Json::Value(value);
|
methods["constructor"] = Json::Value(value);
|
||||||
@ -52,6 +52,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
|||||||
|
|
||||||
for (auto const& it: _contractDef.interfaceFunctions())
|
for (auto const& it: _contractDef.interfaceFunctions())
|
||||||
if (it.second->hasDeclaration())
|
if (it.second->hasDeclaration())
|
||||||
|
{
|
||||||
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||||
{
|
{
|
||||||
string value = extractDoc(f->annotation().docTags, "notice");
|
string value = extractDoc(f->annotation().docTags, "notice");
|
||||||
@ -63,6 +64,19 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
|
|||||||
methods[it.second->externalSignature()] = user;
|
methods[it.second->externalSignature()] = user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (auto var = dynamic_cast<VariableDeclaration const*>(&it.second->declaration()))
|
||||||
|
{
|
||||||
|
solAssert(var->isStateVariable() && var->isPublic(), "");
|
||||||
|
string value = extractDoc(var->annotation().docTags, "notice");
|
||||||
|
if (!value.empty())
|
||||||
|
{
|
||||||
|
Json::Value user;
|
||||||
|
// since @notice is the only user tag if missing function should not appear
|
||||||
|
user["notice"] = Json::Value(value);
|
||||||
|
methods[it.second->externalSignature()] = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
doc["methods"] = methods;
|
doc["methods"] = methods;
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
@ -110,7 +124,20 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value stateVariables(Json::objectValue);
|
||||||
|
for (VariableDeclaration const* varDecl: _contractDef.stateVariables())
|
||||||
|
{
|
||||||
|
if (auto devDoc = devDocumentation(varDecl->annotation().docTags); !devDoc.empty())
|
||||||
|
stateVariables[varDecl->name()] = devDoc;
|
||||||
|
|
||||||
|
solAssert(varDecl->annotation().docTags.count("return") <= 1, "");
|
||||||
|
if (varDecl->annotation().docTags.count("return") == 1)
|
||||||
|
stateVariables[varDecl->name()]["return"] = extractDoc(varDecl->annotation().docTags, "return");
|
||||||
|
}
|
||||||
|
|
||||||
doc["methods"] = methods;
|
doc["methods"] = methods;
|
||||||
|
if (!stateVariables.empty())
|
||||||
|
doc["stateVariables"] = stateVariables;
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
@ -686,6 +686,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
|
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
|
||||||
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
|
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
|
||||||
ASTPointer<TypeName> type;
|
ASTPointer<TypeName> type;
|
||||||
|
ASTPointer<StructuredDocumentation> const documentation = parseStructuredDocumentation();
|
||||||
if (_lookAheadArrayType)
|
if (_lookAheadArrayType)
|
||||||
type = _lookAheadArrayType;
|
type = _lookAheadArrayType;
|
||||||
else
|
else
|
||||||
@ -695,6 +696,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
nodeFactory.setEndPositionFromNode(type);
|
nodeFactory.setEndPositionFromNode(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_options.isStateVariable && documentation != nullptr)
|
||||||
|
parserWarning(2837_error, "Only state variables can have a docstring. This will be disallowed in 0.7.0.");
|
||||||
|
|
||||||
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
||||||
fatalParserError(
|
fatalParserError(
|
||||||
2915_error,
|
2915_error,
|
||||||
@ -809,6 +813,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
identifier,
|
identifier,
|
||||||
value,
|
value,
|
||||||
visibility,
|
visibility,
|
||||||
|
documentation,
|
||||||
_options.isStateVariable,
|
_options.isStateVariable,
|
||||||
isIndexed,
|
isIndexed,
|
||||||
mutability,
|
mutability,
|
||||||
|
@ -87,10 +87,10 @@
|
|||||||
{
|
{
|
||||||
"C":
|
"C":
|
||||||
[
|
[
|
||||||
20
|
23
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"id": 21,
|
"id": 24,
|
||||||
"license": null,
|
"license": null,
|
||||||
"nodeType": "SourceUnit",
|
"nodeType": "SourceUnit",
|
||||||
"nodes":
|
"nodes":
|
||||||
@ -102,90 +102,129 @@
|
|||||||
"contractKind": "contract",
|
"contractKind": "contract",
|
||||||
"documentation": null,
|
"documentation": null,
|
||||||
"fullyImplemented": true,
|
"fullyImplemented": true,
|
||||||
"id": 20,
|
"id": 23,
|
||||||
"linearizedBaseContracts":
|
"linearizedBaseContracts":
|
||||||
[
|
[
|
||||||
20
|
23
|
||||||
],
|
],
|
||||||
"name": "C",
|
"name": "C",
|
||||||
"nodeType": "ContractDefinition",
|
"nodeType": "ContractDefinition",
|
||||||
"nodes":
|
"nodes":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"anonymous": false,
|
"constant": false,
|
||||||
"documentation":
|
"documentation":
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
"nodeType": "StructuredDocumentation",
|
"nodeType": "StructuredDocumentation",
|
||||||
"src": "15:26:3",
|
"src": "15:32:3",
|
||||||
"text": "Some comment on Evt."
|
"text": "Some comment on state var."
|
||||||
},
|
},
|
||||||
|
"functionSelector": "c19d93fb",
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"name": "Evt",
|
"mutability": "mutable",
|
||||||
"nodeType": "EventDefinition",
|
"name": "state",
|
||||||
"parameters":
|
"nodeType": "VariableDeclaration",
|
||||||
|
"overrides": null,
|
||||||
|
"scope": 23,
|
||||||
|
"src": "48:17:3",
|
||||||
|
"stateVariable": true,
|
||||||
|
"storageLocation": "default",
|
||||||
|
"typeDescriptions":
|
||||||
|
{
|
||||||
|
"typeIdentifier": "t_uint256",
|
||||||
|
"typeString": "uint256"
|
||||||
|
},
|
||||||
|
"typeName":
|
||||||
{
|
{
|
||||||
"id": 8,
|
"id": 8,
|
||||||
"nodeType": "ParameterList",
|
"name": "uint",
|
||||||
"parameters": [],
|
"nodeType": "ElementaryTypeName",
|
||||||
"src": "51:2:3"
|
"src": "48:4:3",
|
||||||
|
"typeDescriptions":
|
||||||
|
{
|
||||||
|
"typeIdentifier": "t_uint256",
|
||||||
|
"typeString": "uint256"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"src": "42:12:3"
|
"value": null,
|
||||||
|
"visibility": "public"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"body":
|
"anonymous": false,
|
||||||
{
|
|
||||||
"id": 13,
|
|
||||||
"nodeType": "Block",
|
|
||||||
"src": "99:6:3",
|
|
||||||
"statements":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 12,
|
|
||||||
"nodeType": "PlaceholderStatement",
|
|
||||||
"src": "101:1:3"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"documentation":
|
"documentation":
|
||||||
{
|
{
|
||||||
"id": 10,
|
"id": 10,
|
||||||
"nodeType": "StructuredDocumentation",
|
"nodeType": "StructuredDocumentation",
|
||||||
"src": "57:26:3",
|
"src": "69:26:3",
|
||||||
"text": "Some comment on mod."
|
"text": "Some comment on Evt."
|
||||||
},
|
},
|
||||||
"id": 14,
|
"id": 12,
|
||||||
"name": "mod",
|
"name": "Evt",
|
||||||
"nodeType": "ModifierDefinition",
|
"nodeType": "EventDefinition",
|
||||||
"overrides": null,
|
|
||||||
"parameters":
|
"parameters":
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": 11,
|
||||||
"nodeType": "ParameterList",
|
"nodeType": "ParameterList",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"src": "96:2:3"
|
"src": "105:2:3"
|
||||||
},
|
},
|
||||||
"src": "84:21:3",
|
"src": "96:12:3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"body":
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"nodeType": "Block",
|
||||||
|
"src": "153:6:3",
|
||||||
|
"statements":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"nodeType": "PlaceholderStatement",
|
||||||
|
"src": "155:1:3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"documentation":
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"nodeType": "StructuredDocumentation",
|
||||||
|
"src": "111:26:3",
|
||||||
|
"text": "Some comment on mod."
|
||||||
|
},
|
||||||
|
"id": 17,
|
||||||
|
"name": "mod",
|
||||||
|
"nodeType": "ModifierDefinition",
|
||||||
|
"overrides": null,
|
||||||
|
"parameters":
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"nodeType": "ParameterList",
|
||||||
|
"parameters": [],
|
||||||
|
"src": "150:2:3"
|
||||||
|
},
|
||||||
|
"src": "138:21:3",
|
||||||
"virtual": false,
|
"virtual": false,
|
||||||
"visibility": "internal"
|
"visibility": "internal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"body":
|
"body":
|
||||||
{
|
{
|
||||||
"id": 18,
|
"id": 21,
|
||||||
"nodeType": "Block",
|
"nodeType": "Block",
|
||||||
"src": "155:2:3",
|
"src": "209:2:3",
|
||||||
"statements": []
|
"statements": []
|
||||||
},
|
},
|
||||||
"documentation":
|
"documentation":
|
||||||
{
|
{
|
||||||
"id": 15,
|
"id": 18,
|
||||||
"nodeType": "StructuredDocumentation",
|
"nodeType": "StructuredDocumentation",
|
||||||
"src": "108:25:3",
|
"src": "162:25:3",
|
||||||
"text": "Some comment on fn."
|
"text": "Some comment on fn."
|
||||||
},
|
},
|
||||||
"functionSelector": "a4a2c40b",
|
"functionSelector": "a4a2c40b",
|
||||||
"id": 19,
|
"id": 22,
|
||||||
"implemented": true,
|
"implemented": true,
|
||||||
"kind": "function",
|
"kind": "function",
|
||||||
"modifiers": [],
|
"modifiers": [],
|
||||||
@ -194,29 +233,29 @@
|
|||||||
"overrides": null,
|
"overrides": null,
|
||||||
"parameters":
|
"parameters":
|
||||||
{
|
{
|
||||||
"id": 16,
|
"id": 19,
|
||||||
"nodeType": "ParameterList",
|
"nodeType": "ParameterList",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"src": "145:2:3"
|
"src": "199:2:3"
|
||||||
},
|
},
|
||||||
"returnParameters":
|
"returnParameters":
|
||||||
{
|
{
|
||||||
"id": 17,
|
"id": 20,
|
||||||
"nodeType": "ParameterList",
|
"nodeType": "ParameterList",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"src": "155:0:3"
|
"src": "209:0:3"
|
||||||
},
|
},
|
||||||
"scope": 20,
|
"scope": 23,
|
||||||
"src": "134:23:3",
|
"src": "188:23:3",
|
||||||
"stateMutability": "nonpayable",
|
"stateMutability": "nonpayable",
|
||||||
"virtual": false,
|
"virtual": false,
|
||||||
"visibility": "public"
|
"visibility": "public"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scope": 21,
|
"scope": 24,
|
||||||
"src": "0:159:3"
|
"src": "0:213:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"src": "0:160:3"
|
"src": "0:214:3"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -11,6 +11,7 @@ contract C {}
|
|||||||
// ---- SOURCE: c
|
// ---- SOURCE: c
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
|
/** Some comment on state var.*/ uint public state;
|
||||||
/** Some comment on Evt.*/ event Evt();
|
/** Some comment on Evt.*/ event Evt();
|
||||||
/** Some comment on mod.*/ modifier mod() { _; }
|
/** Some comment on mod.*/ modifier mod() { _; }
|
||||||
/** Some comment on fn.*/ function fn() public {}
|
/** Some comment on fn.*/ function fn() public {}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
{
|
{
|
||||||
"C":
|
"C":
|
||||||
[
|
[
|
||||||
20
|
23
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"license": null
|
"license": null
|
||||||
@ -30,13 +30,54 @@
|
|||||||
"fullyImplemented": true,
|
"fullyImplemented": true,
|
||||||
"linearizedBaseContracts":
|
"linearizedBaseContracts":
|
||||||
[
|
[
|
||||||
20
|
23
|
||||||
],
|
],
|
||||||
"name": "C",
|
"name": "C",
|
||||||
"scope": 21
|
"scope": 24
|
||||||
},
|
},
|
||||||
"children":
|
"children":
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"attributes":
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"functionSelector": "c19d93fb",
|
||||||
|
"mutability": "mutable",
|
||||||
|
"name": "state",
|
||||||
|
"overrides": null,
|
||||||
|
"scope": 23,
|
||||||
|
"stateVariable": true,
|
||||||
|
"storageLocation": "default",
|
||||||
|
"type": "uint256",
|
||||||
|
"value": null,
|
||||||
|
"visibility": "public"
|
||||||
|
},
|
||||||
|
"children":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"attributes":
|
||||||
|
{
|
||||||
|
"name": "uint",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
"id": 8,
|
||||||
|
"name": "ElementaryTypeName",
|
||||||
|
"src": "48:4:3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attributes":
|
||||||
|
{
|
||||||
|
"text": "Some comment on state var."
|
||||||
|
},
|
||||||
|
"id": 7,
|
||||||
|
"name": "StructuredDocumentation",
|
||||||
|
"src": "15:32:3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": 9,
|
||||||
|
"name": "VariableDeclaration",
|
||||||
|
"src": "48:17:3"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
{
|
{
|
||||||
@ -50,9 +91,9 @@
|
|||||||
{
|
{
|
||||||
"text": "Some comment on Evt."
|
"text": "Some comment on Evt."
|
||||||
},
|
},
|
||||||
"id": 7,
|
"id": 10,
|
||||||
"name": "StructuredDocumentation",
|
"name": "StructuredDocumentation",
|
||||||
"src": "15:26:3"
|
"src": "69:26:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -63,14 +104,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
"id": 8,
|
"id": 11,
|
||||||
"name": "ParameterList",
|
"name": "ParameterList",
|
||||||
"src": "51:2:3"
|
"src": "105:2:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 9,
|
"id": 12,
|
||||||
"name": "EventDefinition",
|
"name": "EventDefinition",
|
||||||
"src": "42:12:3"
|
"src": "96:12:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -87,9 +128,9 @@
|
|||||||
{
|
{
|
||||||
"text": "Some comment on mod."
|
"text": "Some comment on mod."
|
||||||
},
|
},
|
||||||
"id": 10,
|
"id": 13,
|
||||||
"name": "StructuredDocumentation",
|
"name": "StructuredDocumentation",
|
||||||
"src": "57:26:3"
|
"src": "111:26:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -100,27 +141,27 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
"id": 11,
|
"id": 14,
|
||||||
"name": "ParameterList",
|
"name": "ParameterList",
|
||||||
"src": "96:2:3"
|
"src": "150:2:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"children":
|
"children":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": 12,
|
"id": 15,
|
||||||
"name": "PlaceholderStatement",
|
"name": "PlaceholderStatement",
|
||||||
"src": "101:1:3"
|
"src": "155:1:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 13,
|
"id": 16,
|
||||||
"name": "Block",
|
"name": "Block",
|
||||||
"src": "99:6:3"
|
"src": "153:6:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 14,
|
"id": 17,
|
||||||
"name": "ModifierDefinition",
|
"name": "ModifierDefinition",
|
||||||
"src": "84:21:3"
|
"src": "138:21:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -135,7 +176,7 @@
|
|||||||
],
|
],
|
||||||
"name": "fn",
|
"name": "fn",
|
||||||
"overrides": null,
|
"overrides": null,
|
||||||
"scope": 20,
|
"scope": 23,
|
||||||
"stateMutability": "nonpayable",
|
"stateMutability": "nonpayable",
|
||||||
"virtual": false,
|
"virtual": false,
|
||||||
"visibility": "public"
|
"visibility": "public"
|
||||||
@ -147,9 +188,9 @@
|
|||||||
{
|
{
|
||||||
"text": "Some comment on fn."
|
"text": "Some comment on fn."
|
||||||
},
|
},
|
||||||
"id": 15,
|
"id": 18,
|
||||||
"name": "StructuredDocumentation",
|
"name": "StructuredDocumentation",
|
||||||
"src": "108:25:3"
|
"src": "162:25:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -160,9 +201,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
"id": 16,
|
"id": 19,
|
||||||
"name": "ParameterList",
|
"name": "ParameterList",
|
||||||
"src": "145:2:3"
|
"src": "199:2:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -173,9 +214,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
"id": 17,
|
"id": 20,
|
||||||
"name": "ParameterList",
|
"name": "ParameterList",
|
||||||
"src": "155:0:3"
|
"src": "209:0:3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"attributes":
|
"attributes":
|
||||||
@ -186,22 +227,22 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"children": [],
|
"children": [],
|
||||||
"id": 18,
|
"id": 21,
|
||||||
"name": "Block",
|
"name": "Block",
|
||||||
"src": "155:2:3"
|
"src": "209:2:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 19,
|
"id": 22,
|
||||||
"name": "FunctionDefinition",
|
"name": "FunctionDefinition",
|
||||||
"src": "134:23:3"
|
"src": "188:23:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 20,
|
"id": 23,
|
||||||
"name": "ContractDefinition",
|
"name": "ContractDefinition",
|
||||||
"src": "0:159:3"
|
"src": "0:213:3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": 21,
|
"id": 24,
|
||||||
"name": "SourceUnit",
|
"name": "SourceUnit",
|
||||||
"src": "0:160:3"
|
"src": "0:214:3"
|
||||||
}
|
}
|
||||||
|
@ -204,6 +204,77 @@ BOOST_AUTO_TEST_CASE(dev_and_user_no_doc)
|
|||||||
checkNatspec(sourceCode, "test", userNatspec, true);
|
checkNatspec(sourceCode, "test", userNatspec, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(public_state_variable)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract test {
|
||||||
|
/// @notice example of notice
|
||||||
|
/// @dev example of dev
|
||||||
|
/// @return returns state
|
||||||
|
uint public state;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
char const* devDoc = R"R(
|
||||||
|
{
|
||||||
|
"methods" : {},
|
||||||
|
"stateVariables" :
|
||||||
|
{
|
||||||
|
"state" :
|
||||||
|
{
|
||||||
|
"details" : "example of dev",
|
||||||
|
"return" : "returns state"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)R";
|
||||||
|
checkNatspec(sourceCode, "test", devDoc, false);
|
||||||
|
|
||||||
|
char const* userDoc = R"R(
|
||||||
|
{
|
||||||
|
"methods" :
|
||||||
|
{
|
||||||
|
"state()" :
|
||||||
|
{
|
||||||
|
"notice": "example of notice"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)R";
|
||||||
|
checkNatspec(sourceCode, "test", userDoc, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(private_state_variable)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract test {
|
||||||
|
/// @dev example of dev
|
||||||
|
uint private state;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
char const* devDoc = R"(
|
||||||
|
{
|
||||||
|
"methods" : {},
|
||||||
|
"stateVariables" :
|
||||||
|
{
|
||||||
|
"state" :
|
||||||
|
{
|
||||||
|
"details" : "example of dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
checkNatspec(sourceCode, "test", devDoc, false);
|
||||||
|
|
||||||
|
char const* userDoc = R"(
|
||||||
|
{
|
||||||
|
"methods":{}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
checkNatspec(sourceCode, "test", userDoc, true);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
|
BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
/// @title title
|
||||||
|
/// @author author
|
||||||
|
uint private state;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (17-56): Documentation tag @title and @author is only allowed on contract definitions. It will be disallowed in 0.7.0.
|
@ -0,0 +1,6 @@
|
|||||||
|
contract test {
|
||||||
|
/// @return returns something
|
||||||
|
uint private state;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DocstringParsingError: (18-47): Documentation tag "@return" is only allowed on public state-variables.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
/// @notice example of notice
|
||||||
|
/// @dev example of dev
|
||||||
|
uint private state;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (17-74): Documentation tag on non-public state variables will be disallowed in 0.7.0. You will need to use the @dev tag explicitly.
|
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
/// @notice example of notice
|
||||||
|
/// @dev example of dev
|
||||||
|
uint public state;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
contract test {
|
||||||
|
/// @notice example of notice
|
||||||
|
/// @dev example of dev
|
||||||
|
/// @return returns something
|
||||||
|
/// @return returns something
|
||||||
|
uint public state;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// DocstringParsingError: (18-137): Documentation tag "@return" is only allowed once on state-variables.
|
14
test/libsolidity/syntaxTests/natspec/docstring_variable.sol
Normal file
14
test/libsolidity/syntaxTests/natspec/docstring_variable.sol
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint) {
|
||||||
|
/// @title example of title
|
||||||
|
/// @author example of author
|
||||||
|
/// @notice example of notice
|
||||||
|
/// @dev example of dev
|
||||||
|
/// @param example of param
|
||||||
|
/// @return example of return
|
||||||
|
uint state = 42;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (290-295): Only state variables can have a docstring. This will be disallowed in 0.7.0.
|
Loading…
Reference in New Issue
Block a user