Relative paths in import directives.

This commit is contained in:
chriseth 2015-12-09 17:35:20 +01:00
parent 7cb7818cea
commit f8228e8ab1
7 changed files with 67 additions and 9 deletions

View File

@ -71,19 +71,25 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
for (auto const& node: _sourceUnit.nodes()) for (auto const& node: _sourceUnit.nodes())
if (auto imp = dynamic_cast<ImportDirective const*>(node.get())) if (auto imp = dynamic_cast<ImportDirective const*>(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; error = true;
} }
else else
{ {
auto scope = m_scopes.find(_sourceUnits.at(imp->identifier())); auto scope = m_scopes.find(_sourceUnits.at(path));
solAssert(scope != end(m_scopes), ""); solAssert(scope != end(m_scopes), "");
for (auto const& nameAndDeclaration: scope->second->declarations()) for (auto const& nameAndDeclaration: scope->second->declarations())
for (auto const& declaration: nameAndDeclaration.second) for (auto const& declaration: nameAndDeclaration.second)
target.registerDeclaration(*declaration, &nameAndDeclaration.first); target.registerDeclaration(*declaration, &nameAndDeclaration.first);
} }
} }
return !error; return !error;

View File

@ -56,6 +56,13 @@ Error ASTNode::createTypeError(string const& _description) const
return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description); 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<ImportAnnotation&>(*m_annotation);
}
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
{ {
auto exportedFunctionList = interfaceFunctionList(); auto exportedFunctionList = interfaceFunctionList();

View File

@ -142,6 +142,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
ASTString const& identifier() const { return *m_identifier; } ASTString const& identifier() const { return *m_identifier; }
virtual ImportAnnotation& annotation() const override;
private: private:
ASTPointer<ASTString> m_identifier; ASTPointer<ASTString> m_identifier;

View File

@ -54,6 +54,12 @@ struct DocumentedAnnotation
std::multimap<std::string, DocTag> docTags; std::multimap<std::string, DocTag> docTags;
}; };
struct ImportAnnotation: ASTAnnotation
{
/// The absolute path of the source unit to import.
std::string absolutePath;
};
struct TypeDeclarationAnnotation: ASTAnnotation struct TypeDeclarationAnnotation: ASTAnnotation
{ {
/// The name of this type, prefixed by proper namespaces if globally accessible. /// The name of this type, prefixed by proper namespaces if globally accessible.

View File

@ -22,6 +22,7 @@
*/ */
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h> #include <libsolidity/parsing/Parser.h>
@ -367,7 +368,7 @@ void CompilerStack::resolveImports()
vector<Source const*> sourceOrder; vector<Source const*> sourceOrder;
set<Source const*> sourcesSeen; set<Source const*> sourcesSeen;
function<void(Source const*)> toposort = [&](Source const* _source) function<void(string const&, Source const*)> toposort = [&](string const& _sourceName, Source const* _source)
{ {
if (sourcesSeen.count(_source)) if (sourcesSeen.count(_source))
return; return;
@ -375,26 +376,44 @@ void CompilerStack::resolveImports()
for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{ {
string const& id = import->identifier(); string path = absolutePath(import->identifier(), _sourceName);
if (!m_sources.count(id)) import->annotation().absolutePath = path;
if (!m_sources.count(path))
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
Error(Error::Type::ParserError) Error(Error::Type::ParserError)
<< errinfo_sourceLocation(import->location()) << errinfo_sourceLocation(import->location())
<< errinfo_comment("Source not found.") << errinfo_comment("Source not found.")
); );
toposort(&m_sources[id]); toposort(path, &m_sources[path]);
} }
sourceOrder.push_back(_source); sourceOrder.push_back(_source);
}; };
for (auto const& sourcePair: m_sources) for (auto const& sourcePair: m_sources)
if (!sourcePair.second.isLibrary) if (!sourcePair.second.isLibrary)
toposort(&sourcePair.second); toposort(sourcePair.first, &sourcePair.second);
swap(m_sourceOrder, sourceOrder); 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( void CompilerStack::compileContract(
bool _optimize, bool _optimize,
unsigned _runs, unsigned _runs,

View File

@ -199,6 +199,8 @@ private:
}; };
void resolveImports(); 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. /// Compile a single contract and put the result in @a _compiledContracts.
void compileContract( void compileContract(
bool _optimize, bool _optimize,

View File

@ -76,6 +76,23 @@ BOOST_AUTO_TEST_CASE(circular_import)
BOOST_CHECK(c.compile()); 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() BOOST_AUTO_TEST_SUITE_END()
} }