diff options
-rw-r--r-- | CODING_STYLE.md | 345 | ||||
-rw-r--r-- | Changelog.md | 1 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 27 | ||||
-rwxr-xr-x | test/externalTests.sh | 7 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 15 | ||||
-rw-r--r-- | test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol | 7 | ||||
-rw-r--r-- | test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol | 9 | ||||
-rw-r--r-- | test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mappings.sol | 10 | ||||
-rw-r--r-- | test/libsolidity/syntaxTests/memberLookup/push_on_memory_types.sol | 8 |
9 files changed, 214 insertions, 215 deletions
diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 2cc9ac70..3244466b 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -1,35 +1,24 @@ -0. Formatting - -GOLDEN RULE: Follow the style of the existing code when you make changes. - -a. Use tabs for leading indentation -- tab stops are every 4 characters (only relevant for line length). -- One indentation level -> exactly one byte (i.e. a tab character) in the source file. -b. Line widths: -- Lines should be at most 99 characters wide to make diff views readable and reduce merge conflicts. -- Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty. -c. Single-statement blocks should not have braces, unless required for clarity. -d. Never place condition bodies on same line as condition. -e. Space between keyword and opening parenthesis, but not following opening parenthesis or before final parenthesis. -f. No spaces for unary operators, `->` or `.`. -g. No space before ':' but one after it, except in the ternary operator: one on both sides. -h. Add spaces around all other operators. -i. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. -j. If lines are broken, a list of elements enclosed with parentheses (of any kind) and separated by a - separator (of any kind) are formatted such that there is exactly one element per line, followed by - the separator, the opening parenthesis is on the first line, followed by a line break and the closing - parenthesis is on a line of its own (unindented). See example below. - -(WRONG) -if( a==b[ i ] ) { printf ("Hello\n"); } -foo->bar(someLongVariableName, - anotherLongVariableName, - anotherLongVariableName, - anotherLongVariableName, - anotherLongVariableName); -cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl; - -(RIGHT) +## 0. Formatting + +**GOLDEN RULE**: Follow the style of the existing code when you make changes. + +1. Use tabs for leading indentation: + - tab stops are every 4 characters (only relevant for line length). + - one indentation level -> exactly one byte (i.e. a tab character) in the source file. +2. Line widths: + - Lines should be at most 99 characters wide to make diff views readable and reduce merge conflicts. + - Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty. +3. Single-statement blocks should not have braces, unless required for clarity. +4. Never place condition bodies on same line as condition. +5. Space between keyword and opening parenthesis, but not following opening parenthesis or before final parenthesis. +6. No spaces for unary operators, `->` or `.`. +7. No space before `:` but one after it, except in the ternary operator: one on both sides. +8. Add spaces around all other operators. +9. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. +10. If lines are broken, a list of elements enclosed with parentheses (of any kind) and separated by a separator (of any kind) are formatted such that there is exactly one element per line, followed by the separator, the opening parenthesis is on the first line, followed by a line break and the closing parenthesis is on a line of its own unindented). See example below. + +Yes: +```cpp if (a == b[i]) printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'. foo->bar( @@ -44,99 +33,92 @@ cout << "text that talks about this and that and contains the words " << "\"lorem\" and \"ipsum\"" << endl; +``` +No: +```cpp +if( a==b[ i ] ) { printf ("Hello\n"); } +foo->bar(someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName); +cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl; +``` +## 1. Namespaces -1. Namespaces; +1. No `using namespace` declarations in header files. +2. All symbols should be declared in a namespace except for final applications. +3. Use anonymous namespaces for helpers whose scope is a cpp file only. +4. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. -a. No "using namespace" declarations in header files. -b. All symbols should be declared in a namespace except for final applications. -c. Use anonymous namespaces for helpers whose scope is a cpp file only. -d. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. +Yes: +```cpp +#include <cassert> +std::tuple<float, float> meanAndSigma(std::vector<float> const& _v); +``` -(WRONG) +No: +```cpp #include <cassert> using namespace std; tuple<float, float> meanAndSigma(vector<float> const& _v); +``` -(CORRECT) -#include <cassert> -std::tuple<float, float> meanAndSigma(std::vector<float> const& _v); - - - -2. Preprocessor; - -a. File comment is always at top, and includes: -- Copyright. -- License (e.g. see COPYING). -b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment. -c. Prefer static const variable to value macros. -d. Prefer inline constexpr functions to function macros. -e. Split complex macro on multiple lines with '\'. - - +## 2. Preprocessor -3. Capitalization; +1. File comment is always at top, and includes: + - Copyright + - License (e.g. see COPYING) +2. Never use `#ifdef`/`#define`/`#endif` file guards. Prefer `#pragma` once as first line below file comment. +3. Prefer static const variable to value macros. +4. Prefer inline constexpr functions to function macros. +5. Split complex macro on multiple lines with `\`. -GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase. +## 3. Capitalization -a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. -b. The following entities' first alpha is upper case: -- Type names. -- Template parameters. -- Enum members. -- static const variables that form an external API. -c. All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation. +**GOLDEN RULE**: Preprocessor: `ALL_CAPS`; C++: `camelCase`. +1. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. +2. The following entities' first alpha is upper case: + - Type names + - Template parameters + - Enum members + - static const variables that form an external API. +3. All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation. All other entities' first alpha is lower case. +## 4. Variable prefixes +1. Leading underscore "_" to parameter names: + - Exception: "o_parameterName" when it is used exclusively for output. See 6(f). + - Exception: "io_parameterName" when it is used for both input and output. See 6(f). +2. Leading "g_" to global (non-const) variables. +3. Leading "s_" to static (non-const, non-global) variables. -4. Variable prefixes: - -a. Leading underscore "_" to parameter names. -- Exception: "o_parameterName" when it is used exclusively for output. See 6(f). -- Exception: "io_parameterName" when it is used for both input and output. See 6(f). -b. Leading "g_" to global (non-const) variables. -c. Leading "s_" to static (non-const, non-global) variables. - - - -5. Assertions: - -- use `solAssert` and `solUnimplementedAssert` generously to check assumptions - that span across different parts of the code base, for example before dereferencing - a pointer. - - -6. Declarations: - -a. {Typename} + {qualifiers} + {name}. -b. Only one per line. -c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). -d. Favour declarations close to use; don't habitually declare at top of scope ala C. -e. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move. -f. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments. -g. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer. -h. Never use a macro where adequate non-preprocessor C++ can be written. -i. Only use ``auto`` if the type is very long and rather irrelevant. -j. Do not pass bools: prefer enumerations instead. -k. Prefer enum class to straight enum. -l. Always initialize POD variables, even if their value is overwritten later. +## 5. Assertions +Use `solAssert` and `solUnimplementedAssert` generously to check assumptions that span across different parts of the code base, for example before dereferencing a pointer. -(WRONG) -const double d = 0; -int i, j; -char *s; -float meanAndSigma(std::vector<float> _v, float* _sigma, bool _approximate); -Derived* x(dynamic_cast<Derived*>(base)); -for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); ++l) {} +## 6. Declarations +1. {Typename} + {qualifiers} + {name}. +2. Only one per line. +3. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). +4. Favour declarations close to use; don't habitually declare at top of scope ala C. +5. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move. +6. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments. +7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer. +8. Never use a macro where adequate non-preprocessor C++ can be written. +9. Only use ``auto`` if the type is very long and rather irrelevant. +10. Do not pass bools: prefer enumerations instead. +11. Prefer enum class to straight enum. +12. Always initialize POD variables, even if their value is overwritten later. -(CORRECT) +Yes: +```cpp enum class Accuracy { Approximate, @@ -154,78 +136,78 @@ char* s; MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a); Derived* x = dynamic_cast<Derived*>(base); for (auto i = x->begin(); i != x->end(); ++i) {} +``` +No: +```cp +const double d = 0; +int i, j; +char *s; +float meanAndSigma(std::vector<float> _v, float* _sigma, bool _approximate); +Derived* x(dynamic_cast<Derived*>(base)); +for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); ++l) {} +``` -7. Structs & classes - -a. Structs to be used when all members public and no virtual functions. -- In this case, members should be named naturally and not prefixed with 'm_' -b. Classes to be used in all other circumstances. - - - -8. Members: - -a. One member per line only. -b. Private, non-static, non-const fields prefixed with m_. -c. Avoid public fields, except in structs. -d. Use override, final and const as much as possible. -e. No implementations with the class declaration, except: -- template or force-inline method (though prefer implementation at bottom of header file). -- one-line implementation (in which case include it in same line as declaration). -f. For a property 'foo' -- Member: m_foo; -- Getter: foo() [ also: for booleans, isFoo() ]; -- Setter: setFoo(); - - - -9. Naming - -a. Avoid unpronouncable names -b. Names should be shortened only if they are extremely common, but shortening should be generally avoided -c. Avoid prefixes of initials (e.g. do not use IMyInterface, CMyImplementation) -c. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. -- A dictionary and thesaurus are your friends. -- Spell correctly. -- Think carefully about the class's purpose. -- Imagine it as an isolated component to try to decontextualise it when considering its name. -- Don't be trapped into naming it (purely) in terms of its implementation. - - - -10. Type-definitions - -a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector<int>; rather than typedef std::vector<int> ints; -b. Generally avoid shortening a standard form that already includes all important information: -- e.g. stick to shared_ptr<X> rather than shortening to ptr<X>. -c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently. -- e.g. using Guard = std::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it is clear in meaning and used commonly. -d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. -e. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time. - - - -11. Commenting - -a. Comments should be doxygen-compilable, using @notation rather than \notation. -b. Document the interface, not the implementation. -- Documentation should be able to remain completely unchanged, even if the method is reimplemented. -- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports). -- Be careful to scrutinise documentation that extends only to intended purpose and usage. -- Reject documentation that is simply an English transaction of the implementation. -c. Avoid in-code comments. Instead, try to extract blocks of functionality into functions. This often already eliminates the need for an in-code comment. - - -12. Include Headers - -Includes should go in increasing order of generality (libsolidity -> libevmasm -> libdevcore -> boost -> STL). -The corresponding .h file should be the first include in the respective .cpp file. -Insert empty lines between blocks of include files. +## 7. Structs & classes + +1. Structs to be used when all members public and no virtual functions: + - In this case, members should be named naturally and not prefixed with `m_`. +2. Classes to be used in all other circumstances. + +## 8. Members + +1. One member per line only. +2. Private, non-static, non-const fields prefixed with `m_`. +3. Avoid public fields, except in structs. +4. Use override, final and const as much as possible. +5. No implementations with the class declaration, except: + - template or force-inline method (though prefer implementation at bottom of header file). + - one-line implementation (in which case include it in same line as declaration). +6. For a property `foo` + - Member: `m_foo` + - Getter: `foo()` [ also: for booleans, `isFoo()` ] + - Setter: `setFoo()` + +## 9. Naming + +1. Avoid unpronouncable names. +2. Names should be shortened only if they are extremely common, but shortening should be generally avoided +3. Avoid prefixes of initials (e.g. do not use `IMyInterface`, `CMyImplementation`) +4. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments: + - A dictionary and thesaurus are your friends; + - Spell correctly; + - Think carefully about the class's purpose; + - Imagine it as an isolated component to try to decontextualise it when considering its name; + - Don't be trapped into naming it (purely) in terms of its implementation. + +## 10. Type definitions + +1. Prefer `using` to `typedef`. e.g. `using ints = std::vector<int>;` rather than typedef `std::vector<int> ints;` +2. Generally avoid shortening a standard form that already includes all important information: + - e.g. stick to `shared_ptr<X>` rather than shortening to `ptr<X>`. +3. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently: + - e.g. `using Guard = std::lock_guard<std::mutex>;` ///< Guard is used throughout the codebase since it is clear in meaning and used commonly. +4. In general expressions should be roughly as important/semantically meaningful as the space they occupy. +5. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time. + +## 11. Commenting + +1. Comments should be doxygen-compilable, using @notation rather than \notation. +2. Document the interface, not the implementation: + - Documentation should be able to remain completely unchanged, even if the method is reimplemented; + - Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports); + - Be careful to scrutinise documentation that extends only to intended purpose and usage; + - Reject documentation that is simply an English transaction of the implementation. +3. Avoid in-code comments. Instead, try to extract blocks of functionality into functions. This often already eliminates the need for an in-code comment. + +## 12. Include Headers + +1. Includes should go in increasing order of generality (`libsolidity` -> `libevmasm` -> `libdevcore` -> `boost` -> `STL`). +2. The corresponding `.h` file should be the first include in the respective `.cpp` file. +3. Insert empty lines between blocks of include files. Example: - -``` +```cpp #include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/ast/AST.h> @@ -245,18 +227,17 @@ Example: #include <numeric> ``` -See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files. - +See [this issue](http://stackoverflow.com/questions/614302/c-header-order/614333#614333 "C header order") for the reason: this makes it easier to find missing includes in header files. -13. Recommended reading +## 13. Recommended reading -Herb Sutter and Bjarne Stroustrup -- "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) +- Herb Sutter and Bjarne Stroustrup: + - [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) -Herb Sutter and Andrei Alexandrescu -- "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices" +- Herb Sutter and Andrei Alexandrescu: + - "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices" -Scott Meyers -- "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)" -- "More Effective C++: 35 New Ways to Improve Your Programs and Designs" -- "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14" +- Scott Meyers: + - "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)" + - "More Effective C++: 35 New Ways to Improve Your Programs and Designs" + - "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14" diff --git a/Changelog.md b/Changelog.md index 8812bace..3922c641 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Syntax Checker: Warn about functions named "constructor". Bugfixes: + * Type Checker: Improve error message for failed function overload resolution. * Type Checker: Do not complain about new-style constructor and fallback function to have the same name. * Type Checker: Detect multiple constructor declarations in the new syntax and old syntax. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 87d69d97..72d29762 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1904,7 +1904,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) // Retrieve the types of the arguments if this is used to call a function. auto const& argumentTypes = _memberAccess.annotation().argumentTypes; MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName); - if (possibleMembers.size() > 1 && argumentTypes) + size_t const initialMemberCount = possibleMembers.size(); + if (initialMemberCount > 1 && argumentTypes) { // do overload resolution for (auto it = possibleMembers.begin(); it != possibleMembers.end();) @@ -1918,17 +1919,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) } if (possibleMembers.size() == 0) { - auto storageType = ReferenceType::copyForLocationIfReference( - DataLocation::Storage, - exprType - ); - if (!storageType->members(m_scope).membersByName(memberName).empty()) - m_errorReporter.fatalTypeError( - _memberAccess.location(), - "Member \"" + memberName + "\" is not available in " + - exprType->toString() + - " outside of storage." + if (initialMemberCount == 0) + { + // Try to see if the member was removed because it is only available for storage types. + auto storageType = ReferenceType::copyForLocationIfReference( + DataLocation::Storage, + exprType ); + if (!storageType->members(m_scope).membersByName(memberName).empty()) + m_errorReporter.fatalTypeError( + _memberAccess.location(), + "Member \"" + memberName + "\" is not available in " + + exprType->toString() + + " outside of storage." + ); + } m_errorReporter.fatalTypeError( _memberAccess.location(), "Member \"" + memberName + "\" not found or not visible " diff --git a/test/externalTests.sh b/test/externalTests.sh index 2a5ff7ef..cd6deb75 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -47,13 +47,6 @@ function test_truffle cd "$DIR" npm install find . -name soljson.js -exec cp "$SOLJSON" {} \; - if [ "$name" == "Zeppelin" ]; then - # Fix some things that look like bugs (only seemed to fail on Node 6 and not Node 8) - # FIXME: report upstream or to web3.js? - sed -i -e 's/let token = await ERC827TokenMock.new();//;' test/token/ERC827/ERC827Token.js - sed -i -e 's/CappedCrowdsale.new(this.startTime, this.endTime, rate, wallet, 0)/CappedCrowdsale.new(this.startTime, this.endTime, rate, wallet, 0, this.token.address)/' test/crowdsale/CappedCrowdsale.test.js - sed -i -e 's/RefundableCrowdsale.new(this.startTime, this.endTime, rate, wallet, 0, { from: owner })/RefundableCrowdsale.new(this.startTime, this.endTime, rate, wallet, 0, this.token.address, { from: owner })/' test/crowdsale/RefundableCrowdsale.test.js - fi if [ "$name" == "Gnosis" ]; then # Replace fixed-version pragmas in Gnosis (part of Consensys best practice) find contracts test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity 0/pragma solidity ^0/' diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7c0e8643..97cf0410 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2993,21 +2993,6 @@ BOOST_AUTO_TEST_CASE(literal_strings) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) -{ - char const* text = R"( - contract Test { - struct S { uint8 a; mapping(uint => uint) b; uint8 c; } - S s; - function f() public { - S memory x; - x.b[1]; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"b\" is not available in struct Test.S memory outside of storage."); -} - BOOST_AUTO_TEST_CASE(string_bytes_conversion) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol new file mode 100644 index 00000000..c23992e9 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint, uint) {} + function f(uint) {} + function g() { f(1, 2, 3); } +} +// ---- +// TypeError: (80-81): No matching declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol new file mode 100644 index 00000000..310c4a10 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol @@ -0,0 +1,9 @@ +library L { + function f(uint, uint) {} + function f(uint) {} +} +contract C { + function g() { L.f(1, 2, 3); } +} +// ---- +// TypeError: (94-97): Member "f" not found or not visible after argument-dependent lookup in type(library L) diff --git a/test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mappings.sol b/test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mappings.sol new file mode 100644 index 00000000..bdafc754 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/memory_structs_with_mappings.sol @@ -0,0 +1,10 @@ +contract Test { + struct S { uint8 a; mapping(uint => uint) b; uint8 c; } + S s; + function f() public { + S memory x; + x.b[1]; + } +} +// ---- +// TypeError: (118-121): Member "b" is not available in struct Test.S memory outside of storage. diff --git a/test/libsolidity/syntaxTests/memberLookup/push_on_memory_types.sol b/test/libsolidity/syntaxTests/memberLookup/push_on_memory_types.sol new file mode 100644 index 00000000..310c073f --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/push_on_memory_types.sol @@ -0,0 +1,8 @@ +contract Test { + function f() public pure { + uint[] memory x; + x.push(1); + } +} +// ---- +// TypeError: (77-83): Member "push" is not available in uint256[] memory outside of storage. |