Browse Source

Merge pull request #2193 from LianaHus/sol_Natspec_warnings_instead_of_exceptions

Solidity: contract documentation is now parsing during compilation and not by request
cl-refactor
chriseth 10 years ago
parent
commit
ee78b0f7a4
  1. 21
      libsolidity/AST.cpp
  2. 10
      libsolidity/AST.h
  3. 10
      libsolidity/CompilerStack.cpp
  4. 26
      libsolidity/InterfaceHandler.cpp
  5. 25
      libsolidity/InterfaceHandler.h
  6. 1
      libsolidity/Parser.cpp
  7. 2
      mix/CodeModel.cpp
  8. 5
      solc/CommandLineInterface.cpp
  9. 4
      solc/jsonCompiler.cpp
  10. 27
      test/libsolidity/SolidityEndToEndTest.cpp
  11. 93
      test/libsolidity/SolidityNatspecJSON.cpp
  12. 9
      test/libsolidity/solidityExecutionFramework.h

21
libsolidity/AST.cpp

@ -382,6 +382,27 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
return *m_interfaceFunctionList;
}
string const& ContractDefinition::devDocumentation() const
{
return m_devDocumentation;
}
string const& ContractDefinition::userDocumentation() const
{
return m_userDocumentation;
}
void ContractDefinition::setDevDocumentation(string const& _devDocumentation)
{
m_devDocumentation = _devDocumentation;
}
void ContractDefinition::setUserDocumentation(string const& _userDocumentation)
{
m_userDocumentation = _userDocumentation;
}
vector<Declaration const*> const& ContractDefinition::getInheritableMembers() const
{
if (!m_inheritableMembers)

10
libsolidity/AST.h

@ -281,6 +281,12 @@ public:
/// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* getFallbackFunction() const;
std::string const& userDocumentation() const;
void setUserDocumentation(std::string const& _userDocumentation);
std::string const& devDocumentation() const;
void setDevDocumentation(std::string const& _devDocumentation);
private:
/// Checks that two functions defined in this contract with the same name have different
/// arguments and that there is at most one constructor.
@ -302,6 +308,10 @@ private:
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
// parsed Natspec documentation of the contract.
std::string m_userDocumentation;
std::string m_devDocumentation;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents;

10
libsolidity/CompilerStack.cpp

@ -116,6 +116,7 @@ void CompilerStack::parse()
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
InterfaceHandler interfaceHandler;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@ -123,6 +124,8 @@ void CompilerStack::parse()
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.checkTypeRequirements(*contract);
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
m_contracts[contract->getName()].contract = contract;
}
m_parseSuccessful = true;
@ -231,6 +234,8 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat
Contract const& contract = getContract(_contractName);
std::unique_ptr<string const>* doc;
// checks wheather we already have the documentation
switch (_type)
{
case DocumentationType::NatspecUser:
@ -248,8 +253,11 @@ string const& CompilerStack::getMetadata(string const& _contractName, Documentat
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}
// caches the result
if (!*doc)
*doc = contract.interfaceHandler->getDocumentation(*contract.contract, _type);
doc->reset(new string(contract.interfaceHandler->getDocumentation(*contract.contract, _type)));
return *(*doc);
}

26
libsolidity/InterfaceHandler.cpp

