diff --git a/.travis.yml b/.travis.yml index 43bd5b9b9..ef7832777 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,8 @@ env: - ENCRYPTION_LABEL="6d4541b72666" - SOLC_BUILD_TYPE=RelWithDebInfo - SOLC_EMSCRIPTEN=Off + # FIXME: Pushing solcjson.js to solc-bin disabled until we fix the cause of #9261 + - SOLC_PUBLISH_EMSCRIPTEN=Off - SOLC_INSTALL_DEPS_TRAVIS=On - SOLC_RELEASE=On - SOLC_TESTS=On @@ -104,12 +106,12 @@ matrix: sudo: required compiler: gcc node_js: - - "8" + - "10" services: - docker before_install: - - nvm install 8 - - nvm use 8 + - nvm install 10 + - nvm use 10 - docker pull ethereum/solidity-buildpack-deps:emsdk-1.39.15-2 env: - SOLC_EMSCRIPTEN=On @@ -213,7 +215,7 @@ deploy: # scripts because TravisCI doesn't provide much in the way of conditional logic. - provider: script - script: test $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh) + script: test $SOLC_PUBLISH_EMSCRIPTEN != On || $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh) skip_cleanup: true on: branch: diff --git a/Changelog.md b/Changelog.md index 1371af1f8..328d777a9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,12 +26,14 @@ Bugfixes: Language Features: + * General: Add unit denomination ``gwei`` Compiler Features: * NatSpec: Add fields "kind" and "version" to the JSON output. + * NatSpec: Inherit tags from unique base if derived function does not provide any. * Commandline Interface: Prevent some incompatible commandline options from being used together. - + * NatSpec: Support NatSpec comments on events. Bugfixes: * NatSpec: Do not consider ``////`` and ``/***`` as NatSpec comments. diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 index 65fee05fa..af351731b 100644 --- a/docs/Solidity.g4 +++ b/docs/Solidity.g4 @@ -359,10 +359,10 @@ subAssembly : 'assembly' identifier assemblyBlock ; numberLiteral - : (DecimalNumber | HexNumber) NumberUnit? ; + : (DecimalNumber | HexNumber) (NumberUnit | Gwei)?; identifier - : ('from' | 'calldata' | 'address' | Identifier) ; + : (Gwei | 'from' | 'calldata' | 'address' | Identifier) ; BooleanLiteral : 'true' | 'false' ; @@ -385,6 +385,8 @@ NumberUnit : 'wei' | 'szabo' | 'finney' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; +Gwei: 'gwei' ; + HexLiteralFragment : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 437cf3cc1..dcfe63095 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -42,10 +42,6 @@ The following example shows a contract and a function using all available tags. .. note:: - NatSpec currently does NOT apply to public state variables (see - `solidity#3418 `__), - even if they are declared public and therefore do affect the ABI. - The Solidity compiler only interprets tags if they are external or public. You are welcome to use similar comments for your internal and private functions, but those will not be parsed. @@ -60,7 +56,6 @@ The following example shows a contract and a function using all available tags. /// @notice You can use this contract for only the most basic simulation /// @dev All function calls are currently implemented without side effects contract Tree { - /// @author Mary A. Botanist /// @notice Calculate tree age in years, rounded up, for live trees /// @dev The Alexandr N. Tetearing algorithm could increase precision /// @param rings The number of rings from dendrochronological sample @@ -84,10 +79,10 @@ in the same way as if it were tagged with ``@notice``. Tag Context =========== =============================================================================== ============================= ``@title`` A title that should describe the contract/interface contract, interface -``@author`` The name of the author contract, interface, function -``@notice`` Explain to an end user what this does contract, interface, function, public state variable -``@dev`` Explain to a developer any extra details contract, interface, function, state variable -``@param`` Documents a parameter just like in doxygen (must be followed by parameter name) function +``@author`` The name of the author contract, interface +``@notice`` Explain to an end user what this does contract, interface, function, public state variable, event +``@dev`` Explain to a developer any extra details contract, interface, function, state variable, event +``@param`` Documents a parameter just like in doxygen (must be followed by parameter name) function, event ``@return`` Documents the return variables of a contract's function function, public state variable =========== =============================================================================== ============================= @@ -127,9 +122,11 @@ documentation and you may read more at Inheritance Notes ----------------- -Currently it is undefined whether a contract with a function having no -NatSpec will inherit the NatSpec of a parent contract/interface for that -same function. +Functions without NatSpec will automatically inherit the documentation of their +base function. Exceptions to this are: + + * When the parameter names are different. + * When there is more than one base function. .. _header-output: @@ -193,7 +190,6 @@ file should also be produced and should look like this: { "age(uint256)" : { - "author" : "Mary A. Botanist", "details" : "The Alexandr N. Tetearing algorithm could increase precision", "params" : { diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 75e9b5091..28232f6a9 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -7,11 +7,12 @@ Units and Globally Available Variables Ether Units =========== -A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. +A literal number can take a suffix of ``wei``, ``gwei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. :: assert(1 wei == 1); + assert(1 gwei == 1e9); assert(1 szabo == 1e12); assert(1 finney == 1e15); assert(1 ether == 1e18); diff --git a/docs/yul.rst b/docs/yul.rst index b928bf85e..832153625 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -180,7 +180,11 @@ appropriate ``PUSHi`` instruction. In the following example, ``3`` and ``2`` are added resulting in 5 and then the bitwise ``and`` with the string "abc" is computed. The final value is assigned to a local variable called ``x``. + Strings are stored left-aligned and cannot be longer than 32 bytes. +The limit does not apply to string literals passed to builtin functions that require +literal arguments (e.g. ``setimmutable`` or ``loadimmutable``). Those strings never end up in the +generated bytecode. .. code-block:: yul @@ -923,6 +927,30 @@ will store ``value`` at all points in memory that contain a call to ``loadimmutable("name")``. +linkersymbol +^^^^^^^^^^^^ + +The function ``linkersymbol("fq_library_name")`` is a placeholder for an address literal to be +substituted by the linker. Its first and only argument must be a string literal and represents the +fully qualified library name used with the ``--libraries`` option. + +For example this code + +.. code-block:: yul + + let a := linkersymbol("file.sol:Math") + +is equivalent to + +.. code-block:: yul + + let a := 0x1234567890123456789012345678901234567890 + +when the linker is invoked with ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890`` +option. + +See :ref:`Using the Commandline Compiler ` for details about the Solidity linker. + .. _yul-object: diff --git a/liblangutil/Token.h b/liblangutil/Token.h index d9d4471cb..916f71c75 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -232,6 +232,7 @@ namespace solidity::langutil \ /* Identifiers (not keywords or future reserved words). */ \ T(Identifier, nullptr, 0) \ + T(SubGwei, "gwei", 0) \ \ /* Keywords reserved for future use. */ \ K(After, "after", 0) \ diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 9031b7574..ec6ec84ec 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -32,6 +32,33 @@ using namespace solidity; using namespace solidity::langutil; using namespace solidity::frontend; +namespace +{ + +void copyMissingTags(StructurallyDocumentedAnnotation& _target, set const& _baseFunctions) +{ + if (_baseFunctions.size() != 1) + return; + + auto& sourceDoc = dynamic_cast((*_baseFunctions.begin())->annotation()); + + set existingTags; + + for (auto const& iterator: _target.docTags) + existingTags.insert(iterator.first); + + for (auto const& [tag, content]: sourceDoc.docTags) + if (tag != "inheritdoc" && !existingTags.count(tag)) + _target.docTags.emplace(tag, content); +} + +bool parameterNamesEqual(CallableDeclaration const& _a, CallableDeclaration const& _b) +{ + return boost::range::equal(_a.parameters(), _b.parameters(), [](auto const& pa, auto const& pb) { return pa->name() == pb->name(); }); +} + +} + bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit) { auto errorWatcher = m_errorReporter.errorWatcher(); @@ -65,7 +92,24 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable) if (_variable.isPublic()) parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables"); else + { parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables"); + if (_variable.annotation().docTags.count("notice") > 0) + m_errorReporter.warning( + 7816_error, _variable.documentation()->location(), + "Documentation tag on non-public state variables will be disallowed in 0.7.0. " + "You will need to use the @dev tag explicitly." + ); + } + if (_variable.annotation().docTags.count("title") > 0 || _variable.annotation().docTags.count("author") > 0) + m_errorReporter.warning( + 8532_error, _variable.documentation()->location(), + "Documentation tag @title and @author is only allowed on contract definitions. " + "It will be disallowed in 0.7.0." + ); + + if (_variable.annotation().docTags.empty()) + copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions); } return false; } @@ -129,6 +173,20 @@ void DocStringAnalyser::handleCallable( static set const validTags = set{"author", "dev", "notice", "return", "param"}; parseDocStrings(_node, _annotation, validTags, "functions"); checkParameters(_callable, _node, _annotation); + + if ( + _annotation.docTags.empty() && + _callable.annotation().baseFunctions.size() == 1 && + parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin()) + ) + copyMissingTags(_annotation, _callable.annotation().baseFunctions); + + if (_node.documentation() && _annotation.docTags.count("author") > 0) + m_errorReporter.warning( + 9843_error, _node.documentation()->location(), + "Documentation tag @author is only allowed on contract definitions. " + "It will be disallowed in 0.7.0." + ); } void DocStringAnalyser::parseDocStrings( diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index c6c560988..88c3e1beb 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -2084,6 +2084,7 @@ public: { None = static_cast(Token::Illegal), Wei = static_cast(Token::SubWei), + Gwei = static_cast(Token::SubGwei), Szabo = static_cast(Token::SubSzabo), Finney = static_cast(Token::SubFinney), Ether = static_cast(Token::SubEther), diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index c524d676a..240b35f54 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -1002,6 +1002,8 @@ Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _no if (subDenStr == "wei") return Literal::SubDenomination::Wei; + else if (subDenStr == "gwei") + return Literal::SubDenomination::Gwei; else if (subDenStr == "szabo") return Literal::SubDenomination::Szabo; else if (subDenStr == "finney") diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 6f0554539..ff04e402f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -889,6 +889,9 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal case Literal::SubDenomination::Wei: case Literal::SubDenomination::Second: break; + case Literal::SubDenomination::Gwei: + value *= bigint("1000000000"); + break; case Literal::SubDenomination::Szabo: value *= bigint("1000000000000"); break; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index eef773095..164938c60 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -307,11 +307,6 @@ bool CompilerStack::analyze() if (source->ast && !syntaxChecker.checkSyntax(*source->ast)) noErrors = false; - DocStringAnalyser docStringAnalyser(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast)) - noErrors = false; - m_globalContext = make_shared(); // We need to keep the same resolver during the whole process. NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter); @@ -367,6 +362,11 @@ bool CompilerStack::analyze() if (!contractLevelChecker.check(*contract)) noErrors = false; + DocStringAnalyser docStringAnalyser(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast)) + noErrors = false; + // New we run full type checks that go down to the expression level. This // cannot be done earlier, because we need cross-contract types and information // about whether a contract is abstract for the `new` expression. diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index d09dd1c72..0cee43435 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -36,6 +36,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; Json::Value methods(Json::objectValue); + Json::Value events(Json::objectValue); doc["version"] = Json::Value(c_natspecVersion); doc["kind"] = Json::Value("user"); @@ -84,7 +85,17 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) } } } + + for (auto const& event: _contractDef.events()) + { + string value = extractDoc(event->annotation().docTags, "notice"); + if (!value.empty()) + events[event->functionType(true)->externalSignature()]["notice"] = value; + } + doc["methods"] = methods; + if (!events.empty()) + doc["events"] = events; return doc; } @@ -145,9 +156,16 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) stateVariables[varDecl->name()]["return"] = extractDoc(varDecl->annotation().docTags, "return"); } + Json::Value events(Json::objectValue); + for (auto const& event: _contractDef.events()) + if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty()) + events[event->functionType(true)->externalSignature()] = devDoc; + doc["methods"] = methods; if (!stateVariables.empty()) doc["stateVariables"] = stateVariables; + if (!events.empty()) + doc["events"] = events; return doc; } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index ab8180dc7..819d18a79 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1825,11 +1825,16 @@ ASTPointer Parser::parsePrimaryExpression() expression = nodeFactory.createNode(token, getLiteralAndAdvance()); break; case Token::Number: - if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())) + if ( + (m_scanner->peekNextToken() == Token::Identifier && m_scanner->peekLiteral() == "gwei") || + TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()) + ) { ASTPointer literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); - Literal::SubDenomination subdenomination = static_cast(m_scanner->currentToken()); + Token actualToken = m_scanner->currentToken() == Token::Identifier ? Token::SubGwei : m_scanner->currentToken(); + + Literal::SubDenomination subdenomination = static_cast(actualToken); m_scanner->next(); expression = nodeFactory.createNode(token, literal, subdenomination); } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index fd05b6404..512284301 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -301,10 +301,15 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) for (size_t i = _funCall.arguments.size(); i > 0; i--) { Expression const& arg = _funCall.arguments[i - 1]; + bool isLiteralArgument = needsLiteralArguments && (*needsLiteralArguments)[i - 1]; + bool isStringLiteral = holds_alternative(arg) && get(arg).kind == LiteralKind::String; - argTypes.emplace_back(expectExpression(arg)); + if (isLiteralArgument && isStringLiteral) + argTypes.emplace_back(expectUnlimitedStringLiteral(get(arg))); + else + argTypes.emplace_back(expectExpression(arg)); - if (needsLiteralArguments && (*needsLiteralArguments)[i - 1]) + if (isLiteralArgument) { if (!holds_alternative(arg)) m_errorReporter.typeError( @@ -433,6 +438,14 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr) return types.empty() ? m_dialect.defaultType : types.front(); } +YulString AsmAnalyzer::expectUnlimitedStringLiteral(Literal const& _literal) +{ + yulAssert(_literal.kind == LiteralKind::String, ""); + yulAssert(m_dialect.validTypeForLiteral(LiteralKind::String, _literal.value, _literal.type), ""); + + return {_literal.type}; +} + void AsmAnalyzer::expectBoolExpression(Expression const& _expr) { YulString type = expectExpression(_expr); @@ -531,7 +544,7 @@ void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, Sour bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location) { auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier)); - if (builtin) + if (builtin && builtin->instruction.has_value()) return warnOnInstructions(builtin->instruction.value(), _location); else return false; diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 3804476ae..29bfca2fd 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -97,6 +97,7 @@ private: /// Visits the expression, expects that it evaluates to exactly one value and /// returns the type. Reports errors on errors and returns the default type. YulString expectExpression(Expression const& _expr); + YulString expectUnlimitedStringLiteral(Literal const& _literal); /// Vists the expression and expects it to return a single boolean value. /// Reports an error otherwise. void expectBoolExpression(Expression const& _expr); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 6875df234..b6a46043e 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -124,6 +124,17 @@ map createBuiltins(langutil::EVMVersion _evmVe ) builtins.emplace(createEVMFunction(instr.first, instr.second)); + builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {true}, []( + FunctionCall const& _call, + AbstractAssembly& _assembly, + BuiltinContext&, + function + ) { + yulAssert(_call.arguments.size() == 1, ""); + Expression const& arg = _call.arguments.front(); + _assembly.appendLinkerSymbol(std::get(arg).value.str()); + })); + if (_objectAccess) { builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, []( diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 904f2b39d..a27d9821c 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #ifdef _WIN32 // windows @@ -1202,9 +1203,14 @@ bool CommandLineInterface::processInput() }; if (countEnabledOptions(nonAssemblyModeOptions) >= 1) { + auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; }; + auto enabledOptions = boost::copy_range>(nonAssemblyModeOptions | boost::adaptors::filtered(optionEnabled)); + serr() << "The following options are invalid in assembly mode: "; - serr() << joinOptionNames(nonAssemblyModeOptions) << ". "; - serr() << "Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; + serr() << joinOptionNames(enabledOptions) << "."; + if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul)) + serr() << " Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; + serr() << endl; return false; } diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 72a07ce3e..6fea5310a 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -48,6 +48,7 @@ using Address = util::h160; // The various denominations; here for ease of use where needed within code. static const u256 wei = 1; static const u256 shannon = u256("1000000000"); +static const u256 gwei = shannon; static const u256 szabo = shannon * 1000; static const u256 finney = szabo * 1000; static const u256 ether = finney * 1000; diff --git a/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/args b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/args new file mode 100644 index 000000000..827d99829 --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/args @@ -0,0 +1 @@ +--strict-assembly --optimize-yul diff --git a/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/err b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/err new file mode 100644 index 000000000..a6378a665 --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/err @@ -0,0 +1 @@ +The following options are invalid in assembly mode: --optimize-yul. Optimization is disabled by default and can be enabled with --optimize. diff --git a/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/exit b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/input.yul b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/input.yul new file mode 100644 index 000000000..f21cd2b7e --- /dev/null +++ b/test/cmdlineTests/strict_asm_invalid_option_optimize_yul/input.yul @@ -0,0 +1,3 @@ +{ + sstore(0, 1) +} diff --git a/test/cmdlineTests/strict_asm_invalid_option_output_dir/err b/test/cmdlineTests/strict_asm_invalid_option_output_dir/err index a9b56e9c7..738a37b29 100644 --- a/test/cmdlineTests/strict_asm_invalid_option_output_dir/err +++ b/test/cmdlineTests/strict_asm_invalid_option_output_dir/err @@ -1 +1 @@ -The following options are invalid in assembly mode: --output-dir, --gas, --combined-json, --optimize-yul, --no-optimize-yul. Optimization is disabled by default and can be enabled with --optimize. +The following options are invalid in assembly mode: --output-dir. diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 4d1e9e23a..10083b827 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -221,6 +221,21 @@ BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE(int_with_gwei_ether_subdenomination) +{ + char const* sourceCode = R"( + contract test { + function test () { + uint x = 1 gwei; + } + } + )"; + bytes code = compileFirstExpression(sourceCode); + + bytes expectation({uint8_t(Instruction::PUSH4), 0x3b, 0x9a, 0xca, 0x00}); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index e75831ec4..48ddfac4d 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -355,6 +355,52 @@ BOOST_AUTO_TEST_CASE(private_state_variable) checkNatspec(sourceCode, "test", userDoc, true); } +BOOST_AUTO_TEST_CASE(event) +{ + char const* sourceCode = R"( + contract ERC20 { + /// @notice This event is emitted when a transfer occurs. + /// @param from The source account. + /// @param to The destination account. + /// @param amount The amount. + /// @dev A test case! + event Transfer(address indexed from, address indexed to, uint amount); + } + )"; + + char const* devDoc = R"ABCDEF( + { + "events": + { + "Transfer(address,address,uint256)": + { + "details": "A test case!", + "params": + { + "amount": "The amount.", "from": "The source account.", "to": "The destination account." + } + } + }, + "methods": {} + } + )ABCDEF"; + checkNatspec(sourceCode, "ERC20", devDoc, false); + + char const* userDoc = R"ABCDEF( + { + "events": + { + "Transfer(address,address,uint256)": + { + "notice": "This event is emitted when a transfer occurs." + } + }, + "methods": {} + } + )ABCDEF"; + checkNatspec(sourceCode, "ERC20", userDoc, true); +} + BOOST_AUTO_TEST_CASE(dev_desc_after_nl) { char const* sourceCode = R"( @@ -1218,6 +1264,263 @@ BOOST_AUTO_TEST_CASE(slash3_slash4) checkNatspec(sourceCode, "test", natspec, true); } -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(dev_default_inherit_variable) +{ + char const *sourceCode = R"( + contract C { + /// @notice Hello world + /// @dev test + function x() virtual external returns (uint) { + return 1; + } + } + + contract D is C { + uint public override x; + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "x()": + { + "details": "test" + } + } + })ABCDEF"; + + char const *natspec1 = R"ABCDEF({ + "methods" : {}, + "stateVariables" : + { + "x" : + { + "details" : "test" + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "C", natspec, false); + checkNatspec(sourceCode, "D", natspec1, false); +} + +BOOST_AUTO_TEST_CASE(user_default_inherit_variable) +{ + char const *sourceCode = R"( + contract C { + /// @notice Hello world + /// @dev test + function x() virtual external returns (uint) { + return 1; + } + } + + contract D is C { + uint public override x; + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "x()": + { + "notice": "Hello world" + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "C", natspec, true); + checkNatspec(sourceCode, "D", natspec, true); +} + +BOOST_AUTO_TEST_CASE(dev_default_inherit) +{ + char const *sourceCode = R"( + interface ERC20 { + /// Transfer ``amount`` from ``msg.sender`` to ``to``. + /// Second line. + /// @author Programmer + /// @dev test + /// @param to address to transfer to + /// @param amount amount to transfer + function transfer(address to, uint amount) external returns (bool); + } + + contract Middle is ERC20 { + function transfer(address to, uint amount) virtual override external returns (bool) + { + return false; + } + } + + contract Token is Middle { + function transfer(address to, uint amount) override external returns (bool) + { + return false; + } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "transfer(address,uint256)": + { + "author": "Programmer", + "details": "test", + "params": + { + "amount": "amount to transfer", + "to": "address to transfer to" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "ERC20", natspec, false); + checkNatspec(sourceCode, "Middle", natspec, false); + checkNatspec(sourceCode, "Token", natspec, false); +} + +BOOST_AUTO_TEST_CASE(user_default_inherit) +{ + char const *sourceCode = R"( + interface ERC20 { + /// Transfer ``amount`` from ``msg.sender`` to ``to``. + /// Second line. + /// @author Programmer + /// @dev test + /// @param to address to transfer to + /// @param amount amount to transfer + function transfer(address to, uint amount) external returns (bool); + } + + contract Middle is ERC20 { + function transfer(address to, uint amount) virtual override external returns (bool) + { + return false; + } + } + + contract Token is Middle { + function transfer(address to, uint amount) override external returns (bool) + { + return false; + } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "transfer(address,uint256)": + { + "notice": "Transfer ``amount`` from ``msg.sender`` to ``to``. Second line." + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "ERC20", natspec, true); + checkNatspec(sourceCode, "Middle", natspec, true); + checkNatspec(sourceCode, "Token", natspec, true); +} + +BOOST_AUTO_TEST_CASE(dev_inherit_parameter_mismatch) +{ + char const *sourceCode = R"( + interface ERC20 { + /// Transfer ``amount`` from ``msg.sender`` to ``to``. + /// @author Programmer + /// @dev test + /// @param to address to transfer to + /// @param amount amount to transfer + function transfer(address to, uint amount) external returns (bool); + } + + contract Middle is ERC20 { + function transfer(address to, uint amount) override virtual external returns (bool) { + return false; + } + } + + contract Token is Middle { + function transfer(address too, uint amount) override external returns (bool) { + return false; + } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "transfer(address,uint256)": + { + "author": "Programmer", + "details": "test", + "params": + { + "amount": "amount to transfer", + "to": "address to transfer to" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": { } + })ABCDEF"; + + checkNatspec(sourceCode, "ERC20", natspec, false); + checkNatspec(sourceCode, "Middle", natspec, false); + checkNatspec(sourceCode, "Token", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(user_inherit_parameter_mismatch) +{ + char const *sourceCode = R"( + interface ERC20 { + /// Transfer ``amount`` from ``msg.sender`` to ``to``. + /// @author Programmer + /// @dev test + /// @param to address to transfer to + /// @param amount amount to transfer + function transfer(address to, uint amount) external returns (bool); + } + + contract Middle is ERC20 { + function transfer(address to, uint amount) override virtual external returns (bool) { + return false; + } + } + + contract Token is Middle { + function transfer(address too, uint amount) override external returns (bool) { + return false; + } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "transfer(address,uint256)": + { + "notice": "Transfer ``amount`` from ``msg.sender`` to ``to``." + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": { } + })ABCDEF"; + + checkNatspec(sourceCode, "ERC20", natspec, true); + checkNatspec(sourceCode, "Middle", natspec, true); + checkNatspec(sourceCode, "Token", natspec2, true); +} } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index dc628674a..b02a910a5 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -428,8 +428,9 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_AUTO_TEST_CASE(ether_subdenominations) { - Scanner scanner(CharStream("wei szabo finney ether", "")); + Scanner scanner(CharStream("wei gwei szabo finney ether", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubWei); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::SubSzabo); BOOST_CHECK_EQUAL(scanner.next(), Token::SubFinney); BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther); diff --git a/test/libsolidity/semanticTests/literals/gwei.sol b/test/libsolidity/semanticTests/literals/gwei.sol new file mode 100644 index 000000000..7762021dc --- /dev/null +++ b/test/libsolidity/semanticTests/literals/gwei.sol @@ -0,0 +1,10 @@ +contract C { + uint constant gwei = 1 gwei; + + function f() public view returns(uint) { return gwei; } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1000000000 + diff --git a/test/libsolidity/syntaxTests/denominations/denominations.sol b/test/libsolidity/syntaxTests/denominations/denominations.sol index 43049a144..a34c03de0 100644 --- a/test/libsolidity/syntaxTests/denominations/denominations.sol +++ b/test/libsolidity/syntaxTests/denominations/denominations.sol @@ -2,5 +2,6 @@ contract C { uint constant a = 1 wei + 2 szabo + 3 finney + 4 ether; uint constant b = 1 seconds + 2 minutes + 3 hours + 4 days + 5 weeks; uint constant c = 2 szabo / 1 seconds + 3 finney * 3 hours; + uint constant d = 2 gwei / 1 seconds + 3 finney * 3 hours; } // ---- diff --git a/test/libsolidity/syntaxTests/denominations/gwei.sol b/test/libsolidity/syntaxTests/denominations/gwei.sol new file mode 100644 index 000000000..5bc5edb3d --- /dev/null +++ b/test/libsolidity/syntaxTests/denominations/gwei.sol @@ -0,0 +1,3 @@ +contract C { + uint constant gwei = 1 gwei; +} diff --git a/test/libsolidity/syntaxTests/immutable/long_name.sol b/test/libsolidity/syntaxTests/immutable/long_name.sol new file mode 100644 index 000000000..914cc2ead --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/long_name.sol @@ -0,0 +1,4 @@ +contract C { + uint immutable long___name___that___definitely___exceeds___the___thirty___two___byte___limit = 0; +} +// ---- diff --git a/test/libsolidity/syntaxTests/natspec/docstring_author_function.sol b/test/libsolidity/syntaxTests/natspec/docstring_author_function.sol new file mode 100644 index 000000000..7b6ca862b --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/docstring_author_function.sol @@ -0,0 +1,6 @@ +contract C { + /// @author author + function iHaveAuthor() public pure {} +} +// ---- +// Warning 9843: (17-35): Documentation tag @author is only allowed on contract definitions. It will be disallowed in 0.7.0. diff --git a/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol b/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol index 395bacc3e..51ce8b4d5 100644 --- a/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol +++ b/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol @@ -1,15 +1,16 @@ -contract c { +contract C { function f() public { a = 1 wei; b = 2 szabo; c = 3 finney; - b = 4 ether; + d = 4 ether; + e = 5 gwei; } uint256 a; uint256 b; uint256 c; uint256 d; + uint256 e; } // ---- -// Warning 2519: (170-179): This declaration shadows an existing declaration. diff --git a/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul b/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul new file mode 100644 index 000000000..b7058289b --- /dev/null +++ b/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul @@ -0,0 +1,17 @@ +object "a" { + code { + setimmutable( + "long___name___that___definitely___exceeds___the___thirty___two___byte___limit", + 0x1234567890123456789012345678901234567890 + ) + } +} +// ---- +// Assembly: +// /* "source":152:194 */ +// 0x1234567890123456789012345678901234567890 +// /* "source":32:204 */ +// assignImmutable("0x85a5b1db611c82c46f5fa18e39ae218397536256c451e5de155a86de843a9ad6") +// Bytecode: 73123456789012345678901234567890123456789050 +// Opcodes: PUSH20 0x1234567890123456789012345678901234567890 POP +// SourceMappings: 152:42:0:-:0;32:172 diff --git a/test/libyul/objectCompiler/linkersymbol.yul b/test/libyul/objectCompiler/linkersymbol.yul new file mode 100644 index 000000000..65bf004ef --- /dev/null +++ b/test/libyul/objectCompiler/linkersymbol.yul @@ -0,0 +1,42 @@ +object "a" { + code { + let addr := linkersymbol("contract/test.sol:L") + mstore(128, shl(227, 0x18530aaf)) + let success := call(gas(), addr, 0, 128, 4, 128, 0) + } +} +// ---- +// Assembly: +// linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612") +// /* "source":109:119 */ +// 0x18530aaf +// /* "source":104:107 */ +// 0xe3 +// /* "source":100:120 */ +// shl +// /* "source":95:98 */ +// 0x80 +// /* "source":88:121 */ +// mstore +// /* "source":179:180 */ +// 0x00 +// /* "source":174:177 */ +// 0x80 +// /* "source":171:172 */ +// 0x04 +// /* "source":166:169 */ +// 0x80 +// /* "source":163:164 */ +// 0x00 +// /* "source":157:161 */ +// dup6 +// /* "source":150:155 */ +// gas +// /* "source":145:181 */ +// call +// /* "source":22:187 */ +// pop +// pop +// Bytecode: 7300000000000000000000000000000000000000006318530aaf60e31b60805260006080600460806000855af15050 +// Opcodes: PUSH20 0x0 PUSH4 0x18530AAF PUSH1 0xE3 SHL PUSH1 0x80 MSTORE PUSH1 0x0 PUSH1 0x80 PUSH1 0x4 PUSH1 0x80 PUSH1 0x0 DUP6 GAS CALL POP POP +// SourceMappings: :::-:0;109:10:0;104:3;100:20;95:3;88:33;179:1;174:3;171:1;166:3;163:1;157:4;150:5;145:36;22:165; diff --git a/test/libyul/yulInterpreterTests/self_balance.yul b/test/libyul/yulInterpreterTests/self_balance.yul new file mode 100644 index 000000000..99b1f960c --- /dev/null +++ b/test/libyul/yulInterpreterTests/self_balance.yul @@ -0,0 +1,16 @@ +{ + mstore(0, balance(address())) + mstore(0x20, selfbalance()) + mstore(0x40, balance(div(mul(address(), 2), 2))) + mstore(0x60, balance(add(address(), 1))) +} +// ==== +// EVMVersion: >=istanbul +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000022223333 +// 20: 0000000000000000000000000000000000000000000000000000000022223333 +// 40: 0000000000000000000000000000000000000000000000000000000022223333 +// 60: 0000000000000000000000000000000000000000000000000000000022222222 +// Storage dump: diff --git a/test/libyul/yulSyntaxTests/byte_of_string_literal.yul b/test/libyul/yulSyntaxTests/byte_of_string_literal.yul new file mode 100644 index 000000000..9ed41172a --- /dev/null +++ b/test/libyul/yulSyntaxTests/byte_of_string_literal.yul @@ -0,0 +1,6 @@ +{ + let x := byte(31, "11112222333344445555666677778888") +} +// ==== +// dialect: evm +// ---- diff --git a/test/libyul/yulSyntaxTests/linkersymbol_evm.yul b/test/libyul/yulSyntaxTests/linkersymbol_evm.yul new file mode 100644 index 000000000..2bec7e49c --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_evm.yul @@ -0,0 +1,6 @@ +{ + let addr := linkersymbol("contract/library.sol:L") +} +// ==== +// dialect: evm +// ---- diff --git a/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul b/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul new file mode 100644 index 000000000..1cadcee0c --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul @@ -0,0 +1,6 @@ +{ + let addr:u256 := linkersymbol("contract/library.sol:L") +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/linkersymbol_ewasm.yul b/test/libyul/yulSyntaxTests/linkersymbol_ewasm.yul new file mode 100644 index 000000000..2b01045ce --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_ewasm.yul @@ -0,0 +1,7 @@ +{ + linkersymbol("contract/library.sol:L") +} +// ==== +// dialect: ewasm +// ---- +// DeclarationError 4619: (6-18): Function not found. diff --git a/test/libyul/yulSyntaxTests/linkersymbol_non_literal_args.yul b/test/libyul/yulSyntaxTests/linkersymbol_non_literal_args.yul new file mode 100644 index 000000000..e8b860a01 --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_non_literal_args.yul @@ -0,0 +1,8 @@ +{ + let library_name := "contract/library.sol:L" + let addr := linkersymbol(library_name) +} +// ==== +// dialect: evm +// ---- +// TypeError 9114: (67-79): Function expects direct literals as arguments. diff --git a/test/libyul/yulSyntaxTests/loadimmutable.yul b/test/libyul/yulSyntaxTests/loadimmutable.yul new file mode 100644 index 000000000..6aea058b5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/loadimmutable.yul @@ -0,0 +1,6 @@ +{ + let addr := loadimmutable("address") +} +// ==== +// dialect: evm +// ---- diff --git a/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul b/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul new file mode 100644 index 000000000..4884fc265 --- /dev/null +++ b/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul @@ -0,0 +1,7 @@ +{ + setimmutable(loadimmutable("abc"), "abc") +} +// ==== +// dialect: evm +// ---- +// TypeError 9114: (6-18): Function expects direct literals as arguments. diff --git a/test/libyul/yulSyntaxTests/setimmutable.yul b/test/libyul/yulSyntaxTests/setimmutable.yul new file mode 100644 index 000000000..3c37c90ff --- /dev/null +++ b/test/libyul/yulSyntaxTests/setimmutable.yul @@ -0,0 +1,6 @@ +{ + setimmutable("address", 0x1234567890123456789012345678901234567890) +} +// ==== +// dialect: evm +// ---- diff --git a/test/libyul/yulSyntaxTests/string_literal_too_long.yul b/test/libyul/yulSyntaxTests/string_literal_too_long.yul new file mode 100644 index 000000000..2ae5be44a --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_too_long.yul @@ -0,0 +1,7 @@ +{ + let name := "long___name___that___definitely___exceeds___the___thirty___two___byte___limit" +} +// ==== +// dialect: evm +// ---- +// TypeError 3069: (18-97): String literal too long (77 > 32) diff --git a/test/libyul/yulSyntaxTests/string_literal_too_long_byte.yul b/test/libyul/yulSyntaxTests/string_literal_too_long_byte.yul new file mode 100644 index 000000000..0ae6d3beb --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_too_long_byte.yul @@ -0,0 +1,7 @@ +{ + let x := byte(40, "long___value__that___definitely___exceeds___the___thirty___two___byte___limit") +} +// ==== +// dialect: evm +// ---- +// TypeError 3069: (24-103): String literal too long (77 > 32) diff --git a/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul b/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul new file mode 100644 index 000000000..37ce835df --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul @@ -0,0 +1,9 @@ +{ + setimmutable( + "long___name___that___definitely___exceeds___the___thirty___two___byte___limit", + 0x1234567890123456789012345678901234567890 + ) +} +// ==== +// dialect: evm +// ---- diff --git a/test/libyul/yulSyntaxTests/string_literal_too_long_linkersymbol.yul b/test/libyul/yulSyntaxTests/string_literal_too_long_linkersymbol.yul new file mode 100644 index 000000000..cf8ef95e1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_too_long_linkersymbol.yul @@ -0,0 +1,6 @@ +{ + let addr := linkersymbol("contract/long___name___that___definitely___exceeds___the___thirty___two___byte___limit.sol:L") +} +// ==== +// dialect: evm +// ---- diff --git a/test/tools/ossfuzz/config/solidity.dict b/test/tools/ossfuzz/config/solidity.dict index 8190e7c55..f788d49c2 100644 --- a/test/tools/ossfuzz/config/solidity.dict +++ b/test/tools/ossfuzz/config/solidity.dict @@ -8,6 +8,7 @@ " ether " " finney " " gasleft() " +" gwei " " hours " " minutes " " msg.data " diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 9c46ea856..c17fa33e8 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -182,7 +182,10 @@ u256 EVMInstructionInterpreter::eval( case Instruction::ADDRESS: return m_state.address; case Instruction::BALANCE: - return m_state.balance; + if (arg[0] == m_state.address) + return m_state.selfbalance; + else + return m_state.balance; case Instruction::SELFBALANCE: return m_state.selfbalance; case Instruction::ORIGIN: