diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | docs/layout-of-source-files.rst | 78 | ||||
-rw-r--r-- | docs/style-guide.rst | 97 | ||||
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.cpp | 11 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 57 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 16 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 125 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 19 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 2 | ||||
-rw-r--r-- | solc/CommandLineInterface.cpp | 13 | ||||
-rw-r--r-- | test/libsolidity/Imports.cpp | 41 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 11 | ||||
-rw-r--r-- | test/libsolidity/SolidityOptimizer.cpp | 21 |
16 files changed, 432 insertions, 70 deletions
@@ -29,6 +29,7 @@ # Build directory build/ +docs/_build # vim stuff *.swp diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 445162f5..1128685d 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -9,24 +9,78 @@ Source files can contain an arbitrary number of contract definitions and include Importing other Source Files ============================ +Syntax and Semantics +-------------------- -Other source files can be referenced using `import "filename";` and the symbols -defined there will also be available in the current source file. +Solidity supports import statements that are very similar to those available in JavaScript +(from ES6 on), although Solidity does not know the concept of a "default export". -.. warning:: +At a global level, you can use import statements of the following form: - Import will not work automatically for the commandline-compiler. - Furthermore, this system of importing other files is not completely fleshed out - yet, so please expect changes. +`import "filename";` will import all global symbols from "filename" (and symbols imported there) into the current global scope (different than in ES6 but backwards-compatible for Solidity). + +`import * as symbolName from "filename";` creates a new global symbol `symbolName` whose members are all the global symbols from `"filename"`. + +`import {symbol1 as alias, symbol2} from "filename";` creates new global symbols `alias` and `symbol2` which reference `symbol1` and `symbal2` from `"filename"`, respectively. + +Another syntax is not part of ES6, but probably convenient: + +`import "filename" as symbolName;` is equivalent to `import * as symbolName from "filename";`. + +Paths +----- + +In the above, `filename` is always treated as a path with `/` as directory separator, +`.` as the current and `..` as the parent directory. Path names that do not start +with `.` are treated as absolute paths. + +To import a file `x` from the same directory as the current file, use `import "./x" as x;`. +If you use `import "x" as x;` instead, a different file could be referenced +(in a global "include directory"). + +It depends on the compiler (see below) how to actually resolve the paths. +In general, the directory hierarchy does not need to strictly map onto your local +filesystem, it can also map to resources discovered via e.g. ipfs, http or git. + +Use in actual Compilers +----------------------- + +When the compiler is invoked, it is not only possible to specify how to +discover the first element of a path, but it is possible to specify path prefix +remappings so that e.g. `github.com/ethereum/dapp-bin/library` is remapped to +`/usr/local/dapp-bin/library` and the compiler will read the files from there. If +remapping keys are prefixes of each other, the longest is tried first. This +allows for a "fallback-remapping" with e.g. `""` maps to +`"/usr/local/include/solidity"`. + +**solc**: + +For solc (the commandline compiler), these remappings are provided as `key=value` +arguments, where the `=value` part is optional (and defaults to key in that +case). All remapping values that are regular files are compiled (including +their dependencies). This mechanism is completely backwards-compatible (as long +as no filename contains a =) and thus not a breaking change. + +So as an example, if you clone +`github.com/ethereum/dapp-bin/` locally to `/usr/local/dapp-bin`, you can use +the following in your source file: + +`import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;` + +and then run the compiler as + +`solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol` + +**browser-solidity**: The `browser-based compiler <https://chriseth.github.io/browser-solidity>`_ -has quite advanced support for multiple files and can even import files -directly from github, by using e.g. -```import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol";``` +provides an automatic remapping for github and will also automatically retrieve +the file over the network: +You can import the iterable mapping by e.g. +`import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;`. + +Other source code providers may be added in the future. -If you want to use multiple source files with the (commandline compiler)[../commandline-compiler/] solc, -you have to specify all files you will use as arguments to solc, -the compiler will not yet search your filesystem on its own. .. index:: ! comment, natspec diff --git a/docs/style-guide.rst b/docs/style-guide.rst index cd901e63..0e782e23 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -529,10 +529,105 @@ No:: x = y+z; x +=1; + +****************** Naming Conventions +****************** + +Naming conventions are powerful when adopted and used broadly. The use of +different conventions can convey significant *meta* information that would +otherwise not be immediately available. + +The naming recommendations given here are intended to improve the readability, +and thus they are not rules, but rather guidelines to try and help convey the +most information through the names of things. + +Lastly, consistency within a codebase should always supercede any conventions +outlined in this document. + + +Naming Styles +============= + +To avoid confusion, the following names will be used to refer to different +naming styles. + +* ``b`` (single lowercase letter) +* ``B`` (single uppercase letter) +* ``lowercase`` +* ``lower_case_with_underscores`` +* ``UPPERCASE`` +* ``UPPER_CASE_WITH_UNDERSCORES`` +* ``CapitalizedWords`` (or CapWords) +* ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!) +* ``Capitalized_Words_With_Underscores`` + +.. note:: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError. + + +Names to Avoid +============== + +* ``l`` - Lowercase letter el +* ``O`` - Uppercase letter oh +* ``I`` - Uppercase letter eye + +Never use any of these for single letter variable names. They are often +indistinguishable from the numerals one and zero. + + +Contract and Library Names +========================== + +Contracts should be named using the CapWords style. + + +Events +====== + +Events should be named using the CapWords style. + + +Function Names +============== + +Functions should use mixedCase. + + +Function Arguments ================== -TODO +When writing library functions that operate on a custom struct, the struct +should be the first argument and should always be named ``self``. + + +Local and State Variables +========================= + +Use mixedCase. + + +Constants +========= + +Constants should be named with all capital letters with underscores separating +words. (for example:``MAX_BLOCKS``) + + +Modifiers +========= + +Function modifiers should use lowercase words separated by underscores. + + +Avoiding Collisions +=================== + +* ``single_trailing_underscore_`` + +This convention is suggested when the desired name collides with that of a +built-in or otherwise reserved name. + General Recommendations ======================= diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index ac56562e..042b7a6a 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -49,6 +49,8 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if (!dynamic_cast<FunctionDefinition const*>(declaration)) return declaration; } + else if (declarations.size() == 1 && declarations.front() == &_declaration) + return nullptr; else if (!declarations.empty()) return declarations.front(); @@ -73,13 +75,12 @@ bool DeclarationContainer::registerDeclaration( m_declarations.erase(*_name); m_invisibleDeclarations.erase(*_name); } - else if (conflictingDeclaration(_declaration)) + else if (conflictingDeclaration(_declaration, _name)) return false; - if (_invisible) - m_invisibleDeclarations[*_name].push_back(&_declaration); - else - m_declarations[*_name].push_back(&_declaration); + vector<Declaration const*>& decls = _invisible ? m_invisibleDeclarations[*_name] : m_declarations[*_name]; + if (!contains(decls, &_declaration)) + decls.push_back(&_declaration); return true; } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 96ffdd6e..5e407383 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -76,23 +76,58 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So string const& path = imp->annotation().absolutePath; if (!_sourceUnits.count(path)) { - reportDeclarationError( node->location(), - "Import \"" + - path + - "\" (referenced as \"" + - imp->path() + - "\") not found." + reportDeclarationError( + imp->location(), + "Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found." ); error = true; + continue; } + auto scope = m_scopes.find(_sourceUnits.at(path)); + solAssert(scope != end(m_scopes), ""); + if (!imp->symbolAliases().empty()) + for (auto const& alias: imp->symbolAliases()) + { + auto declarations = scope->second->resolveName(alias.first->name(), false); + if (declarations.empty()) + { + reportDeclarationError( + imp->location(), + "Declaration \"" + + alias.first->name() + + "\" not found in \"" + + path + + "\" (referenced as \"" + + imp->path() + + "\")." + ); + error = true; + } + else + for (Declaration const* declaration: declarations) + { + ASTString const* name = alias.second ? alias.second.get() : &declaration->name(); + if (!target.registerDeclaration(*declaration, name)) + { + reportDeclarationError( + imp->location(), + "Identifier \"" + *name + "\" already declared." + ); + error = true; + } + } + } else if (imp->name().empty()) - { - auto scope = m_scopes.find(_sourceUnits.at(path)); - solAssert(scope != end(m_scopes), ""); for (auto const& nameAndDeclaration: scope->second->declarations()) for (auto const& declaration: nameAndDeclaration.second) - target.registerDeclaration(*declaration, &nameAndDeclaration.first); - } + if (!target.registerDeclaration(*declaration, &nameAndDeclaration.first)) + { + reportDeclarationError( + imp->location(), + "Identifier \"" + nameAndDeclaration.first + "\" already declared." + ); + error = true; + } } return !error; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ab1151a1..69357043 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -292,17 +292,21 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co if (f->isPartOfExternalInterface()) { auto functionType = make_shared<FunctionType>(*f); - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(f, functionType) - ); + // under non error circumstances this should be true + if (functionType->interfaceFunctionType()) + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(f, functionType) + ); } for (VariableDeclaration const* v: contract->stateVariables()) if (v->isPartOfExternalInterface()) { auto functionType = make_shared<FunctionType>(*v); - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(v, functionType) - ); + // under non error circumstances this should be true + if (functionType->interfaceFunctionType()) + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(v, functionType) + ); } } for (auto const& it: externalDeclarations) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 604a12a0..4baf95d3 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -201,6 +201,10 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; ASTString const& path() const { return *m_path; } + std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> const& symbolAliases() const + { + return m_symbolAliases; + } virtual ImportAnnotation& annotation() const override; virtual TypePointer type() const override; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 2e9b56a1..79e5bb02 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1593,6 +1593,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const else return FunctionTypePointer(); } + auto variable = dynamic_cast<VariableDeclaration const*>(m_declaration); + if (variable && retParamTypes.empty()) + return FunctionTypePointer(); + return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f0dab41a..040217da 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -807,7 +807,6 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) ); auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope()); solAssert(contract && contract->isLibrary(), ""); - //@TODO library name might not be unique m_context.appendLibraryAddress(contract->name()); m_context << funType->externalIdentifier(); utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2); @@ -1118,7 +1117,6 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) { if (contract->isLibrary()) - //@todo name should be unique, change once we have module management m_context.appendLibraryAddress(contract->name()); } else if (dynamic_cast<EventDefinition const*>(declaration)) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 8ff63056..83459183 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -39,11 +39,8 @@ #include <libdevcore/SHA3.h> using namespace std; - -namespace dev -{ -namespace solidity -{ +using namespace dev; +using namespace dev::solidity; const map<string, string> StandardSources = map<string, string>{ {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"}, @@ -59,8 +56,8 @@ const map<string, string> StandardSources = map<string, string>{ {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"} }; -CompilerStack::CompilerStack(bool _addStandardSources): - m_parseSuccessful(false) +CompilerStack::CompilerStack(bool _addStandardSources, ReadFileCallback const& _readFile): + m_readFile(_readFile), m_parseSuccessful(false) { if (_addStandardSources) addSources(StandardSources, true); // add them as libraries @@ -105,16 +102,30 @@ bool CompilerStack::parse() m_errors.clear(); m_parseSuccessful = false; + vector<string> sourcesToParse; + for (auto const& s: m_sources) + sourcesToParse.push_back(s.first); map<string, SourceUnit const*> sourceUnitsByName; - for (auto& sourcePair: m_sources) + for (size_t i = 0; i < sourcesToParse.size(); ++i) { - sourcePair.second.scanner->reset(); - sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner); - if (!sourcePair.second.ast) + string const& path = sourcesToParse[i]; + Source& source = m_sources[path]; + source.scanner->reset(); + source.ast = Parser(m_errors).parse(source.scanner); + sourceUnitsByName[path] = source.ast.get(); + if (!source.ast) solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error."); else - sourcePair.second.ast->annotation().path = sourcePair.first; - sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get(); + { + source.ast->annotation().path = path; + for (auto const& newSource: loadMissingSources(*source.ast, path)) + { + string const& newPath = newSource.first; + string const& newContents = newSource.second; + m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents), newPath); + sourcesToParse.push_back(newPath); + } + } } if (!Error::containsOnlyWarnings(m_errors)) // errors while parsing. sould stop before type checking @@ -154,6 +165,9 @@ bool CompilerStack::parse() m_contracts[contract->name()].contract = contract; } + if (!checkLibraryNameClashes()) + noErrors = false; + for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) @@ -370,13 +384,44 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn); } +StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _path) +{ + StringMap newSources; + for (auto const& node: _ast.nodes()) + if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) + { + string path = absolutePath(import->path(), _path); + import->annotation().absolutePath = path; + if (m_sources.count(path) || newSources.count(path)) + continue; + string contents; + string errorMessage; + if (!m_readFile) + errorMessage = "File not supplied initially."; + else + tie(contents, errorMessage) = m_readFile(path); + if (!errorMessage.empty()) + { + auto err = make_shared<Error>(Error::Type::ParserError); + *err << + errinfo_sourceLocation(import->location()) << + errinfo_comment("Source not found: " + errorMessage); + m_errors.push_back(std::move(err)); + continue; + } + else + newSources[path] = contents; + } + return newSources; +} + void CompilerStack::resolveImports() { // topological sorting (depth first search) of the import graph, cutting potential cycles vector<Source const*> sourceOrder; set<Source const*> sourcesSeen; - function<void(string const&, Source const*)> toposort = [&](string const& _sourceName, Source const* _source) + function<void(Source const*)> toposort = [&](Source const* _source) { if (sourcesSeen.count(_source)) return; @@ -384,28 +429,53 @@ void CompilerStack::resolveImports() for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) { - string path = absolutePath(import->path(), _sourceName); - import->annotation().absolutePath = path; - if (!m_sources.count(path)) - BOOST_THROW_EXCEPTION( - Error(Error::Type::ParserError) - << errinfo_sourceLocation(import->location()) - << errinfo_comment("Source not found.") - ); - import->annotation().sourceUnit = m_sources.at(path).ast.get(); - - toposort(path, &m_sources[path]); + string const& path = import->annotation().absolutePath; + solAssert(!path.empty(), ""); + solAssert(m_sources.count(path), ""); + import->annotation().sourceUnit = m_sources[path].ast.get(); + toposort(&m_sources[path]); } sourceOrder.push_back(_source); }; for (auto const& sourcePair: m_sources) if (!sourcePair.second.isLibrary) - toposort(sourcePair.first, &sourcePair.second); + toposort(&sourcePair.second); swap(m_sourceOrder, sourceOrder); } +bool CompilerStack::checkLibraryNameClashes() +{ + bool clashFound = false; + map<string, SourceLocation> libraries; + for (Source const* source: m_sourceOrder) + for (ASTPointer<ASTNode> const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) + if (contract->isLibrary()) + { + if (libraries.count(contract->name())) + { + auto err = make_shared<Error>(Error::Type::DeclarationError); + *err << + errinfo_sourceLocation(contract->location()) << + errinfo_comment( + "Library \"" + contract->name() + "\" declared twice " + "(will create ambiguities during linking)." + ) << + errinfo_secondarySourceLocation(SecondarySourceLocation().append( + "The other declaration is here:", libraries[contract->name()] + )); + + m_errors.push_back(err); + clashFound = true; + } + else + libraries[contract->name()] = contract->location(); + } + return !clashFound; +} + string CompilerStack::absolutePath(string const& _path, string const& _reference) const { // Anything that does not start with `.` is an absolute path. @@ -479,6 +549,3 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } - -} -} diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 3e6dc456..517d0055 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -27,6 +27,7 @@ #include <string> #include <memory> #include <vector> +#include <functional> #include <boost/noncopyable.hpp> #include <json/json.h> #include <libdevcore/Common.h> @@ -74,8 +75,14 @@ enum class DocumentationType: uint8_t class CompilerStack: boost::noncopyable { public: - /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. - explicit CompilerStack(bool _addStandardSources = true); + /// File reading callback, should return a pair of content and error message (exactly one nonempty) + /// for a given path. + using ReadFileCallback = std::function<std::pair<std::string, std::string>(std::string const&)>; + + /// Creates a new compiler stack. + /// @param _readFile callback to used to read files for import statements. Should return + /// @param _addStandardSources Adds standard sources if @a _addStandardSources. + explicit CompilerStack(bool _addStandardSources = true, ReadFileCallback const& _readFile = ReadFileCallback()); /// Resets the compiler to a state where the sources are not parsed or even removed. void reset(bool _keepSources = false, bool _addStandardSources = true); @@ -198,7 +205,14 @@ private: mutable std::unique_ptr<std::string const> devDocumentation; }; + /// Loads the missing sources from @a _ast (named @a _path) using the callback + /// @a m_readFile and stores the absolute paths of all imports in the AST annotations. + /// @returns the newly loaded sources. + StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path); void resolveImports(); + /// Checks whether there are libraries with the same name, reports that as an error and + /// @returns false in this case. + bool checkLibraryNameClashes(); /// @returns the absolute path corresponding to @a _path relative to @a _reference. std::string absolutePath(std::string const& _path, std::string const& _reference) const; /// Compile a single contract and put the result in @a _compiledContracts. @@ -212,6 +226,7 @@ private: Contract const& contract(std::string const& _contractName = "") const; Source const& source(std::string const& _sourceName = "") const; + ReadFileCallback m_readFile; bool m_parseSuccessful; std::map<std::string const, Source> m_sources; std::shared_ptr<GlobalContext> m_globalContext; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 7dd3564d..4ac3381c 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -145,7 +145,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective() expectToken(Token::As); alias = expectIdentifierToken(); } - symbolAliases.push_back(move(make_pair(move(id), move(alias)))); + symbolAliases.push_back(make_pair(move(id), move(alias))); if (m_scanner->currentToken() != Token::Comma) break; m_scanner->next(); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index fe760fdf..4c9de3c8 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -497,7 +497,18 @@ bool CommandLineInterface::processInput() return link(); } - m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0)); + function<pair<string,string>(string const&)> fileReader = [this](string const& _path) + { + auto path = boost::filesystem::path(_path); + if (!boost::filesystem::exists(path)) + return make_pair(string(), string("File not found.")); + else if (!boost::filesystem::is_regular_file(path)) + return make_pair(string(), string("Not a valid file.")); + else + return make_pair(m_sourceCodes[_path] = dev::contentsString(_path), string()); + }; + + m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader)); try { for (auto const& sourceCode: m_sourceCodes) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index f0f67785..94d3e423 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -101,6 +101,47 @@ BOOST_AUTO_TEST_CASE(simple_alias) BOOST_CHECK(c.compile()); } +BOOST_AUTO_TEST_CASE(library_name_clash) +{ + CompilerStack c; + c.addSource("a", "library A {}"); + c.addSource("b", "library A {}"); + BOOST_CHECK(!c.compile()); +} + +BOOST_AUTO_TEST_CASE(library_name_clash_with_contract) +{ + CompilerStack c; + c.addSource("a", "contract A {}"); + c.addSource("b", "library A {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(complex_import) +{ + CompilerStack c; + c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } }"); + c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; " + "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} }"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(name_clash_in_import) +{ + CompilerStack c; + c.addSource("a", "contract A {}"); + c.addSource("b", "import \"a\"; contract A {} "); + BOOST_CHECK(!c.compile()); + c.addSource("b", "import \"a\" as A; contract A {} "); + BOOST_CHECK(!c.compile()); + c.addSource("b", "import {A as b} from \"a\"; contract b {} "); + BOOST_CHECK(!c.compile()); + c.addSource("b", "import {A} from \"a\"; contract A {} "); + BOOST_CHECK(!c.compile()); + c.addSource("b", "import {A} from \"a\"; contract B {} "); + BOOST_CHECK(c.compile()); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index a95021ea..c8e901e4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1008,6 +1008,17 @@ BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only) +{ + char const* sourceCode = R"( + contract test { + struct Data { uint[15] m_array; } + Data public data; + } + )"; + BOOST_CHECK(expectError(sourceCode) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) { char const* text = "contract Parent {\n" diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 732f599f..2f1eb04e 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -369,6 +369,27 @@ BOOST_AUTO_TEST_CASE(successor_not_found_bug) compileBothVersions(sourceCode); } +BOOST_AUTO_TEST_CASE(incorrect_storage_access_bug) +{ + // This bug appeared because a sha3 operation with too low sequence number was used, + // resulting in memory not being rewritten before the sha3. The fix was to + // take the max of the min sequence numbers when merging the states. + char const* sourceCode = R"( + contract C + { + mapping(uint => uint) data; + function f() returns (uint) + { + if(data[now] == 0) + data[uint(-7)] = 5; + return data[now]; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f()"); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { eth::KnownState state; |