@ -16,7 +16,7 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::None;
}
unique_ptr<string> InterfaceHandler::getDocumentation(
string InterfaceHandler::getDocumentation(
ContractDefinition const& _contractDef,
DocumentationType _type
)
@ -24,9 +24,9 @@ unique_ptr<string> InterfaceHandler::getDocumentation(
switch(_type)
{
case DocumentationType::NatspecUser:
return getUserDocumentation(_contractDef);
return userDocumentation(_contractDef);
case DocumentationType::NatspecDev:
return getDevDocumentation(_contractDef);
return devDocumentation(_contractDef);
case DocumentationType::ABIInterface:
return getABIInterface(_contractDef);
case DocumentationType::ABISolidityInterface:
@ -34,10 +34,10 @@ unique_ptr<string> InterfaceHandler::getDocumentation(
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
return nullptr;
return "";
}
unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
string InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{
Json::Value abi(Json::arrayValue);
@ -103,10 +103,10 @@ unique_ptr<string> InterfaceHandler::getABIInterface(ContractDefinition const& _
event["inputs"] = params;
abi.append(event);
}
return unique_ptr<string>(new string(Json::FastWriter().write(abi)));
return Json::FastWriter().write(abi);
}
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
string InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
{
string ret = "contract " + _contractDef.getName() + "{";
@ -140,10 +140,10 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
ret += ";";
}
return unique_ptr<string>(new string(ret + "}"));
return ret + "}";
}
unique_ptr<string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
{
Json::Value doc;
Json::Value methods(Json::objectValue);
@ -165,10 +165,10 @@ unique_ptr<string> InterfaceHandler::getUserDocumentation(ContractDefinition con
}
doc["methods"] = methods;
return unique_ptr<string>(new string(Json::FastWriter().write(doc)));
return Json::StyledWriter().write(doc);
}
unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef)
string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
{
// LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown
@ -212,7 +212,7 @@ unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition cons
// LTODO: mismatching parameter name, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(
DocstringParsingError() <<
errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function")
errinfo_comment("documented parameter \"" + pair.first + "\" not found in the parameter list of the function.")
);
params[pair.first] = pair.second;
}
@ -229,7 +229,7 @@ unique_ptr<string> InterfaceHandler::getDevDocumentation(ContractDefinition cons
}
doc["methods"] = methods;
return unique_ptr<string>(new string(Json::FastWriter().write(doc)));
return Json::StyledWriter().write(doc);
}
/* -- private -- */

25
libsolidity/InterfaceHandler.h

@ -65,28 +65,25 @@ public:
/// @param _contractDef The contract definition
/// @param _type The type of the documentation. Can be one of the
/// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json
/// representation of provided type
std::unique_ptr<std::string> getDocumentation(
/// @return A string with the json representation of provided type
std::string getDocumentation(
ContractDefinition const& _contractDef,
DocumentationType _type
);
/// Get the ABI Interface of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef);
std::unique_ptr<std::string> getABISolidityInterface(ContractDefinition const& _contractDef);
/// @return A string with the json representation of the contract's ABI Interface
std::string getABIInterface(ContractDefinition const& _contractDef);
std::string getABISolidityInterface(ContractDefinition const& _contractDef);
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition const& _contractDef);
/// Get the Developer's documentation of the contract
/// @return A string with the json representation of the contract's user documentation
std::string userDocumentation(ContractDefinition const& _contractDef);
/// Genereates the Developer's documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition const& _contractDef);
/// @return A string with the json representation
/// of the contract's developer documentation
std::string devDocumentation(ContractDefinition const& _contractDef);
private:
void resetUser();

1
libsolidity/Parser.cpp

@ -26,6 +26,7 @@
#include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/InterfaceHandler.h>
using namespace std;

2
mix/CodeModel.cpp

