From f8228e8ab116799a1b28ea9cbb01fdd7342b395c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 9 Dec 2015 17:35:20 +0100 Subject: Relative paths in import directives. --- libsolidity/analysis/NameAndTypeResolver.cpp | 14 ++++++++++---- libsolidity/ast/AST.cpp | 7 +++++++ libsolidity/ast/AST.h | 1 + libsolidity/ast/ASTAnnotations.h | 6 ++++++ libsolidity/interface/CompilerStack.cpp | 29 +++++++++++++++++++++++----- libsolidity/interface/CompilerStack.h | 2 ++ test/libsolidity/Imports.cpp | 17 ++++++++++++++++ 7 files changed, 67 insertions(+), 9 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 907d703c..ca866ddb 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -71,19 +71,25 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map(node.get())) { - if (!_sourceUnits.count(imp->identifier())) + string const& path = imp->annotation().absolutePath; + if (!_sourceUnits.count(path)) { - reportDeclarationError(node->location(), "Import \"" + imp->identifier() + "\" not found."); + reportDeclarationError( node->location(), + "Import \"" + + path + + "\" (referenced as \"" + + imp->identifier() + + "\") not found." + ); error = true; } else { - auto scope = m_scopes.find(_sourceUnits.at(imp->identifier())); + 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); - } } return !error; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6006d441..701202f9 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -56,6 +56,13 @@ Error ASTNode::createTypeError(string const& _description) const return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description); } +ImportAnnotation& ImportDirective::annotation() const +{ + if (!m_annotation) + m_annotation = new ImportAnnotation(); + return static_cast(*m_annotation); +} + map, FunctionTypePointer> ContractDefinition::interfaceFunctions() const { auto exportedFunctionList = interfaceFunctionList(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 446088ad..75cb9ab2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -142,6 +142,7 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; ASTString const& identifier() const { return *m_identifier; } + virtual ImportAnnotation& annotation() const override; private: ASTPointer m_identifier; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 4e0187cf..0bc91c60 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -54,6 +54,12 @@ struct DocumentedAnnotation std::multimap docTags; }; +struct ImportAnnotation: ASTAnnotation +{ + /// The absolute path of the source unit to import. + std::string absolutePath; +}; + struct TypeDeclarationAnnotation: ASTAnnotation { /// The name of this type, prefixed by proper namespaces if globally accessible. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 38fe5956..7f097523 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -367,7 +368,7 @@ void CompilerStack::resolveImports() vector sourceOrder; set sourcesSeen; - function toposort = [&](Source const* _source) + function toposort = [&](string const& _sourceName, Source const* _source) { if (sourcesSeen.count(_source)) return; @@ -375,26 +376,44 @@ void CompilerStack::resolveImports() for (ASTPointer const& node: _source->ast->nodes()) if (ImportDirective const* import = dynamic_cast(node.get())) { - string const& id = import->identifier(); - if (!m_sources.count(id)) + string path = absolutePath(import->identifier(), _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.") ); - toposort(&m_sources[id]); + toposort(path, &m_sources[path]); } sourceOrder.push_back(_source); }; for (auto const& sourcePair: m_sources) if (!sourcePair.second.isLibrary) - toposort(&sourcePair.second); + toposort(sourcePair.first, &sourcePair.second); swap(m_sourceOrder, sourceOrder); } +string CompilerStack::absolutePath(string const& _path, string const& _reference) const +{ + // Anything that does not start with `.` is an absolute path. + if (_path.empty() || _path.front() != '.') + return _path; + using path = boost::filesystem::path; + path p(_path); + path result(_reference); + result.remove_filename(); + for (path::iterator it = p.begin(); it != p.end(); ++it) + if (*it == "..") + result = result.parent_path(); + else if (*it != ".") + result /= *it; + return result.string(); +} + void CompilerStack::compileContract( bool _optimize, unsigned _runs, diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 0473d58b..3e6dc456 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -199,6 +199,8 @@ private: }; void resolveImports(); + /// @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. void compileContract( bool _optimize, diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index b5ff0c54..ab8e2257 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -76,6 +76,23 @@ BOOST_AUTO_TEST_CASE(circular_import) BOOST_CHECK(c.compile()); } +BOOST_AUTO_TEST_CASE(relative_import) +{ + CompilerStack c; + c.addSource("a", "import \"./dir/b\"; contract A is B {}"); + c.addSource("dir/b", "contract B {}"); + c.addSource("dir/c", "import \"../a\"; contract C is A {}"); + BOOST_CHECK(c.compile()); +} + +BOOST_AUTO_TEST_CASE(relative_import_multiplex) +{ + CompilerStack c; + c.addSource("a", "contract A {}"); + c.addSource("dir/a/b/c", "import \"../../.././a\"; contract B is A {}"); + BOOST_CHECK(c.compile()); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit v1.2.3