NatSpec: Implement `@inheritdoc`

This commit is contained in:
Mathias Baumann
2020-07-20 18:32:18 +02:00
committed by Mathias Baumann
parent 6a1b1283fd
commit ba0a4de50d
18 changed files with 1082 additions and 219 deletions
+2
View File
@@ -16,6 +16,8 @@ set(sources
analysis/DeclarationTypeChecker.h
analysis/DocStringAnalyser.cpp
analysis/DocStringAnalyser.h
analysis/DocStringTagParser.cpp
analysis/DocStringTagParser.h
analysis/ImmutableValidator.cpp
analysis/ImmutableValidator.h
analysis/GlobalContext.cpp
+38 -146
View File
@@ -53,6 +53,17 @@ void copyMissingTags(StructurallyDocumentedAnnotation& _target, set<CallableDecl
_target.docTags.emplace(tag, content);
}
CallableDeclaration const* findBaseCallable(set<CallableDeclaration const*> const& _baseFunctions, int64_t _contractId)
{
for (CallableDeclaration const* baseFuncCandidate: _baseFunctions)
if (baseFuncCandidate->annotation().contract->id() == _contractId)
return baseFuncCandidate;
else if (auto callable = findBaseCallable(baseFuncCandidate->annotation().baseFunctions, _contractId))
return callable;
return nullptr;
}
bool parameterNamesEqual(CallableDeclaration const& _a, CallableDeclaration const& _b)
{
return boost::range::equal(_a.parameters(), _b.parameters(), [](auto const& pa, auto const& pb) { return pa->name() == pb->name(); });
@@ -67,50 +78,23 @@ bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
return errorWatcher.ok();
}
bool DocStringAnalyser::visit(ContractDefinition const& _contract)
{
static set<string> const validTags = set<string>{"author", "title", "dev", "notice"};
parseDocStrings(_contract, _contract.annotation(), validTags, "contracts");
return true;
}
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
{
if (_function.isConstructor())
handleConstructor(_function, _function, _function.annotation());
else
if (!_function.isConstructor())
handleCallable(_function, _function, _function.annotation());
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(
7816_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(
8532_error, _variable.documentation()->location(),
"Documentation tag @title and @author is only allowed on contract definitions. "
"It will be disallowed in 0.7.0."
);
if (!_variable.isStateVariable())
return false;
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
copyMissingTags(_variable.annotation(), {baseFunction});
else if (_variable.annotation().docTags.empty())
copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions);
if (_variable.annotation().docTags.empty())
copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions);
}
return false;
}
@@ -128,133 +112,41 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
return true;
}
void DocStringAnalyser::checkParameters(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
)
{
set<string> validParams;
for (auto const& p: _callable.parameters())
validParams.insert(p->name());
if (_callable.returnParameterList())
for (auto const& p: _callable.returnParameterList()->parameters())
validParams.insert(p->name());
auto paramRange = _annotation.docTags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i)
if (!validParams.count(i->second.paramName))
m_errorReporter.docstringParsingError(
3881_error,
_node.documentation()->location(),
"Documented parameter \"" +
i->second.paramName +
"\" not found in the parameter list of the function."
);
}
void DocStringAnalyser::handleConstructor(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
)
{
static set<string> const validTags = set<string>{"author", "dev", "notice", "param"};
parseDocStrings(_node, _annotation, validTags, "constructor");
checkParameters(_callable, _node, _annotation);
}
void DocStringAnalyser::handleCallable(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
)
{
static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"};
parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _node, _annotation);
if (
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation))
copyMissingTags(_annotation, {baseFunction});
else if (
_annotation.docTags.empty() &&
_callable.annotation().baseFunctions.size() == 1 &&
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
)
copyMissingTags(_annotation, _callable.annotation().baseFunctions);
if (_node.documentation() && _annotation.docTags.count("author") > 0)
m_errorReporter.warning(
9843_error, _node.documentation()->location(),
"Documentation tag @author is only allowed on contract definitions. "
"It will be disallowed in 0.7.0."
);
}
void DocStringAnalyser::parseDocStrings(
CallableDeclaration const* DocStringAnalyser::resolveInheritDoc(
set<CallableDeclaration const*> const& _baseFuncs,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation,
set<string> const& _validTags,
string const& _nodeName
StructurallyDocumentedAnnotation& _annotation
)
{
DocStringParser parser;
if (_node.documentation() && !_node.documentation()->text()->empty())
{
parser.parse(*_node.documentation()->text(), m_errorReporter);
_annotation.docTags = parser.tags();
}
if (_annotation.inheritdocReference == nullptr)
return nullptr;
size_t returnTagsVisited = 0;
for (auto const& docTag: _annotation.docTags)
{
if (!_validTags.count(docTag.first))
m_errorReporter.docstringParsingError(
6546_error,
_node.documentation()->location(),
"Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."
);
else
if (docTag.first == "return")
{
returnTagsVisited++;
if (auto* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
{
if (!varDecl->isPublic())
m_errorReporter.docstringParsingError(
9440_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables."
);
if (returnTagsVisited > 1)
m_errorReporter.docstringParsingError(
5256_error,
_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 firstWord = content.substr(0, content.find_first_of(" \t"));
if (auto const callable = findBaseCallable(_baseFuncs, _annotation.inheritdocReference->id()))
return callable;
if (returnTagsVisited > function->returnParameters().size())
m_errorReporter.docstringParsingError(
2604_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" exceeds the number of return parameters."
);
else
{
auto parameter = function->returnParameters().at(returnTagsVisited - 1);
if (!parameter->name().empty() && parameter->name() != firstWord)
m_errorReporter.docstringParsingError(
5856_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" does not contain the name of its return parameter."
);
}
}
}
}
m_errorReporter.docstringParsingError(
4682_error,
_node.documentation()->location(),
"Documentation tag @inheritdoc references contract \"" +
_annotation.inheritdocReference->name() +
"\", but the contract does not contain a function that is overridden by this function."
);
return nullptr;
}
+3 -29
View File
@@ -15,12 +15,6 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Parses and analyses the doc strings.
* Stores the parsing results in the AST annotations and reports errors.
*/
#pragma once
@@ -35,7 +29,7 @@ namespace solidity::frontend
{
/**
* Parses and analyses the doc strings.
* Analyses and validates the doc strings.
* Stores the parsing results in the AST annotations and reports errors.
*/
class DocStringAnalyser: private ASTConstVisitor
@@ -45,20 +39,13 @@ public:
bool analyseDocStrings(SourceUnit const& _sourceUnit);
private:
bool visit(ContractDefinition const& _contract) override;
bool visit(FunctionDefinition const& _function) override;
bool visit(VariableDeclaration const& _variable) override;
bool visit(ModifierDefinition const& _modifier) override;
bool visit(EventDefinition const& _event) override;
void checkParameters(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
);
void handleConstructor(
CallableDeclaration const& _callable,
CallableDeclaration const* resolveInheritDoc(
std::set<CallableDeclaration const*> const& _baseFunctions,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
);
@@ -69,19 +56,6 @@ private:
StructurallyDocumentedAnnotation& _annotation
);
void handleDeclaration(
Declaration const& _declaration,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
);
void parseDocStrings(
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation,
std::set<std::string> const& _validTags,
std::string const& _nodeName
);
langutil::ErrorReporter& m_errorReporter;
};
+227
View File
@@ -0,0 +1,227 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Parses and analyses the doc strings.
* Stores the parsing results in the AST annotations and reports errors.
*/
#include <libsolidity/analysis/DocStringTagParser.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/parsing/DocStringParser.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <liblangutil/ErrorReporter.h>
using namespace std;
using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::frontend;
bool DocStringTagParser::parseDocStrings(SourceUnit const& _sourceUnit)
{
auto errorWatcher = m_errorReporter.errorWatcher();
_sourceUnit.accept(*this);
return errorWatcher.ok();
}
bool DocStringTagParser::visit(ContractDefinition const& _contract)
{
static set<string> const validTags = set<string>{"author", "title", "dev", "notice"};
parseDocStrings(_contract, _contract.annotation(), validTags, "contracts");
return true;
}
bool DocStringTagParser::visit(FunctionDefinition const& _function)
{
if (_function.isConstructor())
handleConstructor(_function, _function, _function.annotation());
else
handleCallable(_function, _function, _function.annotation());
return true;
}
bool DocStringTagParser::visit(VariableDeclaration const& _variable)
{
if (_variable.isStateVariable())
{
static set<string> const validPublicTags = set<string>{"dev", "notice", "return", "title", "author", "inheritdoc"};
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(
7816_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(
8532_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 DocStringTagParser::visit(ModifierDefinition const& _modifier)
{
handleCallable(_modifier, _modifier, _modifier.annotation());
return true;
}
bool DocStringTagParser::visit(EventDefinition const& _event)
{
handleCallable(_event, _event, _event.annotation());
return true;
}
void DocStringTagParser::checkParameters(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
)
{
set<string> validParams;
for (auto const& p: _callable.parameters())
validParams.insert(p->name());
if (_callable.returnParameterList())
for (auto const& p: _callable.returnParameterList()->parameters())
validParams.insert(p->name());
auto paramRange = _annotation.docTags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i)
if (!validParams.count(i->second.paramName))
m_errorReporter.docstringParsingError(
3881_error,
_node.documentation()->location(),
"Documented parameter \"" +
i->second.paramName +
"\" not found in the parameter list of the function."
);
}
void DocStringTagParser::handleConstructor(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
)
{
static set<string> const validTags = set<string>{"author", "dev", "notice", "param"};
parseDocStrings(_node, _annotation, validTags, "constructor");
checkParameters(_callable, _node, _annotation);
}
void DocStringTagParser::handleCallable(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
)
{
static set<string> const validEventTags = set<string>{"author", "dev", "notice", "return", "param"};
static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param", "inheritdoc"};
if (dynamic_cast<EventDefinition const*>(&_callable))
parseDocStrings(_node, _annotation, validEventTags, "events");
else
parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _node, _annotation);
if (_node.documentation() && _annotation.docTags.count("author") > 0)
m_errorReporter.warning(
9843_error, _node.documentation()->location(),
"Documentation tag @author is only allowed on contract definitions. "
"It will be disallowed in 0.7.0."
);
}
void DocStringTagParser::parseDocStrings(
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation,
set<string> const& _validTags,
string const& _nodeName
)
{
DocStringParser parser;
if (_node.documentation() && !_node.documentation()->text()->empty())
{
parser.parse(*_node.documentation()->text(), m_errorReporter);
_annotation.docTags = parser.tags();
}
size_t returnTagsVisited = 0;
for (auto const& docTag: _annotation.docTags)
{
if (!_validTags.count(docTag.first))
m_errorReporter.docstringParsingError(
6546_error,
_node.documentation()->location(),
"Documentation tag @" + docTag.first + " not valid for " + _nodeName + "."
);
else if (docTag.first == "return")
{
returnTagsVisited++;
if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
{
if (!varDecl->isPublic())
m_errorReporter.docstringParsingError(
9440_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables."
);
if (returnTagsVisited > 1)
m_errorReporter.docstringParsingError(
5256_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + "\" is only allowed once on state-variables."
);
}
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(&_node))
{
string content = docTag.second.content;
string firstWord = content.substr(0, content.find_first_of(" \t"));
if (returnTagsVisited > function->returnParameters().size())
m_errorReporter.docstringParsingError(
2604_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" exceeds the number of return parameters."
);
else
{
auto parameter = function->returnParameters().at(returnTagsVisited - 1);
if (!parameter->name().empty() && parameter->name() != firstWord)
m_errorReporter.docstringParsingError(
5856_error,
_node.documentation()->location(),
"Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" +
" does not contain the name of its return parameter."
);
}
}
}
}
}
+76
View File
@@ -0,0 +1,76 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libsolidity/ast/ASTVisitor.h>
namespace solidity::langutil
{
class ErrorReporter;
}
namespace solidity::frontend
{
/**
* Parses the doc tags and does basic validity checks.
* Stores the parsing results in the AST annotations and reports errors.
*/
class DocStringTagParser: private ASTConstVisitor
{
public:
explicit DocStringTagParser(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
bool parseDocStrings(SourceUnit const& _sourceUnit);
private:
bool visit(ContractDefinition const& _contract) override;
bool visit(FunctionDefinition const& _function) override;
bool visit(VariableDeclaration const& _variable) override;
bool visit(ModifierDefinition const& _modifier) override;
bool visit(EventDefinition const& _event) override;
void checkParameters(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
);
void handleConstructor(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
);
void handleCallable(
CallableDeclaration const& _callable,
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation
);
void parseDocStrings(
StructurallyDocumented const& _node,
StructurallyDocumentedAnnotation& _annotation,
std::set<std::string> const& _validTags,
std::string const& _nodeName
);
langutil::ErrorReporter& m_errorReporter;
};
}
+67 -1
View File
@@ -36,6 +36,7 @@
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>
using namespace std;
using namespace solidity::langutil;
@@ -106,6 +107,14 @@ void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclSt
m_resolver.activateVariable(var->name());
}
bool ReferencesResolver::visit(VariableDeclaration const& _varDecl)
{
if (_varDecl.documentation())
resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation());
return true;
}
bool ReferencesResolver::visit(Identifier const& _identifier)
{
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
@@ -132,6 +141,10 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
{
m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
if (_functionDefinition.documentation())
resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation());
return true;
}
@@ -141,9 +154,13 @@ void ReferencesResolver::endVisit(FunctionDefinition const&)
m_returnParameters.pop_back();
}
bool ReferencesResolver::visit(ModifierDefinition const&)
bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition)
{
m_returnParameters.push_back(nullptr);
if (_modifierDefinition.documentation())
resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation());
return true;
}
@@ -284,4 +301,53 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
visit(*_varDecl.value);
}
void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation)
{
switch (_annotation.docTags.count("inheritdoc"))
{
case 0:
break;
case 1:
{
string const& name = _annotation.docTags.find("inheritdoc")->second.content;
vector<string> path;
boost::split(path, name, boost::is_any_of("."));
Declaration const* result = m_resolver.pathFromCurrentScope(path);
if (result == nullptr)
{
m_errorReporter.docstringParsingError(
9397_error,
_documentation.location(),
"Documentation tag @inheritdoc references inexistent contract \"" +
name +
"\"."
);
return;
}
else
{
_annotation.inheritdocReference = dynamic_cast<ContractDefinition const*>(result);
if (!_annotation.inheritdocReference)
m_errorReporter.docstringParsingError(
1430_error,
_documentation.location(),
"Documentation tag @inheritdoc reference \"" +
name +
"\" is not a contract."
);
}
break;
}
default:
m_errorReporter.docstringParsingError(
5142_error,
_documentation.location(),
"Documentation tag @inheritdoc can only be given once."
);
break;
}
}
}
@@ -76,6 +76,7 @@ private:
bool visit(ForStatement const& _for) override;
void endVisit(ForStatement const& _for) override;
void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
bool visit(VariableDeclaration const& _varDecl) override;
bool visit(Identifier const& _identifier) override;
bool visit(FunctionDefinition const& _functionDefinition) override;
void endVisit(FunctionDefinition const& _functionDefinition) override;
@@ -89,6 +90,8 @@ private:
void operator()(yul::Identifier const& _identifier) override;
void operator()(yul::VariableDeclaration const& _varDecl) override;
void resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation);
langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion;
+2
View File
@@ -79,6 +79,8 @@ struct StructurallyDocumentedAnnotation
/// Mapping docstring tag name -> content.
std::multimap<std::string, DocTag> docTags;
/// contract that @inheritdoc references if it exists
ContractDefinition const* inheritdocReference = nullptr;
};
struct SourceUnitAnnotation: ASTAnnotation
+7
View File
@@ -30,6 +30,7 @@
#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/DocStringTagParser.h>
#include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/PostTypeChecker.h>
@@ -308,6 +309,11 @@ bool CompilerStack::analyze()
if (source->ast && !syntaxChecker.checkSyntax(*source->ast))
noErrors = false;
DocStringTagParser DocStringTagParser(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (source->ast && !DocStringTagParser.parseDocStrings(*source->ast))
noErrors = false;
m_globalContext = make_shared<GlobalContext>();
// We need to keep the same resolver during the whole process.
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter);
@@ -363,6 +369,7 @@ bool CompilerStack::analyze()
if (!contractLevelChecker.check(*contract))
noErrors = false;
// Requires ContractLevelChecker
DocStringAnalyser docStringAnalyser(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast))
+12 -18
View File
@@ -58,28 +58,22 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
for (auto const& it: _contractDef.interfaceFunctions())
if (it.second->hasDeclaration())
{
string value;
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
{
string value = extractDoc(f->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;
}
}
value = extractDoc(f->annotation().docTags, "notice");
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;
}
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;
}
}