diff --git a/Changelog.md b/Changelog.md index aac8ab8b2895..efca354e724b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,6 +19,7 @@ Bugfixes: * Commandline Interface: It is no longer possible to specify both ``--optimize-yul`` and ``--no-optimize-yul`` at the same time. * SMTChecker: Fix encoding of side-effects inside ``if`` and ``ternary conditional``statements in the BMC engine. * SMTChecker: Fix false negative when a verification target can be violated only by trusted external call from another public function. + * Standard JSON Interface: Fix an incomplete AST being returned when analysis is interrupted by certain kinds of fatal errors. * Yul Optimizer: Ensure that the assignment of memory slots for variables moved to memory does not depend on AST IDs that may depend on whether additional files are included during compilation. * Yul Optimizer: Fix optimized IR being unnecessarily passed through the Yul optimizer again before bytecode generation. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index f18c618e9c6f..9d1ac1f1f3c2 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -1305,15 +1305,21 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting )); } + bool parsingSuccess = compilerStack.state() >= CompilerStack::State::Parsed; bool analysisPerformed = compilerStack.state() >= CompilerStack::State::AnalysisPerformed; - bool const compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful; + bool compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful; if (compilerStack.hasError() && !_inputsAndSettings.parserErrorRecovery) analysisPerformed = false; + // If analysis fails, the artifacts inside CompilerStack are potentially incomplete and must not be returned. + // Note that not completing analysis due to stopAfter does not count as a failure. It's neither failure nor success. + bool analysisFailed = !analysisPerformed && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed; + bool compilationFailed = !compilationSuccess && binariesRequested; + /// Inconsistent state - stop here to receive error reports from users if ( - ((binariesRequested && !compilationSuccess) || !analysisPerformed) && + (compilationFailed || !analysisPerformed) && (errors.empty() && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed) ) return formatFatalError(Error::Type::InternalCompilerError, "No error reported, but compilation failed."); @@ -1331,7 +1337,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting output["sources"] = Json::objectValue; unsigned sourceIndex = 0; - if (compilerStack.state() >= CompilerStack::State::Parsed && (!compilerStack.hasError() || _inputsAndSettings.parserErrorRecovery)) + if (parsingSuccess && !analysisFailed && (!compilerStack.hasError() || _inputsAndSettings.parserErrorRecovery)) for (string const& sourceName: compilerStack.sourceNames()) { Json::Value sourceResult = Json::objectValue; diff --git a/test/cmdlineTests/standard_empty_file_name/output.json b/test/cmdlineTests/standard_empty_file_name/output.json index e419d0a5eec0..4e4d84c41c75 100644 --- a/test/cmdlineTests/standard_empty_file_name/output.json +++ b/test/cmdlineTests/standard_empty_file_name/output.json @@ -18,11 +18,5 @@ "type": "DeclarationError" } ], - "sources": - { - "": - { - "id": 0 - } - } + "sources": {} } diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/args b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/args new file mode 100644 index 000000000000..18532c5a6d3f --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/args @@ -0,0 +1 @@ +--allow-paths . diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/in.sol b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/in.sol new file mode 100644 index 000000000000..b31d640322ad --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/in.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C { + // This will trigger a fatal error at the analysis stage, of the kind that terminates analysis + // immediately without letting the current step finish. + constructor(uint[] storage) {} +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/input.json b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/input.json new file mode 100644 index 000000000000..b640f033dc46 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/input.json @@ -0,0 +1,14 @@ +{ + "language": "Solidity", + "sources": { + "C": {"urls": ["standard_outputs_on_analysis_error_fatal/in.sol"]} + }, + "settings": { + "outputSelection": { + "*": { + "*": ["*"], + "": ["*"] + } + } + } +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/output.json b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/output.json new file mode 100644 index 000000000000..51d12668fb13 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal/output.json @@ -0,0 +1,26 @@ +{ + "errors": + [ + { + "component": "general", + "errorCode": "3644", + "formattedMessage": "TypeError: This parameter has a type that can only be used internally. You can make the contract abstract to avoid this problem. + --> C:7:17: + | +7 | constructor(uint[] storage) {} + | ^^^^^^^^^^^^^^ + +", + "message": "This parameter has a type that can only be used internally. You can make the contract abstract to avoid this problem.", + "severity": "error", + "sourceLocation": + { + "end": 258, + "file": "C", + "start": 244 + }, + "type": "TypeError" + } + ], + "sources": {} +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/args b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/args new file mode 100644 index 000000000000..18532c5a6d3f --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/args @@ -0,0 +1 @@ +--allow-paths . diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/in.sol b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/in.sol new file mode 100644 index 000000000000..6c8bb3938b76 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +// This will trigger a fatal error at the analysis stage, of the kind that lets the current +// analysis steps finish but terminates analysis after immediately after that step. +function f(uint immutable x) {} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/input.json b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/input.json new file mode 100644 index 000000000000..0214e00e8406 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/input.json @@ -0,0 +1,14 @@ +{ + "language": "Solidity", + "sources": { + "C": {"urls": ["standard_outputs_on_analysis_error_fatal_after_current_step/in.sol"]} + }, + "settings": { + "outputSelection": { + "*": { + "*": ["*"], + "": ["*"] + } + } + } +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/output.json b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/output.json new file mode 100644 index 000000000000..d47ded5d4018 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_fatal_after_current_step/output.json @@ -0,0 +1,26 @@ +{ + "errors": + [ + { + "component": "general", + "errorCode": "8297", + "formattedMessage": "DeclarationError: The \"immutable\" keyword can only be used for state variables. + --> C:6:12: + | +6 | function f(uint immutable x) {} + | ^^^^^^^^^^^^^^^^ + +", + "message": "The \"immutable\" keyword can only be used for state variables.", + "severity": "error", + "sourceLocation": + { + "end": 259, + "file": "C", + "start": 243 + }, + "type": "DeclarationError" + } + ], + "sources": {} +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/args b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/args new file mode 100644 index 000000000000..18532c5a6d3f --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/args @@ -0,0 +1 @@ +--allow-paths . diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/in.sol b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/in.sol new file mode 100644 index 000000000000..050f5ad43673 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/in.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C { + // This will trigger a non-fatal error at the analysis stage. + // With this kind of error we still run subsequent analysis stages. + uint x; + string y = x; +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/input.json b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/input.json new file mode 100644 index 000000000000..904ebc5bf287 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/input.json @@ -0,0 +1,14 @@ +{ + "language": "Solidity", + "sources": { + "C": {"urls": ["standard_outputs_on_analysis_error_non_fatal/in.sol"]} + }, + "settings": { + "outputSelection": { + "*": { + "*": ["*"], + "": ["*"] + } + } + } +} diff --git a/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/output.json b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/output.json new file mode 100644 index 000000000000..2f3aaae4420c --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_analysis_error_non_fatal/output.json @@ -0,0 +1,26 @@ +{ + "errors": + [ + { + "component": "general", + "errorCode": "7407", + "formattedMessage": "TypeError: Type uint256 is not implicitly convertible to expected type string storage ref. + --> C:8:16: + | +8 | string y = x; + | ^ + +", + "message": "Type uint256 is not implicitly convertible to expected type string storage ref.", + "severity": "error", + "sourceLocation": + { + "end": 235, + "file": "C", + "start": 234 + }, + "type": "TypeError" + } + ], + "sources": {} +} diff --git a/test/cmdlineTests/standard_outputs_on_compilation_error/args b/test/cmdlineTests/standard_outputs_on_compilation_error/args new file mode 100644 index 000000000000..18532c5a6d3f --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_compilation_error/args @@ -0,0 +1 @@ +--allow-paths . diff --git a/test/cmdlineTests/standard_outputs_on_compilation_error/in.sol b/test/cmdlineTests/standard_outputs_on_compilation_error/in.sol new file mode 100644 index 000000000000..1d5118b87287 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_compilation_error/in.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C { + // This will trigger an error at the compilation stage. + // CodeGenerationError due to immutable initialization in constructor being optimized out. + uint immutable public x; + + constructor() { + x = 0; + while (true) {} + } +} diff --git a/test/cmdlineTests/standard_outputs_on_compilation_error/input.json b/test/cmdlineTests/standard_outputs_on_compilation_error/input.json new file mode 100644 index 000000000000..855f007689b8 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_compilation_error/input.json @@ -0,0 +1,15 @@ +{ + "language": "Solidity", + "sources": { + "C": {"urls": ["standard_outputs_on_compilation_error/in.sol"]} + }, + "settings": { + "optimizer": {"enabled": true}, + "outputSelection": { + "*": { + "*": ["*"], + "": ["*"] + } + } + } +} diff --git a/test/cmdlineTests/standard_outputs_on_compilation_error/output.json b/test/cmdlineTests/standard_outputs_on_compilation_error/output.json new file mode 100644 index 000000000000..ac01fb1eba9c --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_compilation_error/output.json @@ -0,0 +1,280 @@ +{ + "contracts": + { + "C": + { + "C": + { + "abi": + [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "x", + "outputs": + [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": + { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "evm": + { + "methodIdentifiers": + { + "x()": "0c55699c" + } + }, + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"x\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0x67a13ebd685e4c6f792e71eb747dac57edb99e94d04d841ee6c979ae517934ce\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://665b000da768823654f680d02686c1e59d682a0b3882e43a77fed9f80ce64ae8\",\"dweb:/ipfs/QmVnKvuidH6KiCdNQpoAQUtDbB8hXkafVLXWMNitUcxnqC\"]}},\"version\":1}", + "storageLayout": + { + "storage": [], + "types": null + }, + "userdoc": + { + "kind": "user", + "methods": {}, + "version": 1 + } + } + } + }, + "errors": + [ + { + "component": "general", + "errorCode": "1284", + "formattedMessage": "CodeGenerationError: Some immutables were read from but never assigned, possibly because of optimization. + +", + "message": "Some immutables were read from but never assigned, possibly because of optimization.", + "severity": "error", + "type": "CodeGenerationError" + } + ], + "sources": + { + "C": + { + "ast": + { + "absolutePath": "C", + "exportedSymbols": + { + "C": + [ + 15 + ] + }, + "id": 16, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": + [ + { + "id": 1, + "literals": + [ + "solidity", + "*" + ], + "nodeType": "PragmaDirective", + "src": "36:18:0" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "C", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 15, + "linearizedBaseContracts": + [ + 15 + ], + "name": "C", + "nameLocation": "65:1:0", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "functionSelector": "0c55699c", + "id": 3, + "mutability": "immutable", + "name": "x", + "nameLocation": "250:1:0", + "nodeType": "VariableDeclaration", + "scope": 15, + "src": "228:23:0", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "228:4:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "public" + }, + { + "body": + { + "id": 13, + "nodeType": "Block", + "src": "272:46:0", + "statements": + [ + { + "expression": + { + "id": 8, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "leftHandSide": + { + "id": 6, + "name": "x", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 3, + "src": "282:1:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": + { + "hexValue": "30", + "id": 7, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "286:1:0", + "typeDescriptions": + { + "typeIdentifier": "t_rational_0_by_1", + "typeString": "int_const 0" + }, + "value": "0" + }, + "src": "282:5:0", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 9, + "nodeType": "ExpressionStatement", + "src": "282:5:0" + }, + { + "body": + { + "id": 11, + "nodeType": "Block", + "src": "310:2:0", + "statements": [] + }, + "condition": + { + "hexValue": "74727565", + "id": 10, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "bool", + "lValueRequested": false, + "nodeType": "Literal", + "src": "304:4:0", + "typeDescriptions": + { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "value": "true" + }, + "id": 12, + "nodeType": "WhileStatement", + "src": "297:15:0" + } + ] + }, + "id": 14, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nameLocation": "-1:-1:-1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": [], + "src": "269:2:0" + }, + "returnParameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "272:0:0" + }, + "scope": 15, + "src": "258:60:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 16, + "src": "56:264:0", + "usedErrors": [], + "usedEvents": [] + } + ], + "src": "36:285:0" + }, + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_outputs_on_parsing_error/args b/test/cmdlineTests/standard_outputs_on_parsing_error/args new file mode 100644 index 000000000000..18532c5a6d3f --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_parsing_error/args @@ -0,0 +1 @@ +--allow-paths . diff --git a/test/cmdlineTests/standard_outputs_on_parsing_error/in.sol b/test/cmdlineTests/standard_outputs_on_parsing_error/in.sol new file mode 100644 index 000000000000..a4d891c1b25a --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_parsing_error/in.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +// This will trigger an error at the parsing stage +contract C {}} diff --git a/test/cmdlineTests/standard_outputs_on_parsing_error/input.json b/test/cmdlineTests/standard_outputs_on_parsing_error/input.json new file mode 100644 index 000000000000..a0d00c4e9e39 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_parsing_error/input.json @@ -0,0 +1,14 @@ +{ + "language": "Solidity", + "sources": { + "C": {"urls": ["standard_outputs_on_parsing_error/in.sol"]} + }, + "settings": { + "outputSelection": { + "*": { + "*": ["*"], + "": ["*"] + } + } + } +} diff --git a/test/cmdlineTests/standard_outputs_on_parsing_error/output.json b/test/cmdlineTests/standard_outputs_on_parsing_error/output.json new file mode 100644 index 000000000000..8c1f6c0dbe28 --- /dev/null +++ b/test/cmdlineTests/standard_outputs_on_parsing_error/output.json @@ -0,0 +1,26 @@ +{ + "errors": + [ + { + "component": "general", + "errorCode": "7858", + "formattedMessage": "ParserError: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. + --> C:5:14: + | +5 | contract C {}} + | ^ + +", + "message": "Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition.", + "severity": "error", + "sourceLocation": + { + "end": 121, + "file": "C", + "start": 120 + }, + "type": "ParserError" + } + ], + "sources": {} +}