Merge pull request #5637 from ethereum/better-json-error-reporting

Json: Provide better error message when 'settings' is not an object
This commit is contained in:
chriseth 2018-12-18 19:39:46 +01:00 committed by GitHub
commit 20189c3f3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 330 additions and 13 deletions

View File

@ -17,6 +17,7 @@ Compiler Features:
Bugfixes:
* Compiler Interface: Report specific error message for json input errors instead of internal compiler error
Build System:

View File

@ -228,48 +228,97 @@ Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _so
return output;
}
boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys)
boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
{
if (!!_input && !_input.isObject())
return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
for (auto const& member: _input.getMemberNames())
if (!_keys.count(member))
return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
return boost::none;
}
boost::optional<Json::Value> checkRootKeys(Json::Value const& _input)
{
static set<string> keys{"auxiliaryInput", "language", "settings", "sources"};
return checkKeys(_input, keys);
return checkKeys(_input, keys, "root");
}
boost::optional<Json::Value> checkSourceKeys(Json::Value const& _input)
boost::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
{
static set<string> keys{"content", "keccak256", "urls"};
return checkKeys(_input, keys);
return checkKeys(_input, keys, "sources." + _name);
}
boost::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
{
static set<string> keys{"smtlib2responses"};
return checkKeys(_input, keys);
return checkKeys(_input, keys, "auxiliaryInput");
}
boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
{
static set<string> keys{"evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"};
return checkKeys(_input, keys);
return checkKeys(_input, keys, "settings");
}
boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
{
static set<string> keys{"enabled", "runs"};
return checkKeys(_input, keys);
return checkKeys(_input, keys, "settings.optimizer");
}
boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
{
static set<string> keys{"useLiteralContent"};
return checkKeys(_input, keys);
return checkKeys(_input, keys, "settings.metadata");
}
boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
{
if (!!_outputSelection && !_outputSelection.isObject())
return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
for (auto const& sourceName: _outputSelection.getMemberNames())
{
auto const& sourceVal = _outputSelection[sourceName];
if (!sourceVal.isObject())
return formatFatalError(
"JSONError",
"\"settings.outputSelection." + sourceName + "\" must be an object"
);
for (auto const& contractName: sourceVal.getMemberNames())
{
auto const& contractVal = sourceVal[contractName];
if (!contractVal.isArray())
return formatFatalError(
"JSONError",
"\"settings.outputSelection." +
sourceName +
"." +
contractName +
"\" must be a string array"
);
for (auto const& output: contractVal)
if (!output.isString())
return formatFatalError(
"JSONError",
"\"settings.outputSelection." +
sourceName +
"." +
contractName +
"\" must be a string array"
);
}
}
return boost::none;
}
}
@ -301,10 +350,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
string hash;
if (!sources[sourceName].isObject())
return formatFatalError("JSONError", "Source input is not a JSON object.");
if (auto result = checkSourceKeys(sources[sourceName]))
if (auto result = checkSourceKeys(sources[sourceName], sourceName))
return *result;
if (sources[sourceName]["keccak256"].isString())
@ -380,6 +426,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"];
if (!!smtlib2Responses)
{
if (!smtlib2Responses.isObject())
return formatFatalError("JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object.");
for (auto const& hashString: smtlib2Responses.getMemberNames())
{
h256 hash;
@ -392,8 +442,15 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input.");
}
if (!smtlib2Responses[hashString].isString())
return formatFatalError(
"JSONError",
"\"smtlib2Responses." + hashString + "\" must be a string."
);
m_compilerStack.addSMTLib2Response(hash, smtlib2Responses[hashString].asString());
}
}
}
Json::Value const& settings = _input.get("settings", Json::Value());
@ -411,11 +468,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setEVMVersion(*version);
}
if (settings.isMember("remappings") && !settings["remappings"].isArray())
return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings.");
vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
{
if (!remapping.isString())
return formatFatalError("JSONError", "Remapping entry must be a string.");
return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings");
if (auto r = CompilerStack::parseRemapping(remapping.asString()))
remappings.emplace_back(std::move(*r));
else
@ -498,6 +558,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());
Json::Value outputSelection = settings.get("outputSelection", Json::Value());
if (auto jsonError = checkOutputSelection(outputSelection))
return *jsonError;
m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection));
try

View File

@ -0,0 +1,11 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
},
"auxiliaryInput": [1, 2, 3]
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1,2 @@
{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput\" must be an object","message":"\"auxiliaryInput\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,20 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
"": [ "legacyAST" ]
}
}
},
"auxiliaryInput":
{
"smtlib2responses": "not an object"
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput.smtlib2responses\" must be an object.","message":"\"auxiliaryInput.smtlib2responses\" must be an object.","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,23 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
"": [ "legacyAST" ]
}
}
},
"auxiliaryInput":
{
"smtlib2responses":
{
"abc": ["not a string"]
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"smtlib2Responses.abc\" must be a string.","message":"\"smtlib2Responses.abc\" must be a string.","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,19 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
},
"settings":
{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "byzantium",
"metadata": ["meta1", "meta2", "meta3"]
}
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.metadata\" must be an object","message":"\"settings.metadata\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,18 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
},
"settings":
{
"optimizer": 1,
"evmVersion": "byzantium",
"metadata": {
"useLiteralContent": true
}
}
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer\" must be an object","message":"\"settings.optimizer\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,11 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": "not an object"
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection\" must be an object","message":"\"settings.outputSelection\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": "it's a contract, but not an array!",
"": [ "legacyAST" ]
}
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,13 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": "awesome file!"
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA\" must be an object","message":"\"settings.outputSelection.fileA\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ 1, 2, 3 ,4],
"": [ "legacyAST" ]
}
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,17 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
"": [ "legacyAST" ]
}
},
"remappings": "not an object"
}
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings.","message":"\"settings.remappings\" must be an array of strings.","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,17 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
"": [ "legacyAST" ]
}
},
"remappings": [1, 2 ,3 ,4]
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings","message":"\"settings.remappings\" must be an array of strings","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1 @@
["abc"]

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"Input is not a JSON object.","message":"Input is not a JSON object.","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,23 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
},
"settings":
[
{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "byzantium",
"metadata": {
"useLiteralContent": true
}
}
]
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings\" must be an object","message":"\"settings\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,12 @@
{
"language": "Solidity",
"sources":
{
"A": "not an object :o",
"B": [1, 2, 3],
"C":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
}
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"sources.A\" must be an object","message":"\"sources.A\" must be an object","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,4 @@
{
"language": "Solidity",
"sources": ["source1", "source2", "source3"]
}

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"sources\" is not a JSON object.","message":"\"sources\" is not a JSON object.","severity":"error","type":"JSONError"}]}