diff --git a/Changelog.md b/Changelog.md index 50eb08f24..098be7003 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Compiler Features: * eWasm: Highly experimental eWasm output using ``--ewasm`` in the commandline interface or output selection of ``ewasm.wast`` in standard-json. * Metadata: Update the swarm hash, changes ``bzzr0`` to ``bzzr1`` and urls to use ``bzz-raw://``. * Standard JSON Interface: Compile only selected sources and contracts. + * Standard JSON Interface: Provide secondary error locations (e.g. the source position of other conflicting declarations). diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 55e22dfec..a4ce4e7b3 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -314,6 +314,15 @@ Output Description "start": 0, "end": 100 ], + // Optional: Further locations (e.g. places of conflicting declarations) + "secondarySourceLocations": [ + { + "file": "sourceFile.sol", + "start": 64, + "end": 92, + "message": "Other declaration is here:" + } + ], // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc. // See below for complete list of types. "type": "TypeError", diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index fd23223d5..f61e2af21 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -40,7 +40,8 @@ using namespace langutil; using namespace dev::solidity; using namespace yul; -namespace { +namespace +{ Json::Value formatError( bool _warning, @@ -48,7 +49,8 @@ Json::Value formatError( string const& _component, string const& _message, string const& _formattedMessage = "", - Json::Value const& _sourceLocation = Json::Value() + Json::Value const& _sourceLocation = Json::Value(), + Json::Value const& _secondarySourceLocation = Json::Value() ) { Json::Value error = Json::objectValue; @@ -59,6 +61,8 @@ Json::Value formatError( error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message; if (_sourceLocation.isObject()) error["sourceLocation"] = _sourceLocation; + if (_secondarySourceLocation.isArray()) + error["secondarySourceLocations"] = _secondarySourceLocation; return error; } @@ -70,6 +74,34 @@ Json::Value formatFatalError(string const& _type, string const& _message) return output; } +Json::Value formatSourceLocation(SourceLocation const* location) +{ + Json::Value sourceLocation; + if (location && location->source && !location->source->name().empty()) + { + sourceLocation["file"] = location->source->name(); + sourceLocation["start"] = location->start; + sourceLocation["end"] = location->end; + } + + return sourceLocation; +} + +Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _secondaryLocation) +{ + if (!_secondaryLocation) + return {}; + + Json::Value secondarySourceLocation = Json::arrayValue; + for (auto const& location: _secondaryLocation->infos) + { + Json::Value msg = formatSourceLocation(&location.second); + msg["message"] = location.first; + secondarySourceLocation.append(msg); + } + return secondarySourceLocation; +} + Json::Value formatErrorWithException( Exception const& _exception, bool const& _warning, @@ -81,23 +113,20 @@ Json::Value formatErrorWithException( string message; string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); - // NOTE: the below is partially a copy from SourceReferenceFormatter - SourceLocation const* location = boost::get_error_info(_exception); - if (string const* description = boost::get_error_info(_exception)) message = ((_message.length() > 0) ? (_message + ":") : "") + *description; else message = _message; - Json::Value sourceLocation; - if (location && location->source && location->source->name() != "") - { - sourceLocation["file"] = location->source->name(); - sourceLocation["start"] = location->start; - sourceLocation["end"] = location->end; - } - - return formatError(_warning, _type, _component, message, formattedMessage, sourceLocation); + return formatError( + _warning, + _type, + _component, + message, + formattedMessage, + formatSourceLocation(boost::get_error_info(_exception)), + formatSecondarySourceLocation(boost::get_error_info(_exception)) + ); } map> requestedContractNames(Json::Value const& _outputSelection) diff --git a/test/cmdlineTests/standard_secondary_source_location/exit b/test/cmdlineTests/standard_secondary_source_location/exit new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/test/cmdlineTests/standard_secondary_source_location/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/standard_secondary_source_location/input.json b/test/cmdlineTests/standard_secondary_source_location/input.json new file mode 100644 index 000000000..f08069d9f --- /dev/null +++ b/test/cmdlineTests/standard_secondary_source_location/input.json @@ -0,0 +1,10 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}" + } + } +} diff --git a/test/cmdlineTests/standard_secondary_source_location/output.json b/test/cmdlineTests/standard_secondary_source_location/output.json new file mode 100644 index 000000000..f765579cc --- /dev/null +++ b/test/cmdlineTests/standard_secondary_source_location/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"A:1:112: DeclarationError: Base constructor arguments given twice.\npragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}\n ^-------------------^\nA:1:81: First constructor call is here: \npragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}\n ^--^\nA:1:104: Second constructor call is here: \npragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}\n ^--^\n","message":"Base constructor arguments given twice.","secondarySourceLocations":[{"end":84,"file":"A","message":"First constructor call is here: ","start":80},{"end":107,"file":"A","message":"Second constructor call is here: ","start":103}],"severity":"error","sourceLocation":{"end":132,"file":"A","start":111},"type":"DeclarationError"}],"sources":{}}