mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
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:
commit
20189c3f3b
@ -17,6 +17,7 @@ Compiler Features:
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Compiler Interface: Report specific error message for json input errors instead of internal compiler error
|
||||
|
||||
|
||||
Build System:
|
||||
|
@ -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
|
||||
|
11
test/cmdlineTests/standard_wrong_type_auxiliary_input.json
Normal file
11
test/cmdlineTests/standard_wrong_type_auxiliary_input.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||
}
|
||||
},
|
||||
"auxiliaryInput": [1, 2, 3]
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1,2 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput\" must be an object","message":"\"auxiliaryInput\" must be an object","severity":"error","type":"JSONError"}]}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput.smtlib2responses\" must be an object.","message":"\"auxiliaryInput.smtlib2responses\" must be an object.","severity":"error","type":"JSONError"}]}
|
@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"smtlib2Responses.abc\" must be a string.","message":"\"smtlib2Responses.abc\" must be a string.","severity":"error","type":"JSONError"}]}
|
19
test/cmdlineTests/standard_wrong_type_metadata.json
Normal file
19
test/cmdlineTests/standard_wrong_type_metadata.json
Normal 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"]
|
||||
}
|
||||
}
|
1
test/cmdlineTests/standard_wrong_type_metadata.json.exit
Normal file
1
test/cmdlineTests/standard_wrong_type_metadata.json.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"settings.metadata\" must be an object","message":"\"settings.metadata\" must be an object","severity":"error","type":"JSONError"}]}
|
18
test/cmdlineTests/standard_wrong_type_optimizer.json
Normal file
18
test/cmdlineTests/standard_wrong_type_optimizer.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer\" must be an object","message":"\"settings.optimizer\" must be an object","severity":"error","type":"JSONError"}]}
|
11
test/cmdlineTests/standard_wrong_type_output_selection.json
Normal file
11
test/cmdlineTests/standard_wrong_type_output_selection.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": {
|
||||
"fileA": {
|
||||
"content": "contract A { }"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"outputSelection": "not an object"
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection\" must be an object","message":"\"settings.outputSelection\" must be an object","severity":"error","type":"JSONError"}]}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": {
|
||||
"fileA": {
|
||||
"content": "contract A { }"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"outputSelection": {
|
||||
"fileA": {
|
||||
"A": "it's a contract, but not an array!",
|
||||
"": [ "legacyAST" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -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"}]}
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": {
|
||||
"fileA": {
|
||||
"content": "contract A { }"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"outputSelection": {
|
||||
"fileA": "awesome file!"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -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"}]}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": {
|
||||
"fileA": {
|
||||
"content": "contract A { }"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"outputSelection": {
|
||||
"fileA": {
|
||||
"A": [ 1, 2, 3 ,4],
|
||||
"": [ "legacyAST" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -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"}]}
|
17
test/cmdlineTests/standard_wrong_type_remappings.json
Normal file
17
test/cmdlineTests/standard_wrong_type_remappings.json
Normal 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"
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -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"}]}
|
17
test/cmdlineTests/standard_wrong_type_remappings_entry.json
Normal file
17
test/cmdlineTests/standard_wrong_type_remappings_entry.json
Normal 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]
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
0
|
@ -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"}]}
|
1
test/cmdlineTests/standard_wrong_type_root.json
Normal file
1
test/cmdlineTests/standard_wrong_type_root.json
Normal file
@ -0,0 +1 @@
|
||||
["abc"]
|
1
test/cmdlineTests/standard_wrong_type_root.json.exit
Normal file
1
test/cmdlineTests/standard_wrong_type_root.json.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
1
test/cmdlineTests/standard_wrong_type_root.json.stdout
Normal file
1
test/cmdlineTests/standard_wrong_type_root.json.stdout
Normal 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"}]}
|
23
test/cmdlineTests/standard_wrong_type_settings.json
Normal file
23
test/cmdlineTests/standard_wrong_type_settings.json
Normal 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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
1
test/cmdlineTests/standard_wrong_type_settings.json.exit
Normal file
1
test/cmdlineTests/standard_wrong_type_settings.json.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"settings\" must be an object","message":"\"settings\" must be an object","severity":"error","type":"JSONError"}]}
|
12
test/cmdlineTests/standard_wrong_type_source.json
Normal file
12
test/cmdlineTests/standard_wrong_type_source.json
Normal 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 {} }"
|
||||
}
|
||||
}
|
||||
}
|
1
test/cmdlineTests/standard_wrong_type_source.json.exit
Normal file
1
test/cmdlineTests/standard_wrong_type_source.json.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
1
test/cmdlineTests/standard_wrong_type_source.json.stdout
Normal file
1
test/cmdlineTests/standard_wrong_type_source.json.stdout
Normal 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"}]}
|
4
test/cmdlineTests/standard_wrong_type_sources.json
Normal file
4
test/cmdlineTests/standard_wrong_type_sources.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"language": "Solidity",
|
||||
"sources": ["source1", "source2", "source3"]
|
||||
}
|
1
test/cmdlineTests/standard_wrong_type_sources.json.exit
Normal file
1
test/cmdlineTests/standard_wrong_type_sources.json.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
@ -0,0 +1 @@
|
||||
{"errors":[{"component":"general","formattedMessage":"\"sources\" is not a JSON object.","message":"\"sources\" is not a JSON object.","severity":"error","type":"JSONError"}]}
|
Loading…
Reference in New Issue
Block a user