@ -157,7 +157,7 @@ CompiledContract::CompiledContract(const dev::solidity::CompilerStack& _compiler
m_bytes = _compiler.getBytecode(_contractName.toStdString());
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
m_contractInterface = QString::fromStdString(interfaceHandler.getABIInterface(contractDefinition));
if (m_contractInterface.isEmpty())
m_contractInterface = "[]";
if (contractDefinition.getLocation().sourceName.get())

5
solc/CommandLineInterface.cpp

@ -449,6 +449,11 @@ bool CommandLineInterface::processInput()
<< boost::diagnostic_information(_exception);
return false;
}
catch (DocstringParsingError const& _exception)
{
cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_exception) << endl;
return false;
}
catch (Exception const& _exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl;

4
solc/jsonCompiler.cpp

@ -147,6 +147,10 @@ string compile(string _input, bool _optimize)
{
return formatError(exception, "Internal compiler error", compiler);
}
catch (DocstringParsingError const& exception)
{
return formatError(exception, "Documentation parsing error", compiler);
}
catch (Exception const& exception)
{
output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception);

27
test/libsolidity/SolidityEndToEndTest.cpp

@ -25,6 +25,7 @@
#include <tuple>
#include <boost/test/unit_test.hpp>
#include <libdevcore/Hash.h>
#include <libsolidity/Exceptions.h>
#include <test/libsolidity/solidityExecutionFramework.h>
using namespace std;
@ -4657,6 +4658,32 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access)
) == encodeArgs(u256(data.size()), string("d")));
}
BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @title I really should not be here\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
compileRequireThrow<DocstringParsingError>(sourceCode);
}
BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param not_existing Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
compileRequireThrow<DocstringParsingError>(sourceCode);
}
BOOST_AUTO_TEST_CASE(storage_array_ref)
{
char const* sourceCode = R"(

93
test/libsolidity/SolidityNatspecJSON.cpp

@ -21,6 +21,7 @@
*/
#include "../TestHelper.h"
#include <string>
#include <json/json.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
@ -38,9 +39,11 @@ class DocumentationChecker
public:
DocumentationChecker(): m_compilerStack(false) {}
void checkNatspec(std::string const& _code,
std::string const& _expectedDocumentationString,
bool _userDocumentation)
void checkNatspec(
std::string const& _code,
std::string const& _expectedDocumentationString,
bool _userDocumentation
)
{
std::string generatedDocumentationString;
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed");
@ -53,9 +56,11 @@ public:
m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation;
m_reader.parse(_expectedDocumentationString, expectedDocumentation);
BOOST_CHECK_MESSAGE(expectedDocumentation == generatedDocumentation,
"Expected " << _expectedDocumentationString <<
"\n but got:\n" << generatedDocumentationString);
BOOST_CHECK_MESSAGE(
expectedDocumentation == generatedDocumentation,
"Expected " << _expectedDocumentationString <<
"\n but got:\n" << generatedDocumentationString
);
}
private:
@ -229,18 +234,6 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params)
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_documenting_nonexistant_param)
{
char const* sourceCode = "contract test {\n"
" /// @dev Multiplies a number by 7 and adds second parameter\n"
" /// @param a Documentation for the first parameter\n"
" /// @param not_existing Documentation for the second parameter\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(dev_mutiline_param_description)
{
char const* sourceCode = "contract test {\n"
@ -488,46 +481,48 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function)
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_title_at_function_error)
{
char const* sourceCode = " /// @author Lefteris\n"
" /// @title Just a test contract\n"
"contract test {\n"
" /// @dev Mul function\n"
" /// @title I really should not be here\n"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
BOOST_CHECK_THROW(checkNatspec(sourceCode, "", false), DocstringParsingError);
}
BOOST_AUTO_TEST_CASE(natspec_notice_without_tag)
{
char const* sourceCode = "contract test {\n"
" /// I do something awesome\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* sourceCode = R"(
contract test {
/// I do something awesome
function mul(uint a) returns(uint d) { return a * 7; }
}
)";
char const* natspec = "{"
"\"methods\":{"
" \"mul(uint256)\":{ \"notice\": \"I do something awesome\"}"
"}}";
char const* natspec = R"ABCDEF(
{
"methods" : {
"mul(uint256)" : {
"notice" : "I do something awesome"
}
}
}
)ABCDEF";
checkNatspec(sourceCode, natspec, true);
}
BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag)
{
char const* sourceCode = "contract test {\n"
" /// I do something awesome\n"
" /// which requires two lines to explain\n"
" function mul(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul(uint256)\":{ \"notice\": \"I do something awesome which requires two lines to explain\"}"
"}}";
char const* sourceCode = R"(
contract test {
/// I do something awesome
/// which requires two lines to explain
function mul(uint a) returns(uint d) { return a * 7; }
}
)";
char const* natspec = R"ABCDEF(
{
"methods" : {
"mul(uint256)" : {
"notice" : "I do something awesome which requires two lines to explain"
}
}
}
)ABCDEF";
checkNatspec(sourceCode, natspec, true);
}

9
test/libsolidity/solidityExecutionFramework.h

@ -28,6 +28,7 @@
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/Exceptions.h>
namespace dev
{
@ -57,6 +58,14 @@ public:
return m_output;
}
template <class Exceptiontype>
void compileRequireThrow(std::string const& _sourceCode)
{
m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", _sourceCode);
BOOST_REQUIRE_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), Exceptiontype);
}
bytes const& compileAndRun(
std::string const& _sourceCode,
u256 const& _value = 0,

Loading…
Cancel
Save