diff --git a/.circleci/config.yml b/.circleci/config.yml index 15f122e48..a34649d74 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -531,7 +531,7 @@ jobs: xcode: "11.0.0" environment: TERM: xterm - CMAKE_BUILD_TYPE: Debug + CMAKE_BUILD_TYPE: Release steps: - checkout - restore_cache: diff --git a/Changelog.md b/Changelog.md index f20a4a34c..7e9e3c3be 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,7 +30,9 @@ Compiler Features: Bugfixes: * Optimizer: Fixed a bug in BlockDeDuplicator. * Type Checker: Disallow assignments to storage variables of type ``mapping``. - + * NatSpec: DocString block is terminated when encountering an empty line. + * Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL. + * Code Generator: Trigger proper unimplemented errors on certain array copy operations. ### 0.6.8 (2020-05-14) diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index 99e969e07..a1ee96d95 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -166,7 +166,7 @@ This section gives detailed instructions on how to update prior code for every b documentation so long as the notices are in the order they appear in the tuple return type. * Choose unique identifiers for variable declarations in inline assembly that do not conflict - with declartions outside the inline assembly block. + with declarations outside the inline assembly block. * Add ``virtual`` to every non-interface function you intend to override. Add ``virtual`` to all functions without implementation outside interfaces. For single inheritance, add diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index fa6ea4f16..296a330ab 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -435,10 +435,10 @@ map Assembly::optimiseInternal( // This only modifies PushTags, we have to run again to actually remove code. if (_settings.runDeduplicate) { - BlockDeduplicator dedup{m_items}; - if (dedup.deduplicate()) + BlockDeduplicator deduplicator{m_items}; + if (deduplicator.deduplicate()) { - for (auto const& replacement: dedup.replacedTags()) + for (auto const& replacement: deduplicator.replacedTags()) { assertThrow( replacement.first <= size_t(-1) && replacement.second <= size_t(-1), diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 9d748c570..6cf319067 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -267,10 +267,13 @@ bool Scanner::skipWhitespace() return sourcePos() != startPosition; } -void Scanner::skipWhitespaceExceptUnicodeLinebreak() +bool Scanner::skipWhitespaceExceptUnicodeLinebreak() { + int const startPosition = sourcePos(); while (isWhiteSpace(m_char) && !isUnicodeLinebreak()) advance(); + // Return whether or not we skipped any characters. + return sourcePos() != startPosition; } Token Scanner::skipSingleLineComment() @@ -321,7 +324,7 @@ int Scanner::scanSingleLineDocComment() { // Check if next line is also a single-line comment. // If any whitespaces were skipped, use source position before. - if (!skipWhitespace()) + if (!skipWhitespaceExceptUnicodeLinebreak()) endPosition = m_source->position(); if (!m_source->isPastEndOfInput(3) && @@ -329,8 +332,10 @@ int Scanner::scanSingleLineDocComment() m_source->get(1) == '/' && m_source->get(2) == '/') { - addCommentLiteralChar('\n'); m_char = m_source->advanceAndGet(3); + if (atEndOfLine()) + continue; + addCommentLiteralChar('\n'); } else break; // next line is not a documentation comment, we are done @@ -389,9 +394,11 @@ Token Scanner::scanMultiLineDocComment() } else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) != '/') { // skip first '*' in subsequent lines + m_char = m_source->advanceAndGet(1); + if (atEndOfLine()) // ignores empty lines + continue; if (charsAdded) - addCommentLiteralChar('\n'); - m_char = m_source->advanceAndGet(2); + addCommentLiteralChar('\n'); // corresponds to the end of previous line } else if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '/') { // if after newline the comment ends, don't insert the newline diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 5b365f134..d54bb7532 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -214,7 +214,7 @@ private: /// Skips all whitespace and @returns true if something was skipped. bool skipWhitespace(); /// Skips all whitespace that are neither '\r' nor '\n'. - void skipWhitespaceExceptUnicodeLinebreak(); + bool skipWhitespaceExceptUnicodeLinebreak(); Token skipSingleLineComment(); Token skipMultiLineComment(); diff --git a/liblangutil/SourceReferenceFormatterHuman.cpp b/liblangutil/SourceReferenceFormatterHuman.cpp index ca947314e..cbf24cf35 100644 --- a/liblangutil/SourceReferenceFormatterHuman.cpp +++ b/liblangutil/SourceReferenceFormatterHuman.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include using namespace std; @@ -103,12 +104,13 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _ m_stream << leftpad << ' '; frameColored() << '|'; m_stream << ' '; + for_each( _ref.text.cbegin(), - _ref.text.cbegin() + _ref.startColumn, + _ref.text.cbegin() + numCodepoints(_ref.text.substr(0, _ref.startColumn)), [this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); } ); - diagColored() << string(locationLength, '^'); + diagColored() << string(numCodepoints(_ref.text.substr(_ref.startColumn, locationLength)), '^'); m_stream << '\n'; } else diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index a06679b97..ca565b53a 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -50,7 +50,8 @@ bool DeclarationTypeChecker::visit(ElementaryTypeName const& _typeName) _typeName.annotation().type = TypeProvider::address(); break; default: - typeError( + m_errorReporter.typeError( + 2311_error, _typeName.location(), "Address types can only be payable or non-payable." ); @@ -102,7 +103,11 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct) auto visitor = [&](StructDefinition const& _struct, auto& _cycleDetector, size_t _depth) { if (_depth >= 256) - fatalDeclarationError(_struct.location(), "Struct definition exhausts cyclic dependency validator."); + m_errorReporter.fatalDeclarationError( + 5651_error, + _struct.location(), + "Struct definition exhausts cyclic dependency validator." + ); for (ASTPointer const& member: _struct.members()) { @@ -119,7 +124,7 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct) } }; if (util::CycleDetector(visitor).run(_struct) != nullptr) - fatalTypeError(_struct.location(), "Recursive struct definition."); + m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition."); return false; } @@ -145,9 +150,14 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName) else { _typeName.annotation().type = TypeProvider::emptyTuple(); - fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); + m_errorReporter.fatalTypeError( + 9755_error, + _typeName.location(), + "Name has to refer to a struct, enum or contract." + ); } } + bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName) { if (_typeName.annotation().type) @@ -165,18 +175,27 @@ bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName) case Visibility::External: break; default: - fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); + m_errorReporter.fatalTypeError( + 7653_error, + _typeName.location(), + "Invalid visibility, can only be \"external\" or \"internal\"." + ); return false; } if (_typeName.isPayable() && _typeName.visibility() != Visibility::External) { - fatalTypeError(_typeName.location(), "Only external function types can be payable."); + m_errorReporter.fatalTypeError( + 6138_error, + _typeName.location(), + "Only external function types can be payable." + ); return false; } _typeName.annotation().type = TypeProvider::function(_typeName); return false; } + void DeclarationTypeChecker::endVisit(Mapping const& _mapping) { if (_mapping.annotation().type) @@ -226,7 +245,11 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) return; } if (baseType->storageBytes() == 0) - fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array."); + m_errorReporter.fatalTypeError( + 9390_error, + _typeName.baseType().location(), + "Illegal base type of storage size zero for array." + ); if (Expression const* length = _typeName.length()) { TypePointer& lengthTypeGeneric = length->annotation().type; @@ -235,13 +258,17 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) RationalNumberType const* lengthType = dynamic_cast(lengthTypeGeneric); u256 lengthValue = 0; if (!lengthType || !lengthType->mobileType()) - typeError(length->location(), "Invalid array length, expected integer literal or constant expression."); + m_errorReporter.typeError( + 8922_error, + length->location(), + "Invalid array length, expected integer literal or constant expression." + ); else if (lengthType->isZero()) - typeError(length->location(), "Array with zero length specified."); + m_errorReporter.typeError(1220_error, length->location(), "Array with zero length specified."); else if (lengthType->isFractional()) - typeError(length->location(), "Array with fractional length specified."); + m_errorReporter.typeError(4323_error, length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) - typeError(length->location(), "Array with negative length specified."); + m_errorReporter.typeError(9308_error, length->location(), "Array with negative length specified."); else lengthValue = lengthType->literalValue(nullptr); _typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue); @@ -249,15 +276,24 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) else _typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType); } + void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) { if (_variable.annotation().type) return; if (_variable.isConstant() && !_variable.isStateVariable()) - m_errorReporter.declarationError(1788_error, _variable.location(), "The \"constant\" keyword can only be used for state variables."); + m_errorReporter.declarationError( + 1788_error, + _variable.location(), + "The \"constant\" keyword can only be used for state variables." + ); if (_variable.immutable() && !_variable.isStateVariable()) - m_errorReporter.declarationError(8297_error, _variable.location(), "The \"immutable\" keyword can only be used for state variables."); + m_errorReporter.declarationError( + 8297_error, + _variable.location(), + "The \"immutable\" keyword can only be used for state variables." + ); if (!_variable.typeName()) { @@ -309,7 +345,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) errorString += " for variable"; } errorString += ", but " + locationToString(varLoc) + " was given."; - typeError(_variable.location(), errorString); + m_errorReporter.typeError(6160_error, _variable.location(), errorString); solAssert(!allowedDataLocations.empty(), ""); varLoc = *allowedDataLocations.begin(); @@ -359,24 +395,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) } -void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description) -{ - m_errorReporter.typeError(2311_error, _location, _description); -} - -void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description) -{ - m_errorReporter.fatalTypeError(5651_error, _location, _description); -} - -void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description) -{ - m_errorReporter.fatalDeclarationError(2046_error, _location, _description); -} - bool DeclarationTypeChecker::check(ASTNode const& _node) { - unsigned errorCount = m_errorReporter.errorCount(); + auto watcher = m_errorReporter.errorWatcher(); _node.accept(*this); - return m_errorReporter.errorCount() == errorCount; + return watcher.ok(); } diff --git a/libsolidity/analysis/DeclarationTypeChecker.h b/libsolidity/analysis/DeclarationTypeChecker.h index e54075998..764a66e1d 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.h +++ b/libsolidity/analysis/DeclarationTypeChecker.h @@ -59,15 +59,6 @@ private: void endVisit(VariableDeclaration const& _variable) override; bool visit(StructDefinition const& _struct) override; - /// Adds a new error to the list of errors. - void typeError(langutil::SourceLocation const& _location, std::string const& _description); - - /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description); - - /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description); - langutil::ErrorReporter& m_errorReporter; langutil::EVMVersion m_evmVersion; bool m_insideFunctionType = false; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index a04c9314e..7d8a8a43f 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -68,14 +68,16 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable) parseDocStrings(_variable, _variable.annotation(), validPublicTags, "non-public state variables"); if (_variable.annotation().docTags.count("notice") > 0) m_errorReporter.warning( - 9098_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." + 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( - 4822_error, _variable.documentation()->location(), - "Documentation tag @title and @author is only allowed on contract definitions. It will be disallowed in 0.7.0." + 8532_error, _variable.documentation()->location(), + "Documentation tag @title and @author is only allowed on contract definitions. " + "It will be disallowed in 0.7.0." ); } return false; @@ -110,7 +112,8 @@ void DocStringAnalyser::checkParameters( auto paramRange = _annotation.docTags.equal_range("param"); for (auto i = paramRange.first; i != paramRange.second; ++i) if (!validParams.count(i->second.paramName)) - appendError( + m_errorReporter.docstringParsingError( + 3881_error, _node.documentation()->location(), "Documented parameter \"" + i->second.paramName + @@ -159,7 +162,8 @@ void DocStringAnalyser::parseDocStrings( for (auto const& docTag: _annotation.docTags) { if (!_validTags.count(docTag.first)) - appendError( + m_errorReporter.docstringParsingError( + 6546_error, _node.documentation()->location(), "Documentation tag @" + docTag.first + " not valid for " + _nodeName + "." ); @@ -170,12 +174,14 @@ void DocStringAnalyser::parseDocStrings( if (auto* varDecl = dynamic_cast(&_node)) { if (!varDecl->isPublic()) - appendError( + m_errorReporter.docstringParsingError( + 9440_error, _node.documentation()->location(), "Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables." ); if (returnTagsVisited > 1) - appendError( + m_errorReporter.docstringParsingError( + 5256_error, _node.documentation()->location(), "Documentation tag \"@" + docTag.first + "\" is only allowed once on state-variables." ); @@ -186,7 +192,8 @@ void DocStringAnalyser::parseDocStrings( string firstWord = content.substr(0, content.find_first_of(" \t")); if (returnTagsVisited > function->returnParameters().size()) - appendError( + m_errorReporter.docstringParsingError( + 2604_error, _node.documentation()->location(), "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + " exceeds the number of return parameters." @@ -195,7 +202,8 @@ void DocStringAnalyser::parseDocStrings( { auto parameter = function->returnParameters().at(returnTagsVisited - 1); if (!parameter->name().empty() && parameter->name() != firstWord) - appendError( + m_errorReporter.docstringParsingError( + 5856_error, _node.documentation()->location(), "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + " does not contain the name of its return parameter." @@ -205,8 +213,3 @@ void DocStringAnalyser::parseDocStrings( } } } - -void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description) -{ - m_errorReporter.docstringParsingError(7816_error, _location, _description); -} diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index cddfc2f1d..9bc8ec578 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -81,8 +81,6 @@ private: std::string const& _nodeName ); - void appendError(langutil::SourceLocation const& _location, std::string const& _description); - langutil::ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index ac3553f6b..f92f8dc30 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -119,7 +119,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier) else errorMessage += " Did you mean " + std::move(suggestions) + "?"; } - declarationError(_identifier.location(), errorMessage); + m_errorReporter.declarationError(8051_error, _identifier.location(), errorMessage); } else if (declarations.size() == 1) _identifier.annotation().referencedDeclaration = declarations.front(); @@ -157,7 +157,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); if (!declaration) { - fatalDeclarationError(_typeName.location(), "Identifier not found or not unique."); + m_errorReporter.fatalDeclarationError(7556_error, _typeName.location(), "Identifier not found or not unique."); return; } @@ -209,14 +209,22 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) )); if (realName.empty()) { - declarationError(_identifier.location, "In variable names _slot and _offset can only be used as a suffix."); + m_errorReporter.declarationError( + 9553_error, + _identifier.location, + "In variable names _slot and _offset can only be used as a suffix." + ); return; } declarations = m_resolver.nameFromCurrentScope(realName); } if (declarations.size() > 1) { - declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported."); + m_errorReporter.declarationError( + 8827_error, + _identifier.location, + "Multiple matching identifiers. Resolving overloaded identifiers is not supported." + ); return; } else if (declarations.size() == 0) @@ -224,7 +232,11 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) if (auto var = dynamic_cast(declarations.front())) if (var->isLocalVariable() && m_yulInsideFunction) { - declarationError(_identifier.location, "Cannot access local Solidity variables from inside an inline assembly function."); + m_errorReporter.declarationError( + 8477_error, + _identifier.location, + "Cannot access local Solidity variables from inside an inline assembly function." + ); return; } @@ -242,7 +254,11 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.')); if (isSlot || isOffset) - declarationError(identifier.location, "In variable declarations _slot and _offset can not be used as a suffix."); + m_errorReporter.declarationError( + 8820_error, + identifier.location, + "In variable declarations _slot and _offset can not be used as a suffix." + ); else if ( auto declarations = m_resolver.nameFromCurrentScope(namePrefix); !declarations.empty() @@ -252,7 +268,8 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) for (auto const* decl: declarations) ssl.append("The shadowed declaration is here:", decl->location()); if (!ssl.infos.empty()) - declarationError( + m_errorReporter.declarationError( + 6005_error, identifier.location, ssl, namePrefix.size() < identifier.name.str().size() ? @@ -266,19 +283,4 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) visit(*_varDecl.value); } -void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description) -{ - m_errorReporter.declarationError(8532_error, _location, _description); -} - -void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description) -{ - m_errorReporter.declarationError(3881_error, _location, _ssl, _description); -} - -void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description) -{ - m_errorReporter.fatalDeclarationError(6546_error, _location, _description); -} - } diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 7b8c1fff5..7da745cc5 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -88,15 +88,6 @@ private: void operator()(yul::Identifier const& _identifier) override; void operator()(yul::VariableDeclaration const& _varDecl) override; - /// Adds a new error to the list of errors. - void declarationError(langutil::SourceLocation const& _location, std::string const& _description); - - /// Adds a new error to the list of errors. - void declarationError(langutil::SourceLocation const& _location, langutil::SecondarySourceLocation const& _ssl, std::string const& _description); - - /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description); - langutil::ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; langutil::EVMVersion m_evmVersion; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index eb8f7a263..507559ca6 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -250,7 +250,7 @@ struct ExpressionAnnotation: ASTAnnotation bool lValueOfOrdinaryAssignment = false; /// Types and - if given - names of arguments if the expr. is a function - /// that is called, used for overload resoultion + /// that is called, used for overload resolution std::optional arguments; }; diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 089d0f5cf..732deca6f 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -185,6 +185,13 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons { solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); + + solUnimplementedAssert( + _sourceType.location() != DataLocation::CallData || + !_sourceType.isDynamicallyEncoded() || + !sourceBaseArrayType.isDynamicallySized(), + "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator." + ); _context << Instruction::DUP3; if (sourceBaseArrayType.location() == DataLocation::Memory) _context << Instruction::MLOAD; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 8bf0a21e2..25c91f0a3 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -974,6 +974,14 @@ void CompilerUtils::convertType( } else { + if (auto baseType = dynamic_cast(typeOnStack.baseType())) + solUnimplementedAssert( + typeOnStack.location() != DataLocation::CallData || + !typeOnStack.isDynamicallyEncoded() || + !baseType->isDynamicallySized(), + "Copying nested dynamic calldata arrays to memory is not implemented in the old code generator." + ); + m_context << u256(0) << Instruction::SWAP1; // stack: (variably sized) auto repeat = m_context.newTag(); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 3d5d85825..a3c266bde 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -286,7 +286,7 @@ public: /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type. void computeHashStatic(); - /// Apppends code that copies the code of the given contract to memory. + /// Appends code that copies the code of the given contract to memory. /// Stack pre: Memory position /// Stack post: Updated memory position /// @param creation if true, copies creation code, if false copies runtime code. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f05ffda8f..6397c7492 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1912,7 +1912,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) // If the identifier is called right away, this code is executed in visit(FunctionCall...), because // we want to avoid having a reference to the runtime function entry point in the // constructor context, since this would force the compiler to include unreferenced - // internal functions in the runtime contex. + // internal functions in the runtime context. utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract())); else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index 1f1cc1cfe..2bf4a36cf 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -99,3 +99,13 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName) { return "zero_value_for_type_" + _type.identifier() + _variableName; } + +FunctionDefinition const* IRHelpers::referencedFunctionDeclaration(Expression const& _expression) +{ + if (auto memberAccess = dynamic_cast(&_expression)) + return dynamic_cast(memberAccess->annotation().referencedDeclaration); + else if (auto identifier = dynamic_cast(&_expression)) + return dynamic_cast(identifier->annotation().referencedDeclaration); + else + return nullptr; +} diff --git a/libsolidity/codegen/ir/Common.h b/libsolidity/codegen/ir/Common.h index f1235553b..3f4ea64f4 100644 --- a/libsolidity/codegen/ir/Common.h +++ b/libsolidity/codegen/ir/Common.h @@ -62,6 +62,11 @@ struct IRNames static std::string zeroValue(Type const& _type, std::string const& _variableName); }; +struct IRHelpers +{ + static FunctionDefinition const* referencedFunctionDeclaration(Expression const& _expression); +}; + } // Overloading std::less() makes it possible to use YulArity as a map key. We could define operator< diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index b91ed9f2b..5472ec7ed 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -625,15 +625,22 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]); } - if (auto memberAccess = dynamic_cast(&_functionCall.expression())) + auto memberAccess = dynamic_cast(&_functionCall.expression()); + if (memberAccess) + { if (auto expressionType = dynamic_cast(memberAccess->expression().annotation().type)) + { + solAssert(!functionType->bound(), ""); if (auto contractType = dynamic_cast(expressionType->actualType())) solUnimplementedAssert( !contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal, "Only internal function calls implemented for libraries" ); + } + } + else + solAssert(!functionType->bound(), ""); - solUnimplementedAssert(!functionType->bound(), ""); switch (functionType->kind()) { case FunctionType::Kind::Declaration: @@ -641,53 +648,40 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) break; case FunctionType::Kind::Internal: { - optional functionDef; - if (auto memberAccess = dynamic_cast(&_functionCall.expression())) - { - solUnimplementedAssert(!functionType->bound(), "Internal calls to bound functions are not yet implemented for libraries and not allowed for contracts"); + auto identifier = dynamic_cast(&_functionCall.expression()); + FunctionDefinition const* functionDef = IRHelpers::referencedFunctionDeclaration(_functionCall.expression()); - functionDef = dynamic_cast(memberAccess->annotation().referencedDeclaration); - if (functionDef.value() != nullptr) - solAssert(functionType->declaration() == *memberAccess->annotation().referencedDeclaration, ""); - else - { - solAssert(dynamic_cast(memberAccess->annotation().referencedDeclaration), ""); - solAssert(!functionType->hasDeclaration(), ""); - } - } - else if (auto identifier = dynamic_cast(&_functionCall.expression())) + if (functionDef) { - solAssert(!functionType->bound(), ""); + solAssert(memberAccess || identifier, ""); + solAssert(functionType->declaration() == *functionDef, ""); - if (auto unresolvedFunctionDef = dynamic_cast(identifier->annotation().referencedDeclaration)) - { - functionDef = &unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract()); - solAssert(functionType->declaration() == *identifier->annotation().referencedDeclaration, ""); - } - else - { - functionDef = nullptr; - solAssert(dynamic_cast(identifier->annotation().referencedDeclaration), ""); - solAssert(!functionType->hasDeclaration(), ""); - } + if (identifier) + functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); + + solAssert(functionDef->isImplemented(), ""); } else - // Not a simple expression like x or A.x - functionDef = nullptr; + solAssert(!functionType->hasDeclaration(), ""); - solAssert(functionDef.has_value(), ""); - solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), ""); + solAssert(!functionType->takesArbitraryParameters(), ""); vector args; - for (size_t i = 0; i < arguments.size(); ++i) - if (functionType->takesArbitraryParameters()) - args += IRVariable(*arguments[i]).stackSlots(); - else - args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); + if (functionType->bound()) + { + solAssert(memberAccess && functionDef, ""); + solAssert(functionDef->parameters().size() == arguments.size() + 1, ""); + args += convert(memberAccess->expression(), *functionDef->parameters()[0]->type()).stackSlots(); + } + else + solAssert(!functionDef || functionDef->parameters().size() == arguments.size(), ""); - if (functionDef.value() != nullptr) + for (size_t i = 0; i < arguments.size(); ++i) + args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); + + if (functionDef) define(_functionCall) << - m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) << + m_context.enqueueFunctionForCodeGeneration(*functionDef) << "(" << joinHumanReadable(args) << ")\n"; @@ -1237,13 +1231,27 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { ASTString const& member = _memberAccess.memberName(); - if (auto funType = dynamic_cast(_memberAccess.annotation().type)) - if (funType->bound()) - { - solUnimplementedAssert(false, ""); - } + auto memberFunctionType = dynamic_cast(_memberAccess.annotation().type); + Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); - switch (_memberAccess.expression().annotation().type->category()) + if (memberFunctionType && memberFunctionType->bound()) + { + solAssert((set{ + Type::Category::Contract, + Type::Category::Bool, + Type::Category::Integer, + Type::Category::Address, + Type::Category::Function, + Type::Category::Struct, + Type::Category::Enum, + Type::Category::Mapping, + Type::Category::Array, + Type::Category::FixedBytes, + }).count(objectCategory) > 0, ""); + return; + } + + switch (objectCategory) { case Type::Category::Contract: { @@ -1476,9 +1484,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { if (auto const* variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) handleVariableReference(*variable, _memberAccess); - else if (auto const* funType = dynamic_cast(_memberAccess.annotation().type)) + else if (memberFunctionType) { - switch (funType->kind()) + switch (memberFunctionType->kind()) { case FunctionType::Kind::Declaration: break; @@ -1497,7 +1505,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) break; case FunctionType::Kind::DelegateCall: define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); - define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(funType->externalIdentifier()) << "\n"; + define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n"; break; case FunctionType::Kind::External: case FunctionType::Kind::Creation: diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index feda8fc53..e04b0b2f1 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -178,7 +178,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _art } /// -/// @a _outputSelection is a JSON object containining a two-level hashmap, where the first level is the filename, +/// @a _outputSelection is a JSON object containing a two-level hashmap, where the first level is the filename, /// the second level is the contract name and the value is an array of artifact names to be requested for that contract. /// @a _file is the current file /// @a _contract is the current contract @@ -229,7 +229,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection) if (!_outputSelection.isObject()) return false; - // This does not inculde "evm.methodIdentifiers" on purpose! + // This does not include "evm.methodIdentifiers" on purpose! static vector const outputsThatRequireBinaries{ "*", "ir", "irOptimized", diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 3ce67a430..33cdbd937 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -96,7 +96,10 @@ void DocStringParser::parse(string const& _docString, ErrorReporter& _errorRepor auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end); if (tagNameEndPos == end) { - appendError("End of tag " + string(tagPos, tagNameEndPos) + " not found"); + m_errorReporter->docstringParsingError( + 9222_error, + "End of tag " + string(tagPos, tagNameEndPos) + " not found" + ); break; } @@ -138,7 +141,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) auto nameStartPos = skipWhitespace(_pos, _end); if (nameStartPos == _end) { - appendError("No param name given"); + m_errorReporter->docstringParsingError(3335_error, "No param name given"); return _end; } auto nameEndPos = firstNonIdentifier(nameStartPos, _end); @@ -149,7 +152,7 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) if (descStartPos == nlPos) { - appendError("No description given for param " + paramName); + m_errorReporter->docstringParsingError(9942_error, "No description given for param " + paramName); return _end; } @@ -189,8 +192,3 @@ void DocStringParser::newTag(string const& _tagName) { m_lastTag = &m_docTags.insert(make_pair(_tagName, DocTag()))->second; } - -void DocStringParser::appendError(string const& _description) -{ - m_errorReporter->docstringParsingError(9440_error, _description); -} diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h index b4c9186a7..eaf18503b 100644 --- a/libsolidity/parsing/DocStringParser.h +++ b/libsolidity/parsing/DocStringParser.h @@ -58,8 +58,6 @@ private: /// Creates and inserts a new tag and adjusts m_lastTag. void newTag(std::string const& _tagName); - void appendError(std::string const& _description); - /// Mapping tag name -> content. std::multimap m_docTags; DocTag* m_lastTag = nullptr; diff --git a/libsolutil/UTF8.cpp b/libsolutil/UTF8.cpp index a7d55af6c..82c87bb44 100644 --- a/libsolutil/UTF8.cpp +++ b/libsolutil/UTF8.cpp @@ -138,4 +138,13 @@ bool validateUTF8(std::string const& _input, size_t& _invalidPosition) return validateUTF8(reinterpret_cast(_input.c_str()), _input.length(), _invalidPosition); } +size_t numCodepoints(std::string const& _utf8EncodedInput) +{ + size_t codepoint = 0; + for (char c: _utf8EncodedInput) + codepoint += (c & 0xc0) != 0x80; + + return codepoint; +} + } diff --git a/libsolutil/UTF8.h b/libsolutil/UTF8.h index cd84c3982..fb5ee376c 100644 --- a/libsolutil/UTF8.h +++ b/libsolutil/UTF8.h @@ -38,4 +38,6 @@ inline bool validateUTF8(std::string const& _input) return validateUTF8(_input, invalidPos); } +size_t numCodepoints(std::string const& _utf8EncodedInput); + } diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 249d87a33..43eced734 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -382,15 +382,15 @@ bytes BinaryTransform::operator()(Loop const& _loop) return result; } -bytes BinaryTransform::operator()(Break const& _break) +bytes BinaryTransform::operator()(Branch const& _branch) { - return toBytes(Opcode::Br) + encodeLabelIdx(_break.label.name); + return toBytes(Opcode::Br) + encodeLabelIdx(_branch.label.name); } -bytes BinaryTransform::operator()(BreakIf const& _breakIf) +bytes BinaryTransform::operator()(BranchIf const& _branchIf) { - bytes result = std::visit(*this, *_breakIf.condition); - result += toBytes(Opcode::BrIf) + encodeLabelIdx(_breakIf.label.name); + bytes result = std::visit(*this, *_branchIf.condition); + result += toBytes(Opcode::BrIf) + encodeLabelIdx(_branchIf.label.name); return result; } diff --git a/libyul/backends/wasm/BinaryTransform.h b/libyul/backends/wasm/BinaryTransform.h index e11c92bf6..c1c924dcc 100644 --- a/libyul/backends/wasm/BinaryTransform.h +++ b/libyul/backends/wasm/BinaryTransform.h @@ -48,8 +48,8 @@ public: bytes operator()(wasm::GlobalAssignment const& _assignment); bytes operator()(wasm::If const& _if); bytes operator()(wasm::Loop const& _loop); - bytes operator()(wasm::Break const& _break); - bytes operator()(wasm::BreakIf const& _break); + bytes operator()(wasm::Branch const& _branch); + bytes operator()(wasm::BranchIf const& _branchIf); bytes operator()(wasm::Return const& _return); bytes operator()(wasm::Block const& _block); bytes operator()(wasm::FunctionDefinition const& _function); diff --git a/libyul/backends/wasm/TextTransform.cpp b/libyul/backends/wasm/TextTransform.cpp index 2a2499b61..fd8eaf746 100644 --- a/libyul/backends/wasm/TextTransform.cpp +++ b/libyul/backends/wasm/TextTransform.cpp @@ -121,14 +121,14 @@ string TextTransform::operator()(wasm::Loop const& _loop) return "(loop" + move(label) + "\n" + indented(joinTransformed(_loop.statements, '\n')) + ")\n"; } -string TextTransform::operator()(wasm::Break const& _break) +string TextTransform::operator()(wasm::Branch const& _branch) { - return "(br $" + _break.label.name + ")\n"; + return "(br $" + _branch.label.name + ")\n"; } -string TextTransform::operator()(wasm::BreakIf const& _break) +string TextTransform::operator()(wasm::BranchIf const& _branchIf) { - return "(br_if $" + _break.label.name + " " + visit(*_break.condition) + ")\n"; + return "(br_if $" + _branchIf.label.name + " " + visit(*_branchIf.condition) + ")\n"; } string TextTransform::operator()(wasm::Return const&) diff --git a/libyul/backends/wasm/TextTransform.h b/libyul/backends/wasm/TextTransform.h index 10f9f3fbb..ebbca4e9e 100644 --- a/libyul/backends/wasm/TextTransform.h +++ b/libyul/backends/wasm/TextTransform.h @@ -48,9 +48,9 @@ public: std::string operator()(wasm::GlobalAssignment const& _assignment); std::string operator()(wasm::If const& _if); std::string operator()(wasm::Loop const& _loop); - std::string operator()(wasm::Break const& _break); + std::string operator()(wasm::Branch const& _branch); + std::string operator()(wasm::BranchIf const& _branchIf); std::string operator()(wasm::Return const& _return); - std::string operator()(wasm::BreakIf const& _break); std::string operator()(wasm::Block const& _block); private: diff --git a/libyul/backends/wasm/WasmAST.h b/libyul/backends/wasm/WasmAST.h index 635b7c4ea..7ba7066cb 100644 --- a/libyul/backends/wasm/WasmAST.h +++ b/libyul/backends/wasm/WasmAST.h @@ -41,13 +41,13 @@ struct GlobalAssignment; struct Block; struct If; struct Loop; -struct Break; -struct BreakIf; +struct Branch; +struct BranchIf; struct Return; using Expression = std::variant< Literal, StringLiteral, LocalVariable, GlobalVariable, FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment, - Block, If, Loop, Break, BreakIf, Return + Block, If, Loop, Branch, BranchIf, Return >; struct Literal { uint64_t value; }; @@ -66,9 +66,9 @@ struct If { std::unique_ptr> elseStatements; }; struct Loop { std::string labelName; std::vector statements; }; -struct Break { Label label; }; +struct Branch { Label label; }; struct Return {}; -struct BreakIf { Label label; std::unique_ptr condition; }; +struct BranchIf { Label label; std::unique_ptr condition; }; struct VariableDeclaration { std::string variableName; }; struct GlobalVariableDeclaration { std::string variableName; }; diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index f2e624552..308171c44 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -256,26 +256,26 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) wasm::Loop loop; loop.labelName = newLabel(); loop.statements = visit(_for.pre.statements); - loop.statements.emplace_back(wasm::BreakIf{wasm::Label{breakLabel}, make_unique( + loop.statements.emplace_back(wasm::BranchIf{wasm::Label{breakLabel}, make_unique( wasm::BuiltinCall{"i64.eqz", make_vector( visitReturnByValue(*_for.condition) )} )}); loop.statements.emplace_back(wasm::Block{continueLabel, visit(_for.body.statements)}); loop.statements += visit(_for.post.statements); - loop.statements.emplace_back(wasm::Break{wasm::Label{loop.labelName}}); + loop.statements.emplace_back(wasm::Branch{wasm::Label{loop.labelName}}); return { wasm::Block{breakLabel, make_vector(move(loop))} }; } wasm::Expression WasmCodeTransform::operator()(Break const&) { - return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().first}}; + return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}}; } wasm::Expression WasmCodeTransform::operator()(Continue const&) { - return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}}; + return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}}; } wasm::Expression WasmCodeTransform::operator()(Leave const&) diff --git a/scripts/codespell_whitelist.txt b/scripts/codespell_whitelist.txt index 0409dc1a2..f1111719f 100644 --- a/scripts/codespell_whitelist.txt +++ b/scripts/codespell_whitelist.txt @@ -11,3 +11,4 @@ compilability errorstring hist otion +keypair diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 768f94801..0f2bc1d79 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -13,7 +13,7 @@ import hashlib from os.path import join, isfile def extract_test_cases(path): - lines = open(path, mode='r', encoding='utf8').read().splitlines() + lines = open(path, encoding="utf8", errors='ignore', mode='r').read().splitlines() inside = False delimiter = '' diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 6d50e0066..267346e41 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -6,7 +6,7 @@ ## You can pass a branch name as argument to this script (which, if no argument is given, ## will default to "develop"). ## -## If the gien branch is "release", the resulting package will be uplaoded to +## If the given branch is "release", the resulting package will be uploaded to ## ethereum/ethereum PPA, or ethereum/ethereum-dev PPA otherwise. ## ## The gnupg key for "builds@ethereum.org" has to be present in order to sign diff --git a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py index eab461bff..0a7571891 100755 --- a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py +++ b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py @@ -8,7 +8,7 @@ from os.path import join, isfile def extract_test_cases(path): - lines = open(path, 'rb').read().splitlines() + lines = open(path, encoding="utf8", errors='ignore', mode='rb').read().splitlines() inside = False delimiter = '' diff --git a/test/cmdlineTests/message_format_utf16/err b/test/cmdlineTests/message_format_utf16/err new file mode 100644 index 000000000..abe92d2e5 --- /dev/null +++ b/test/cmdlineTests/message_format_utf16/err @@ -0,0 +1,11 @@ +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +--> message_format_utf16/input.sol + +Warning: Source file does not specify required compiler version! +--> message_format_utf16/input.sol + +Warning: Statement has no effect. + --> message_format_utf16/input.sol:2:58: + | +2 | /* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; } + | ^^^^^^^^^^^^ diff --git a/test/cmdlineTests/message_format_utf16/input.sol b/test/cmdlineTests/message_format_utf16/input.sol new file mode 100644 index 000000000..2c1f9007a --- /dev/null +++ b/test/cmdlineTests/message_format_utf16/input.sol @@ -0,0 +1,3 @@ +contract Foo { +/* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; } +} diff --git a/test/compilationTests/corion/ico.sol b/test/compilationTests/corion/ico.sol index 26858e9d6..a8804fd4c 100644 --- a/test/compilationTests/corion/ico.sol +++ b/test/compilationTests/corion/ico.sol @@ -193,7 +193,7 @@ contract ico is safeMath { function setICOEthPrice(uint256 value) external { /* - Setting of the ICO ETC USD rates which can only be calle by a pre-defined address. + Setting of the ICO ETC USD rates which can only be called by a pre-defined address. After this function is completed till the call of the next function (which is at least an exchangeRateDelay array) this rate counts. With this process avoiding the sudden rate changes. @@ -221,8 +221,8 @@ contract ico is safeMath { /* Closing the ICO. It is only possible when the ICO period passed and only by the owner. - The 96% of the whole amount of the token is generated to the address of the fundation. - Ethers which are situated in this contract will be sent to the address of the fundation. + The 96% of the whole amount of the token is generated to the address of the foundation. + Ethers which are situated in this contract will be sent to the address of the foundation. */ require( msg.sender == owner ); require( block.number > icoDelay ); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 782b687fc..536eaa951 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -826,8 +826,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator) Instruction::JUMP, AssemblyItem(Tag, 3) }; - BlockDeduplicator dedup(input); - dedup.deduplicate(); + BlockDeduplicator deduplicator(input); + deduplicator.deduplicate(); set pushTags; for (AssemblyItem const& item: input) @@ -857,8 +857,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_same) AssemblyItem(PushTag, 1), AssemblyItem(PushTag, 1), } + blocks; - BlockDeduplicator dedup(input); - dedup.deduplicate(); + BlockDeduplicator deduplicator(input); + deduplicator.deduplicate(); BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); } @@ -876,8 +876,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_value) AssemblyItem{AssignImmutable, 0x1234}, Instruction::JUMP }; - BlockDeduplicator dedup(input); - BOOST_CHECK(!dedup.deduplicate()); + BlockDeduplicator deduplicator(input); + BOOST_CHECK(!deduplicator.deduplicate()); } BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash) @@ -894,8 +894,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash) AssemblyItem{AssignImmutable, 0xABCD}, Instruction::JUMP }; - BlockDeduplicator dedup(input); - BOOST_CHECK(!dedup.deduplicate()); + BlockDeduplicator deduplicator(input); + BOOST_CHECK(!deduplicator.deduplicate()); } BOOST_AUTO_TEST_CASE(block_deduplicator_loops) @@ -920,8 +920,8 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_loops) AssemblyItem(PushTag, 2), Instruction::JUMP, }; - BlockDeduplicator dedup(input); - dedup.deduplicate(); + BlockDeduplicator deduplicator(input); + deduplicator.deduplicate(); set pushTags; for (AssemblyItem const& item: input) diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 51a452923..8ae6e4710 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -78,6 +78,81 @@ private: BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) +BOOST_AUTO_TEST_CASE(user_empty_natspec_test) +{ + char const* sourceCode = R"( + contract test { + /// + /// + function f() public { + } + } + )"; + + char const* natspec = R"( + { + "methods": {} + } + )"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(user_newline_break) +{ + char const* sourceCode = R"( + contract test { + /// + /// @notice hello + + /// @notice world + function f() public { + } + } + )"; + + char const* natspec = R"ABCDEF( + { + "methods": { + "f()": + { + "notice": "world" + } + } + } + )ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(user_multiline_empty_lines) +{ + char const* sourceCode = R"( + contract test { + /** + * + * + * @notice hello world + */ + function f() public { + } + } + )"; + + char const* natspec = R"ABCDEF( + { + "methods": { + "f()": { + "notice": "hello world" + } + } + } + )ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, true); +} + + BOOST_AUTO_TEST_CASE(user_basic_test) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index a80ef8b5c..dc628674a 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -361,7 +361,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed) BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars) @@ -385,7 +385,7 @@ BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell) BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); - BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Send $(value / 1000) chocolates to the user"); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); } BOOST_AUTO_TEST_CASE(comment_before_eos) diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_address.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_address.sol new file mode 100644 index 000000000..08cc04346 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_address.sol @@ -0,0 +1,18 @@ +library L { + function equals(address a, address b) internal pure returns (bool) { + return a == b; + } +} + +contract C { + using L for address; + + function foo(address a, address b) public returns (bool) { + return a.equals(b); + } +} +// ==== +// compileViaYul: also +// ---- +// foo(address, address): 0x111122223333444455556666777788889999aAaa, 0x111122223333444455556666777788889999aAaa -> true +// foo(address, address): 0x111122223333444455556666777788889999aAaa, 0x0000000000000000000000000000000000000000 -> false diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_address_named_send_transfer.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_address_named_send_transfer.sol new file mode 100644 index 000000000..bfc5a3d62 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_address_named_send_transfer.sol @@ -0,0 +1,21 @@ +library L { + function transfer(address a) internal {} + function send(address a) internal {} +} + +contract C { + using L for address; + + function useTransfer(address a) public { + a.transfer(); + } + + function useSend(address a) public { + a.send(); + } +} +// ==== +// compileViaYul: also +// ---- +// useTransfer(address): 0x111122223333444455556666777788889999aAaa -> +// useSend(address): 0x111122223333444455556666777788889999aAaa -> diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol new file mode 100644 index 000000000..c684cc77f --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol @@ -0,0 +1,19 @@ +library L { + function pop(uint[2] memory a) internal {} + function push(uint[2] memory a) internal {} +} + +contract C { + using L for uint[2]; + + function test() public { + uint[2] memory input; + + input.push(); + input.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol new file mode 100644 index 000000000..836f976bb --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol @@ -0,0 +1,20 @@ +library L { + function xor(bool a, bool b) internal pure returns (bool) { + return a != b; + } +} + +contract C { + using L for bool; + + function foo(bool a, bool b) public returns (bool) { + return a.xor(b); + } +} +// ==== +// compileViaYul: also +// ---- +// foo(bool, bool): true, true -> false +// foo(bool, bool): true, false -> true +// foo(bool, bool): false, true -> true +// foo(bool, bool): false, false -> false diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol new file mode 100644 index 000000000..a9566ca59 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol @@ -0,0 +1,21 @@ +contract E {} + +library L { + function foo(E e) internal pure returns (uint) { + return 42; + } +} + +contract C { + using L for E; + + function test() public returns (uint) { + E e = new E(); + return e.foo(); + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> 42 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_dynamic_array.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_dynamic_array.sol new file mode 100644 index 000000000..0ea7e4144 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_dynamic_array.sol @@ -0,0 +1,21 @@ +library L { + function at(uint[] memory a, uint i) internal pure returns (uint) { + return a[i]; + } +} + +contract C { + using L for uint[]; + + function secondItem() public returns (uint) { + uint[] memory input = new uint[](2); + input[0] = 0x11; + input[1] = 0x22; + + return input.at(1); + } +} +// ==== +// compileViaYul: also +// ---- +// secondItem() -> 0x22 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_enum.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_enum.sol new file mode 100644 index 000000000..93c5fd4a0 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_enum.sol @@ -0,0 +1,21 @@ +library L { + enum E { A, B } + + function equals(E a, E b) internal pure returns (bool) { + return a == b; + } +} + +contract C { + using L for L.E; + + function equalsA(uint choice) public returns (bool) { + L.E x = L.E.A; + return x.equals(L.E(choice)); + } +} +// ==== +// compileViaYul: also +// ---- +// equalsA(uint256): 0 -> true +// equalsA(uint256): 1 -> false diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_external_function.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_external_function.sol new file mode 100644 index 000000000..f68cb4e21 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_external_function.sol @@ -0,0 +1,23 @@ +library L { + // NOTE: External function takes up two stack slots + function double(function(uint) external pure returns (uint) f, uint x) internal pure returns (uint) { + return f(x) * 2; + } +} + +contract C { + using L for function(uint) external pure returns (uint); + + function identity(uint x) external pure returns (uint) { + return x; + } + + function test(uint value) public returns (uint) { + return this.identity.double(value); + } +} + +// ==== +// compileViaYul: also +// ---- +// test(uint256): 5 -> 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol new file mode 100644 index 000000000..842911079 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol @@ -0,0 +1,21 @@ +library L { + function at(uint[2] memory a, uint i) internal pure returns (uint) { + return a[i]; + } +} + +contract C { + using L for uint[2]; + + function secondItem() public returns (uint) { + uint[2] memory input; + input[0] = 0x11; + input[1] = 0x22; + + return input.at(1); + } +} +// ==== +// compileViaYul: also +// ---- +// secondItem() -> 0x22 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol new file mode 100644 index 000000000..62bd48beb --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol @@ -0,0 +1,17 @@ +library L { + function add(bytes2 a, bytes2 b) internal pure returns (bytes2) { + return bytes2(uint16(a) + uint16(b)); + } +} + +contract C { + using L for bytes2; + + function sum(bytes2 a, bytes2 b) public returns (bytes2) { + return a.add(b); + } +} +// ==== +// compileViaYul: also +// ---- +// sum(bytes2, bytes2): left(0x1100), left(0x0022) -> left(0x1122) diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol new file mode 100644 index 000000000..75c4e4cb8 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol @@ -0,0 +1,22 @@ +library L { + function selector(function(uint) internal pure returns (uint) f, uint x) internal pure returns (uint) { + return f(x) * 2; + } +} + +contract C { + using L for function(uint) internal pure returns (uint); + + function identity(uint x) internal pure returns (uint) { + return x; + } + + function test(uint value) public returns (uint) { + return identity.selector(value); + } +} + +// ==== +// compileViaYul: also +// ---- +// test(uint256): 5 -> 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol new file mode 100644 index 000000000..b6793c3d7 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol @@ -0,0 +1,17 @@ +library L { + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } +} + +contract C { + using L for uint256; + + function foo(uint256 a, uint256 b) public returns (uint256) { + return a.add(b); + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256, uint256): 8, 42 -> 50 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol new file mode 100644 index 000000000..47e58bb32 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol @@ -0,0 +1,22 @@ +interface I {} +contract E is I {} + +library L { + function foo(I i) internal pure returns (uint) { + return 42; + } +} + +contract C { + using L for I; + + function test() public returns (uint) { + E e = new E(); + return I(e).foo(); + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> 42 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol new file mode 100644 index 000000000..519eafddd --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol @@ -0,0 +1,22 @@ +library L { + function double(function(uint) internal pure returns (uint) f, uint x) internal pure returns (uint) { + return f(x) * 2; + } +} + +contract C { + using L for function(uint) internal pure returns (uint); + + function identity(uint x) internal pure returns (uint) { + return x; + } + + function test(uint value) public returns (uint) { + return identity.double(value); + } +} + +// ==== +// compileViaYul: also +// ---- +// test(uint256): 5 -> 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_mapping.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_mapping.sol new file mode 100644 index 000000000..ec1250a62 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_mapping.sol @@ -0,0 +1,22 @@ +library L { + function at(mapping(uint => uint) storage a, uint i) internal view returns (uint) { + return a[i]; + } +} + +contract C { + using L for mapping(uint => uint); + + mapping(uint => uint) map; + + function mapValue(uint a) public returns (uint) { + map[42] = 0x24; + map[66] = 0x66; + + return map.at(a); + } +} +// ==== +// compileViaYul: also +// ---- +// mapValue(uint256): 42 -> 0x24 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol new file mode 100644 index 000000000..d8d420512 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol @@ -0,0 +1,18 @@ +library L { + function at(string memory a, uint i) internal pure returns (uint8) { + return uint8(bytes(a)[i]); + } +} + +contract C { + using L for string; + + function secondChar() public returns (uint8) { + string memory input = "abc"; + return input.at(1); + } +} +// ==== +// compileViaYul: also +// ---- +// secondChar() -> 98 diff --git a/test/libsolidity/smtCheckerTests/types/unused_mapping.sol b/test/libsolidity/smtCheckerTests/types/unused_mapping.sol new file mode 100644 index 000000000..f12cd41de --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/unused_mapping.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + uint x; + uint y; + mapping (address => bool) public never_used; + + function inc() public { + require(x < 10); + require(y < 10); + + if(x == 0) x = 0; // noop state var read + x++; + y++; + assert(y == x); + } +} diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol b/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol new file mode 100644 index 000000000..b8361e115 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol @@ -0,0 +1,13 @@ +pragma experimental ABIEncoderV2; + +contract Test { + struct shouldBug { + bytes[2] deadly; + } + function killer(bytes[2] calldata weapon) pure external { + shouldBug(weapon); + } +} + +// ---- +// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol b/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol new file mode 100644 index 000000000..c886c8eee --- /dev/null +++ b/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol @@ -0,0 +1,13 @@ +pragma experimental ABIEncoderV2; + +contract Test { + struct shouldBug { + uint256[][2] deadly; + } + function killer(uint256[][2] calldata weapon) pure external { + shouldBug(weapon); + } +} + +// ---- +// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol b/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol new file mode 100644 index 000000000..d8716d18e --- /dev/null +++ b/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol @@ -0,0 +1,13 @@ +pragma experimental ABIEncoderV2; + +contract Test { + struct shouldBug { + uint256[][] deadly; + } + function killer(uint256[][] calldata weapon) pure external { + shouldBug(weapon); + } +} + +// ---- +// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol b/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol new file mode 100644 index 000000000..bae103321 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; + +contract C { + uint[][2] tmp_i; + function i(uint[][2] calldata s) external { tmp_i = s; } +} + +// ---- +// UnimplementedFeatureError: Copying nested calldata dynamic arrays to storage is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol b/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol new file mode 100644 index 000000000..d922d717f --- /dev/null +++ b/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; + +contract C { + uint[][] tmp_i; + function i(uint[][] calldata s) external { tmp_i = s; } +} + +// ---- +// UnimplementedFeatureError: Copying nested calldata dynamic arrays to storage is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/natspec/docstring_double_empty.sol b/test/libsolidity/syntaxTests/natspec/docstring_double_empty.sol new file mode 100644 index 000000000..a84d5d928 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/docstring_double_empty.sol @@ -0,0 +1,7 @@ +contract C { + /// + /// + function vote(uint id) public { + } +} +// ----