Repository: mun-lang/mun (language: rust)

Issue:
Add language support for `use` statement
Similar to Rust, we want to be able to `use` functions, constants and types from other modules. However, our keywords and implementation differ slightly. For more information, see #248.

Given a folder structure:
- mod.mun
- foo.mun
- bar.mun

file: `mod.mun`
```mun
pub use foo::Foo; // Re-export a child's definition

struct Baz;
```

file: `foo.mun`
```mun
use package::{bar::Bar, Baz};
// Illegal: A statement with the same name was already imported (Even though both use statements are equivalent)
use super::Baz; 

pub(super) struct Foo {
    baz: Baz, // Can use private definitions from any of its ancestors
}

// Illegal: cannot leak a definition with smaller scope
// (i.e. [private] < pub(super) < pub(package) < pub)
pub foo_from_bar(bar: Bar) -> Foo {
    bar.0
}
```

file: `bar.mun`
```mun
use package::Foo;

pub(package) Bar(Foo);
```




Discussion / hints:
A lot of work has already been done on the HIR side in #283 to support resolution of paths between different files. And what a file exposes. 

Next steps on the HIR side are:
- [ ] Implement `use` statement in the parser
- [ ] Implement resolving of all `use` statements and storing them in the PackageDef.

Finally we need a way to generate multiple assemblies and a way to reference one from another. 
I've started work on this!

Fix patch:
diff --git a/crates/mun_hir/Cargo.toml b/crates/mun_hir/Cargo.toml
index 2d7343555..ff87da21c 100644
--- a/crates/mun_hir/Cargo.toml
+++ b/crates/mun_hir/Cargo.toml
@@ -23,9 +23,11 @@ once_cell = "1.4.0"
 ena = "0.14"
 drop_bomb = "0.1.4"
 either = "1.5.3"
-itertools = "0.9.0"
+itertools = "0.10.0"
+smallvec = "1.4.2"
 
 [dev-dependencies]
 insta = "0.16"
+text_trees = "0.1.2"
 parking_lot = "0.10"
 mun_test = { version = "=0.1.0", path = "../mun_test" }
diff --git a/crates/mun_hir/src/code_model/module.rs b/crates/mun_hir/src/code_model/module.rs
index 88e7c1a4d..6f76eb798 100644
--- a/crates/mun_hir/src/code_model/module.rs
+++ b/crates/mun_hir/src/code_model/module.rs
@@ -1,7 +1,7 @@
 use super::{Function, Package, Struct, TypeAlias};
 use crate::ids::{ItemDefinitionId, ModuleId};
 use crate::primitive_type::PrimitiveType;
-use crate::{DiagnosticSink, FileId, HirDatabase};
+use crate::{DiagnosticSink, FileId, HirDatabase, Name};
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct Module {
@@ -23,6 +23,49 @@ impl Module {
             .find(|m| m.file_id(db) == Some(file))
     }
 
+    /// Returns the parent module of this module.
+    pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
+        let module_tree = db.module_tree(self.id.package);
+        let parent_id = module_tree[self.id.local_id].parent?;
+        Some(Module {
+            id: ModuleId {
+                package: self.id.package,
+                local_id: parent_id,
+            },
+        })
+    }
+
+    /// Returns the name of this module or None if this is the root module
+    pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
+        let module_tree = db.module_tree(self.id.package);
+        let parent = module_tree[self.id.local_id].parent?;
+        module_tree[parent]
+            .children
+            .iter()
+            .find_map(|(name, module_id)| {
+                if *module_id == self.id.local_id {
+                    Some(name.clone())
+                } else {
+                    None
+                }
+            })
+    }
+
+    /// Returns all the child modules of this module
+    pub fn children(self, db: &dyn HirDatabase) -> Vec<Module> {
+        let module_tree = db.module_tree(self.id.package);
+        module_tree[self.id.local_id]
+            .children
+            .iter()
+            .map(|(_, local_id)| Module {
+                id: ModuleId {
+                    package: self.id.package,
+                    local_id: *local_id,
+                },
+            })
+            .collect()
+    }
+
     /// Returns the file that defines the module
     pub fn file_id(self, db: &dyn HirDatabase) -> Option<FileId> {
         db.module_tree(self.id.package).modules[self.id.local_id].file
@@ -39,12 +82,19 @@ impl Module {
 
     /// Iterate over all diagnostics from this `Module` by placing them in the `sink`
     pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
+        // Add diagnostics from the package definitions
+        let package_defs = db.package_defs(self.id.package);
+        package_defs.add_diagnostics(db.upcast(), self.id.local_id, sink);
+
+        // Add diagnostics from the item tree
         if let Some(file_id) = self.file_id(db) {
             let item_tree = db.item_tree(file_id);
             for diagnostics in item_tree.diagnostics.iter() {
                 diagnostics.add_to(db, &*item_tree, sink);
             }
         }
+
+        // Add diagnostics from the items
         for decl in self.declarations(db) {
             match decl {
                 ModuleDef::Function(f) => f.diagnostics(db, sink),
@@ -54,6 +104,17 @@ impl Module {
             }
         }
     }
+
+    /// Returns the path from this module to the root module
+    pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
+        let mut res = vec![self];
+        let mut curr = self;
+        while let Some(next) = curr.parent(db) {
+            res.push(next);
+            curr = next
+        }
+        res
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/mun_hir/src/diagnostics.rs b/crates/mun_hir/src/diagnostics.rs
index 70e787b73..89c929083 100644
--- a/crates/mun_hir/src/diagnostics.rs
+++ b/crates/mun_hir/src/diagnostics.rs
@@ -746,3 +746,41 @@ impl Diagnostic for FreeTypeAliasWithoutTypeRef {
         self
     }
 }
+
+#[derive(Debug)]
+pub struct UnresolvedImport {
+    pub use_tree: InFile<AstPtr<ast::UseTree>>,
+}
+
+impl Diagnostic for UnresolvedImport {
+    fn message(&self) -> String {
+        "unresolved import".to_string()
+    }
+
+    fn source(&self) -> InFile<SyntaxNodePtr> {
+        self.use_tree.map(Into::into)
+    }
+
+    fn as_any(&self) -> &(dyn Any + Send) {
+        self
+    }
+}
+
+#[derive(Debug)]
+pub struct ImportDuplicateDefinition {
+    pub use_tree: InFile<AstPtr<ast::UseTree>>,
+}
+
+impl Diagnostic for ImportDuplicateDefinition {
+    fn message(&self) -> String {
+        "a second item with the same name imported. Try to use an alias.".to_string()
+    }
+
+    fn source(&self) -> InFile<SyntaxNodePtr> {
+        self.use_tree.map(Into::into)
+    }
+
+    fn as_any(&self) -> &(dyn Any + Send) {
+        self
+    }
+}
diff --git a/crates/mun_hir/src/item_scope.rs b/crates/mun_hir/src/item_scope.rs
index f3dbe6bf4..a25bde428 100644
--- a/crates/mun_hir/src/item_scope.rs
+++ b/crates/mun_hir/src/item_scope.rs
@@ -1,7 +1,32 @@
+use crate::module_tree::LocalModuleId;
 use crate::primitive_type::PrimitiveType;
 use crate::{ids::ItemDefinitionId, visibility::Visibility, Name, PerNs};
 use once_cell::sync::Lazy;
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
+use std::collections::hash_map::Entry;
+
+/// Defines the type of import. An import can either be a named import (e.g. `use foo::Bar`) or a
+/// wildcard import (e.g. `use foo::*`)
+#[derive(Copy, Clone)]
+pub(crate) enum ImportType {
+    /// A wildcard import statement (`use foo::*`)
+    Glob,
+
+    /// A named import statement (`use foo::Bar`)
+    Named,
+}
+
+/// A struct that holds information on which name was imported via a glob import. This information
+/// is used by the `PackageDef` collector to keep track of duplicates so that this doesn't result in
+/// a duplicate name error; e.g. :
+/// ```mun
+/// use foo::{Foo, *};
+/// ```
+#[derive(Debug, Default)]
+pub struct PerNsGlobImports {
+    types: FxHashSet<(LocalModuleId, Name)>,
+    values: FxHashSet<(LocalModuleId, Name)>,
+}
 
 /// Holds all items that are visible from an item as well as by which name and under which
 /// visibility they are accessible.
@@ -17,6 +42,16 @@ pub struct ItemScope {
     defs: Vec<ItemDefinitionId>,
 }
 
+/// A struct that is returned from `add_resolution_from_import`.
+#[derive(Debug)]
+pub(crate) struct AddResolutionFromImportResult {
+    /// Whether or not adding the resolution changed the item scope
+    pub changed: bool,
+
+    /// Whether or not adding the resolution will overwrite an existing entry
+    pub duplicate: bool,
+}
+
 pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs<(ItemDefinitionId, Visibility)>>> =
     Lazy::new(|| {
         PrimitiveType::ALL
@@ -31,6 +66,14 @@ pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs<(ItemDefinitionId, V
     });
 
 impl ItemScope {
+    /// Returns all the entries in the scope
+    pub fn entries(
+        &self,
+    ) -> impl Iterator<Item = (&'_ Name, PerNs<(ItemDefinitionId, Visibility)>)> + '_ {
+        let keys: FxHashSet<_> = self.types.keys().chain(self.values.keys()).collect();
+        keys.into_iter().map(move |name| (name, self.get(name)))
+    }
+
     /// Returns an iterator over all declarations with this scope
     pub fn declarations(&self) -> impl Iterator<Item = ItemDefinitionId> + '_ {
         self.defs.iter().copied()
@@ -41,18 +84,110 @@ impl ItemScope {
         self.defs.push(def)
     }
 
-    /// Adds a named item resolution into the scope
+    /// Adds a named item resolution into the scope. Returns true if adding the resolution changes
+    /// the scope.
     pub(crate) fn add_resolution(
         &mut self,
         name: Name,
         def: PerNs<(ItemDefinitionId, Visibility)>,
-    ) {
+    ) -> bool {
+        let mut changed = false;
         if let Some((types, visibility)) = def.types {
-            self.types.insert(name.clone(), (types, visibility));
+            self.types.entry(name.clone()).or_insert_with(|| {
+                changed = true;
+                (types, visibility)
+            });
         }
         if let Some((values, visibility)) = def.values {
-            self.values.insert(name, (values, visibility));
+            self.values.entry(name).or_insert_with(|| {
+                changed = true;
+                (values, visibility)
+            });
         }
+
+        changed
+    }
+
+    /// Adds a named item resolution into the scope which is the result of a `use` statement.
+    /// Returns true if adding the resolution changes the scope.
+    pub(crate) fn add_resolution_from_import(
+        &mut self,
+        glob_imports: &mut PerNsGlobImports,
+        lookup: (LocalModuleId, Name),
+        def: PerNs<(ItemDefinitionId, Visibility)>,
+        def_import_type: ImportType,
+    ) -> AddResolutionFromImportResult {
+        let mut changed = false;
+        let mut duplicate = false;
+
+        macro_rules! check_changed {
+            (
+                $changed:ident,
+                ( $this:ident / $def:ident ) . $field:ident,
+                $glob_imports:ident [ $lookup:ident ],
+                $def_import_type:ident
+            ) => {{
+                let existing = $this.$field.entry($lookup.1.clone());
+                match (existing, $def.$field) {
+                    // The name doesnt exist yet in the scope
+                    (Entry::Vacant(entry), Some(_)) => {
+                        match $def_import_type {
+                            // If this is a wildcard import, add it to the list of items we imported
+                            // via a glob. This information is stored so if we later explicitly
+                            // import this type or value, it doesn't cause a conflict.
+                            ImportType::Glob => {
+                                $glob_imports.$field.insert($lookup.clone());
+                            }
+                            // If this is *not* a wildcard import, remove it from the list of items
+                            // imported via a glob.
+                            ImportType::Named => {
+                                $glob_imports.$field.remove(&$lookup);
+                            }
+                        }
+
+                        if let Some(fld) = $def.$field {
+                            entry.insert(fld);
+                        }
+                        $changed = true;
+                    }
+                    // If there is already an entry for this resolution, but it came from a glob
+                    // pattern, overwrite it and mark it as not included from the glob pattern.
+                    (Entry::Occupied(mut entry), Some(_))
+                        if $glob_imports.$field.contains(&$lookup)
+                            && matches!($def_import_type, ImportType::Named) =>
+                    {
+                        $glob_imports.$field.remove(&$lookup);
+                        if let Some(fld) = $def.$field {
+                            entry.insert(fld);
+                        }
+                        $changed = true;
+                    }
+                    (Entry::Occupied(_), Some(_)) => {
+                        let is_previous_from_glob = $glob_imports.$field.contains(&$lookup);
+                        let is_explicit_import = matches!($def_import_type, ImportType::Named);
+                        if is_explicit_import && !is_previous_from_glob {
+                            duplicate = true;
+                        }
+                    }
+                    _ => {}
+                }
+            }};
+        }
+
+        check_changed!(
+            changed,
+            (self / def).types,
+            glob_imports[lookup],
+            def_import_type
+        );
+        check_changed!(
+            changed,
+            (self / def).values,
+            glob_imports[lookup],
+            def_import_type
+        );
+
+        AddResolutionFromImportResult { changed, duplicate }
     }
 
     /// Gets a name from the current module scope
diff --git a/crates/mun_hir/src/item_tree.rs b/crates/mun_hir/src/item_tree.rs
index 31faed7e5..433334ee4 100644
--- a/crates/mun_hir/src/item_tree.rs
+++ b/crates/mun_hir/src/item_tree.rs
@@ -2,12 +2,13 @@ mod lower;
 #[cfg(test)]
 mod tests;
 
+use crate::path::ImportAlias;
 use crate::{
     arena::{Arena, Idx},
     source_id::FileAstId,
     type_ref::TypeRef,
     visibility::RawVisibility,
-    DefDatabase, FileId, InFile, Name,
+    DefDatabase, FileId, InFile, Name, Path,
 };
 use mun_syntax::{ast, AstNode};
 use std::{
@@ -45,6 +46,8 @@ impl fmt::Debug for RawVisibilityId {
 }
 
 /// An `ItemTree` is a derivative of an AST that only contains the items defined in the AST.
+///
+/// Examples of items are: functions, structs, use statements.
 #[derive(Debug, Eq, PartialEq)]
 pub struct ItemTree {
     file_id: FileId,
@@ -102,6 +105,7 @@ impl ItemVisibilities {
 
 #[derive(Default, Debug, Eq, PartialEq)]
 struct ItemTreeData {
+    imports: Arena<Import>,
     functions: Arena<Function>,
     structs: Arena<Struct>,
     fields: Arena<Field>,
@@ -222,6 +226,7 @@ mod_items! {
     Function in functions -> ast::FunctionDef,
     Struct in structs -> ast::StructDef,
     TypeAlias in type_aliases -> ast::TypeAliasDef,
+    Import in imports -> ast::Use,
 }
 
 macro_rules! impl_index {
@@ -265,6 +270,30 @@ impl<N: ItemTreeNode> Index<LocalItemTreeId<N>> for ItemTree {
     }
 }
 
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Import {
+    /// The path of the import (e.g. foo::Bar). Note that group imports have been desugared, each
+    /// item in the import tree is a seperate import.
+    pub path: Path,
+
+    /// An optional alias for this import statement (e.g. `use foo as bar`)
+    pub alias: Option<ImportAlias>,
+
+    /// The visibility of the import statement as seen from the file that contains the import
+    /// statement.
+    pub visibility: RawVisibilityId,
+
+    /// Whether or not this is a wildcard import.
+    pub is_glob: bool,
+
+    /// AST Id of the `use` item this import was derived from. Note that multiple `Import`s can map
+    /// to the same `use` item.
+    pub ast_id: FileAstId<ast::Use>,
+
+    /// Index of this `Import` when the containing `Use` is visited with `Path::expand_use_item`.
+    pub index: usize,
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Function {
     pub name: Name,
@@ -414,6 +443,9 @@ mod diagnostics {
                     ModItem::TypeAlias(item) => {
                         SyntaxNodePtr::new(item_tree.source(db, item).syntax())
                     }
+                    ModItem::Import(item) => {
+                        SyntaxNodePtr::new(item_tree.source(db, item).syntax())
+                    }
                 }
             }
         }
diff --git a/crates/mun_hir/src/item_tree/lower.rs b/crates/mun_hir/src/item_tree/lower.rs
index 2b54a7464..b1198883c 100644
--- a/crates/mun_hir/src/item_tree/lower.rs
+++ b/crates/mun_hir/src/item_tree/lower.rs
@@ -4,20 +4,33 @@ use super::{
     diagnostics, Field, Fields, Function, IdRange, ItemTree, ItemTreeData, ItemTreeNode,
     LocalItemTreeId, ModItem, RawVisibilityId, Struct, StructDefKind, TypeAlias,
 };
+use crate::item_tree::Import;
 use crate::{
     arena::{Idx, RawId},
     name::AsName,
     source_id::AstIdMap,
     type_ref::TypeRef,
     visibility::RawVisibility,
-    DefDatabase, FileId, Name,
+    DefDatabase, FileId, InFile, Name, Path,
 };
 use mun_syntax::{
     ast,
     ast::{ExternOwner, ModuleItemOwner, NameOwner, StructKind, TypeAscriptionOwner},
 };
+use smallvec::SmallVec;
 use std::{collections::HashMap, convert::TryInto, marker::PhantomData, sync::Arc};
 
+struct ModItems(SmallVec<[ModItem; 1]>);
+
+impl<T> From<T> for ModItems
+where
+    T: Into<ModItem>,
+{
+    fn from(t: T) -> Self {
+        ModItems(SmallVec::from_buf([t.into(); 1]))
+    }
+}
+
 impl<N: ItemTreeNode> From<Idx<N>> for LocalItemTreeId<N> {
     fn from(index: Idx<N>) -> Self {
         LocalItemTreeId {
@@ -50,25 +63,29 @@ impl Context {
         let top_level = item_owner
             .items()
             .flat_map(|item| self.lower_mod_item(&item))
+            .flat_map(|items| items.0)
             .collect::<Vec<_>>();
 
         // Check duplicates
         let mut set = HashMap::<Name, &ModItem>::new();
         for item in top_level.iter() {
             let name = match item {
-                ModItem::Function(item) => &self.data.functions[item.index].name,
-                ModItem::Struct(item) => &self.data.structs[item.index].name,
-                ModItem::TypeAlias(item) => &self.data.type_aliases[item.index].name,
+                ModItem::Function(item) => Some(&self.data.functions[item.index].name),
+                ModItem::Struct(item) => Some(&self.data.structs[item.index].name),
+                ModItem::TypeAlias(item) => Some(&self.data.type_aliases[item.index].name),
+                ModItem::Import(_) => None,
             };
-            if let Some(first_item) = set.get(&name) {
-                self.diagnostics
-                    .push(diagnostics::ItemTreeDiagnostic::DuplicateDefinition {
-                        name: name.clone(),
-                        first: **first_item,
-                        second: *item,
-                    })
-            } else {
-                set.insert(name.clone(), item);
+            if let Some(name) = name {
+                if let Some(first_item) = set.get(&name) {
+                    self.diagnostics
+                        .push(diagnostics::ItemTreeDiagnostic::DuplicateDefinition {
+                            name: name.clone(),
+                            first: **first_item,
+                            second: *item,
+                        })
+                } else {
+                    set.insert(name.clone(), item);
+                }
             }
         }
 
@@ -81,14 +98,49 @@ impl Context {
     }
 
     /// Lowers a single module item
-    fn lower_mod_item(&mut self, item: &ast::ModuleItem) -> Option<ModItem> {
+    fn lower_mod_item(&mut self, item: &ast::ModuleItem) -> Option<ModItems> {
         match item.kind() {
             ast::ModuleItemKind::FunctionDef(ast) => self.lower_function(&ast).map(Into::into),
             ast::ModuleItemKind::StructDef(ast) => self.lower_struct(&ast).map(Into::into),
             ast::ModuleItemKind::TypeAliasDef(ast) => self.lower_type_alias(&ast).map(Into::into),
+            ast::ModuleItemKind::Use(ast) => Some(ModItems(
+                self.lower_use(&ast)
+                    .into_iter()
+                    .map(Into::into)
+                    .collect::<SmallVec<_>>(),
+            )),
         }
     }
 
+    /// Lowers a `use` statement
+    fn lower_use(&mut self, use_item: &ast::Use) -> Vec<LocalItemTreeId<Import>> {
+        let visibility = self.lower_visibility(use_item);
+        let ast_id = self.source_ast_id_map.ast_id(use_item);
+
+        // Every use item can expand to many `Import`s.
+        let mut imports = Vec::new();
+        let tree = &mut self.data;
+        Path::expand_use_item(
+            InFile::new(self.file, use_item.clone()),
+            |path, _use_tree, is_glob, alias| {
+                imports.push(
+                    tree.imports
+                        .alloc(Import {
+                            path,
+                            alias,
+                            visibility,
+                            is_glob,
+                            ast_id,
+                            index: imports.len(),
+                        })
+                        .into(),
+                );
+            },
+        );
+
+        imports
+    }
+
     /// Lowers a function
     fn lower_function(&mut self, func: &ast::FunctionDef) -> Option<LocalItemTreeId<Function>> {
         let name = func.name()?.as_name();
diff --git a/crates/mun_hir/src/lib.rs b/crates/mun_hir/src/lib.rs
index b440c3fb0..20a64ad86 100644
--- a/crates/mun_hir/src/lib.rs
+++ b/crates/mun_hir/src/lib.rs
@@ -22,7 +22,7 @@ mod item_tree;
 pub mod line_index;
 mod module_tree;
 mod name;
-mod name_resolution;
+pub(crate) mod name_resolution;
 mod path;
 mod primitive_type;
 mod resolve;
diff --git a/crates/mun_hir/src/module_tree.rs b/crates/mun_hir/src/module_tree.rs
index a18bec0f4..480ca50f3 100644
--- a/crates/mun_hir/src/module_tree.rs
+++ b/crates/mun_hir/src/module_tree.rs
@@ -10,7 +10,16 @@ use paths::RelativePath;
 use rustc_hash::FxHashMap;
 use std::sync::Arc;
 
-/// Represents the tree of modules in a package.
+/// Represents the tree of modules of a package.
+///
+/// The `ModuleTree` is built by looking at all the source files of the source root of a package and
+/// creating a tree based on their relative paths. See the [`ModuleTree::module_tree_query`] method.
+/// When constructing the `ModuleTree` extra empty modules may be added for missing files. For
+/// instance for the relative path `foo/bar/baz.mun`, besides the module `foo::bar::baz` the modules
+/// `foo`, `foo::bar` get created along the way.
+///
+/// A `ModuleTree` represent the inner connections between files. It can be used to query the
+/// shortest path for use declarations
 #[derive(Debug, PartialEq, Eq)]
 pub struct ModuleTree {
     pub root: LocalModuleId,
@@ -108,8 +117,8 @@ impl ModuleTree {
         })
     }
 
-    /// Given a `RawVisibility` which describes the visibility of an item relative to a module into
-    /// a `Visibility` which describes the absolute visibility within the module tree.
+    /// Converts a `RawVisibility` which describes the visibility of an item relative to a module
+    /// into a `Visibility` which describes the absolute visibility within the module tree.
     pub(crate) fn resolve_visibility(
         &self,
         _db: &dyn DefDatabase,
diff --git a/crates/mun_hir/src/name_resolution.rs b/crates/mun_hir/src/name_resolution.rs
index 9399b58f9..74cef1508 100644
--- a/crates/mun_hir/src/name_resolution.rs
+++ b/crates/mun_hir/src/name_resolution.rs
@@ -2,3 +2,4 @@ mod path_resolution;
 mod per_ns;
 
 pub use self::per_ns::{Namespace, PerNs};
+pub use path_resolution::ReachedFixedPoint;
diff --git a/crates/mun_hir/src/name_resolution/path_resolution.rs b/crates/mun_hir/src/name_resolution/path_resolution.rs
index 5c9932351..c01d9937c 100644
--- a/crates/mun_hir/src/name_resolution/path_resolution.rs
+++ b/crates/mun_hir/src/name_resolution/path_resolution.rs
@@ -7,8 +7,11 @@ use crate::{
 };
 use std::iter::successors;
 
+/// Indicates whether or not any newly resolved import statements will actually change the outcome
+/// of an operation. This is useful to know if more iterations of an algorithm might be required, or
+/// whether its hopeless.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(super) enum ReachedFixedPoint {
+pub enum ReachedFixedPoint {
     Yes,
     No,
 }
@@ -16,11 +19,11 @@ pub(super) enum ReachedFixedPoint {
 /// Contains the result of resolving a path. It contains how far the path was able to be resolved
 /// as well as the resolved values or types so far.
 #[derive(Debug, Clone)]
-pub(super) struct ResolvePathResult {
-    pub(super) resolved_def: PerNs<(ItemDefinitionId, Visibility)>,
-    pub(super) segment_index: Option<usize>,
-    pub(super) reached_fixedpoint: ReachedFixedPoint,
-    pub(super) package: Option<PackageId>,
+pub(crate) struct ResolvePathResult {
+    pub(crate) resolved_def: PerNs<(ItemDefinitionId, Visibility)>,
+    pub(crate) segment_index: Option<usize>,
+    pub(crate) reached_fixedpoint: ReachedFixedPoint,
+    pub(crate) package: Option<PackageId>,
 }
 
 impl ResolvePathResult {
@@ -74,7 +77,7 @@ impl PackageDefs {
     /// Resolves the specified `path` from within the specified `module`. Also returns whether or
     /// not additions to the `PackageDef` would change the result or whether a fixed point has been
     /// reached. This is useful when resolving all imports.
-    pub(super) fn resolve_path_with_fixedpoint(
+    pub(crate) fn resolve_path_with_fixedpoint(
         &self,
         db: &dyn DefDatabase,
         original_module: LocalModuleId,
diff --git a/crates/mun_hir/src/package_defs.rs b/crates/mun_hir/src/package_defs.rs
index c45effca4..ee9efddfd 100644
--- a/crates/mun_hir/src/package_defs.rs
+++ b/crates/mun_hir/src/package_defs.rs
@@ -1,8 +1,10 @@
 mod collector;
+#[cfg(test)]
+mod tests;
 
 use crate::{
     arena::map::ArenaMap, item_scope::ItemScope, module_tree::LocalModuleId,
-    module_tree::ModuleTree, DefDatabase, PackageId,
+    module_tree::ModuleTree, DefDatabase, DiagnosticSink, PackageId,
 };
 use std::{ops::Index, sync::Arc};
 
@@ -11,15 +13,29 @@ use std::{ops::Index, sync::Arc};
 pub struct PackageDefs {
     pub modules: ArenaMap<LocalModuleId, ItemScope>,
     pub module_tree: Arc<ModuleTree>,
+    diagnostics: Vec<diagnostics::DefDiagnostic>,
 }
 
 impl PackageDefs {
+    /// Constructs a `PackageDefs` for the specified `package` with the data from the `db`.
     pub(crate) fn package_def_map_query(
         db: &dyn DefDatabase,
         package: PackageId,
     ) -> Arc<PackageDefs> {
         Arc::new(collector::collect(db, package))
     }
+
+    /// Adds all the diagnostics for the specified `module` to the `sink`.
+    pub fn add_diagnostics(
+        &self,
+        db: &dyn DefDatabase,
+        module: LocalModuleId,
+        sink: &mut DiagnosticSink,
+    ) {
+        for diagnostic in self.diagnostics.iter() {
+            diagnostic.add_to(db, module, sink)
+        }
+    }
 }
 
 impl Index<LocalModuleId> for PackageDefs {
@@ -29,3 +45,100 @@ impl Index<LocalModuleId> for PackageDefs {
         &self.modules[index]
     }
 }
+
+mod diagnostics {
+    use crate::diagnostics::{ImportDuplicateDefinition, UnresolvedImport};
+    use crate::{
+        module_tree::LocalModuleId, source_id::AstId, AstDatabase, DefDatabase, DiagnosticSink,
+        InFile, Path,
+    };
+    use mun_syntax::ast::Use;
+    use mun_syntax::{ast, AstPtr};
+
+    /// A type of diagnostic that may be emitted during resolving all package definitions.
+    #[derive(Debug, PartialEq, Eq)]
+    enum DiagnosticKind {
+        UnresolvedImport { ast: AstId<ast::Use>, index: usize },
+        DuplicateImport { ast: AstId<ast::Use>, index: usize },
+    }
+
+    /// A diagnostic that may be emitted during resolving all package definitions.
+    #[derive(Debug, PartialEq, Eq)]
+    pub(super) struct DefDiagnostic {
+        /// The module that contains the diagnostic
+        in_module: LocalModuleId,
+
+        /// The type of diagnostic
+        kind: DiagnosticKind,
+    }
+
+    impl DefDiagnostic {
+        /// Constructs a new `DefDiagnostic` which indicates that an import could not be resolved.
+        pub(super) fn unresolved_import(
+            container: LocalModuleId,
+            ast: AstId<ast::Use>,
+            index: usize,
+        ) -> Self {
+            Self {
+                in_module: container,
+                kind: DiagnosticKind::UnresolvedImport { ast, index },
+            }
+        }
+
+        /// Constructs a new `DefDiagnostic` which indicates that an import names a duplication.
+        pub(super) fn duplicate_import(
+            container: LocalModuleId,
+            ast: AstId<ast::Use>,
+            index: usize,
+        ) -> Self {
+            Self {
+                in_module: container,
+                kind: DiagnosticKind::DuplicateImport { ast, index },
+            }
+        }
+
+        pub(super) fn add_to(
+            &self,
+            db: &dyn DefDatabase,
+            target_module: LocalModuleId,
+            sink: &mut DiagnosticSink,
+        ) {
+            if self.in_module != target_module {
+                return;
+            }
+
+            match &self.kind {
+                DiagnosticKind::UnresolvedImport { ast, index } => {
+                    if let Some(use_tree) = use_tree_ptr_from_ast(db.upcast(), ast, *index) {
+                        sink.push(UnresolvedImport { use_tree });
+                    }
+                }
+                DiagnosticKind::DuplicateImport { ast, index } => {
+                    if let Some(use_tree) = use_tree_ptr_from_ast(db.upcast(), ast, *index) {
+                        sink.push(ImportDuplicateDefinition { use_tree });
+                    }
+                }
+            }
+
+            fn use_tree_ptr_from_ast(
+                db: &dyn AstDatabase,
+                ast: &AstId<Use>,
+                index: usize,
+            ) -> Option<InFile<AstPtr<ast::UseTree>>> {
+                let use_item = ast.to_node(db);
+                let mut cur = 0;
+                let mut tree = None;
+                Path::expand_use_item(
+                    InFile::new(ast.file_id, use_item),
+                    |_path, use_tree, _is_glob, _alias| {
+                        if cur == index {
+                            tree = Some(use_tree.clone())
+                        }
+                        cur += 1;
+                    },
+                );
+                tree.map(|t| InFile::new(ast.file_id, AstPtr::new(&t)))
+            }
+        }
+    }
+}
diff --git a/crates/mun_hir/src/package_defs/collector.rs b/crates/mun_hir/src/package_defs/collector.rs
index dad03ad4a..7d136b4a0 100644
--- a/crates/mun_hir/src/package_defs/collector.rs
+++ b/crates/mun_hir/src/package_defs/collector.rs
@@ -1,23 +1,114 @@
 use super::PackageDefs;
 use crate::{
-    arena::map::ArenaMap,
-    ids::{FunctionLoc, Intern, ItemDefinitionId, ModuleId, StructLoc, TypeAliasLoc},
-    item_scope::ItemScope,
+    ids::ItemDefinitionId,
+    ids::{FunctionLoc, Intern, StructLoc, TypeAliasLoc},
+    item_scope::ImportType,
+    item_scope::{ItemScope, PerNsGlobImports},
     item_tree::{
-        Function, ItemTree, ItemTreeId, LocalItemTreeId, ModItem, Struct, StructDefKind, TypeAlias,
+        self, Function, ItemTree, ItemTreeId, LocalItemTreeId, ModItem, Struct, StructDefKind,
+        TypeAlias,
     },
-    module_tree::{LocalModuleId, ModuleTree},
+    module_tree::LocalModuleId,
+    name_resolution::ReachedFixedPoint,
+    package_defs::diagnostics::DefDiagnostic,
+    path::ImportAlias,
     visibility::RawVisibility,
-    DefDatabase, FileId, Name, PackageId, PerNs, Visibility,
+    DefDatabase, FileId, InFile, ModuleId, Name, PackageId, Path, PerNs, Visibility,
 };
-use std::sync::Arc;
+use rustc_hash::FxHashMap;
+
+/// Result of resolving an import statement
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+enum PartiallyResolvedImport {
+    /// None of the namespaces are resolved
+    Unresolved,
+    /// One of namespaces is resolved.
+    Indeterminate(PerNs<(ItemDefinitionId, Visibility)>),
+    /// All namespaces are resolved, OR it came from another crate
+    Resolved(PerNs<(ItemDefinitionId, Visibility)>),
+}
+
+/// The result of an import directive
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct ImportResolution {
+    /// The name to expose the resolution as
+    name: Option<Name>,
+
+    /// The resolution itself
+    resolution: PerNs<(ItemDefinitionId, Visibility)>,
+}
+
+impl PartiallyResolvedImport {
+    fn namespaces(&self) -> PerNs<(ItemDefinitionId, Visibility)> {
+        match self {
+            PartiallyResolvedImport::Unresolved => PerNs::none(),
+            PartiallyResolvedImport::Indeterminate(ns) => *ns,
+            PartiallyResolvedImport::Resolved(ns) => *ns,
+        }
+    }
+}
+
+/// Definition of a single import statement
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct Import {
+    /// The path of the import (e.g. foo::Bar). Note that group imports have been desugared, each
+    /// item in the import tree is a seperate import.
+    pub path: Path,
+
+    /// The alias for this import statement
+    pub alias: Option<ImportAlias>,
+
+    /// The visibility of the import in the file the import statement resides in
+    pub visibility: RawVisibility,
+
+    /// Whether or not this is a * import.
+    pub is_glob: bool,
+
+    /// The original location of the import
+    source: ItemTreeId<item_tree::Import>,
+}
+
+impl Import {
+    /// Constructs an `Import` from a `use` statement in an `ItemTree`.
+    fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self {
+        let it = &tree[id.value];
+        let visibility = &tree[it.visibility];
+        Self {
+            path: it.path.clone(),
+            alias: it.alias.clone(),
+            visibility: visibility.clone(),
+            is_glob: it.is_glob,
+            source: id,
+        }
+    }
+}
+
+/// A struct that keeps track of the state of an import directive.
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct ImportDirective {
+    /// The module that defines the import statement
+    module_id: LocalModuleId,
+
+    /// Information about the import statement.
+    import: Import,
+
+    /// The current status of the import.
+    status: PartiallyResolvedImport,
+}
 
 pub(super) fn collect(db: &dyn DefDatabase, package_id: PackageId) -> PackageDefs {
     let mut collector = DefCollector {
         db,
         package_id,
-        modules: Default::default(),
-        module_tree: db.module_tree(package_id),
+        package_defs: PackageDefs {
+            modules: Default::default(),
+            module_tree: db.module_tree(package_id),
+            diagnostics: Default::default(),
+        },
+        unresolved_imports: Default::default(),
+        resolved_imports: Default::default(),
+        glob_imports: Default::default(),
+        from_glob_import: Default::default(),
     };
     collector.collect();
     collector.finish()
@@ -27,26 +118,86 @@ pub(super) fn collect(db: &dyn DefDatabase, package_id: PackageId) -> PackageDef
 struct DefCollector<'db> {
     db: &'db dyn DefDatabase,
     package_id: PackageId,
-    modules: ArenaMap<LocalModuleId, ItemScope>,
-    module_tree: Arc<ModuleTree>,
+    package_defs: PackageDefs,
+    unresolved_imports: Vec<ImportDirective>,
+    resolved_imports: Vec<ImportDirective>,
+
+    /// A mapping from local module to wildcard imports of other modules
+    glob_imports:
+        FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, ItemTreeId<item_tree::Import>)>>,
+
+    /// A list of all items that have been imported via a wildcard
+    from_glob_import: PerNsGlobImports,
 }
 
 impl<'db> DefCollector<'db> {
     /// Collects all information and stores it in the instance
     fn collect(&mut self) {
         // Collect all definitions in each module
-        let module_tree = self.module_tree.clone();
+        let module_tree = self.package_defs.module_tree.clone();
+
+        // Start by collecting the definitions from all modules. This ensures that, for every module,
+        // all local definitions are accessible. This is the starting point for the import
+        // resolution.
+        collect_modules_recursive(self, module_tree.root, None);
+
+        // Now, as long as we have unresolved imports, try to resolve them, or part of them.
+        while !self.unresolved_imports.is_empty() {
+            // Keep track of whether we were able to resolve anything
+            let mut resolved_something = false;
+
+            // Get all the current unresolved import directives
+            let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
+
+            // For each import, try to resolve it with the current state.
+            for mut directive in imports {
+                // Resolve the import
+                directive.status = self.resolve_import(directive.module_id, &directive.import);
 
+                // Check the status of the import, if the import is still considered unresolved, try
+                // again in the next round.
+                match directive.status {
+                    PartiallyResolvedImport::Indeterminate(_) => {
+                        self.record_resolved_import(&directive);
+                        // FIXME: To avoid performance regression, we consider an import resolved
+                        // if it is indeterminate (i.e not all namespace resolved). This might not
+                        // completely resolve correctly in the future if we can have values and
+                        // types with the same name.
+                        self.resolved_imports.push(directive);
+                        resolved_something = true;
+                    }
+                    PartiallyResolvedImport::Resolved(_) => {
+                        self.record_resolved_import(&directive);
+                        self.resolved_imports.push(directive);
+                        resolved_something = true;
+                    }
+                    PartiallyResolvedImport::Unresolved => {
+                        self.unresolved_imports.push(directive);
+                    }
+                }
+            }
+
+            // If nothing actually changed up to this point, stop resolving.
+            if !resolved_something {
+                break;
+            }
+        }
+
+        /// Recursively iterate over all modules in the `ModuleTree` and add them and their
+        /// definitions to their corresponding `ItemScope`.
         fn collect_modules_recursive(
             collector: &mut DefCollector,
             module_id: LocalModuleId,
             parent: Option<(Name, LocalModuleId)>,
         ) {
             // Insert an empty item scope for this module, this will be filled in.
-            collector.modules.insert(module_id, ItemScope::default());
+            collector
+                .package_defs
+                .modules
+                .insert(module_id, ItemScope::default());
 
             // If there is a file associated with the module, collect all definitions from it
-            let module_data = &collector.module_tree[module_id];
+            let module_data = &collector.package_defs.module_tree[module_id];
             if let Some(file_id) = module_data.file {
                 let item_tree = collector.db.item_tree(file_id);
                 let mut mod_collector = ModCollectorContext {
@@ -61,7 +212,7 @@ impl<'db> DefCollector<'db> {
 
             // Insert this module into the scope of the parent
             if let Some((name, parent)) = parent {
-                collector.modules[parent].add_resolution(
+                collector.package_defs.modules[parent].add_resolution(
                     name,
                     PerNs::from_definition(
                         ModuleId {
@@ -76,7 +227,7 @@ impl<'db> DefCollector<'db> {
             }
 
             // Iterate over all children
-            let child_module_ids = collector.module_tree[module_id]
+            let child_module_ids = collector.package_defs.module_tree[module_id]
                 .children
                 .iter()
                 .map(|(name, local_id)| (name.clone(), *local_id))
@@ -85,16 +236,250 @@ impl<'db> DefCollector<'db> {
                 collect_modules_recursive(collector, child_module_id, Some((name, module_id)));
             }
         };
+    }
 
-        collect_modules_recursive(self, module_tree.root, None);
+    /// Given an import, try to resolve it.
+    fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartiallyResolvedImport {
+        let res = self
+            .package_defs
+            .resolve_path_with_fixedpoint(self.db, module_id, &import.path);
+
+        let def = res.resolved_def;
+        if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
+            return PartiallyResolvedImport::Unresolved;
+        }
+
+        if let Some(package) = res.package {
+            if package != self.package_defs.module_tree.package {
+                return PartiallyResolvedImport::Resolved(def);
+            }
+        }
+
+        // Check whether all namespaces have been resolved
+        if def.take_types().is_some() && def.take_values().is_some() {
+            PartiallyResolvedImport::Resolved(def)
+        } else {
+            PartiallyResolvedImport::Indeterminate(def)
+        }
+    }
+
+    /// Records ands propagates the resolution of an import directive.
+    fn record_resolved_import(&mut self, directive: &ImportDirective) {
+        let import_module_id = directive.module_id;
+        let import = &directive.import;
+
+        // Get the resolved definition of the use statement
+        let resolution = directive.status.namespaces();
+
+        // Get the visibility of the import statement
+        let import_visibility = self.package_defs.module_tree.resolve_visibility(
+            self.db,
+            import_module_id,
+            &directive.import.visibility,
+        );
+
+        if import.is_glob {
+            match resolution.take_types() {
+                Some((ItemDefinitionId::ModuleId(m), _)) => {
+                    let scope = &self.package_defs[m.local_id];
+
+                    // Get all the items that are visible from this module
+                    let resolutions = scope
+                        .entries()
+                        .map(|(n, res)| ImportResolution {
+                            name: Some(n.clone()),
+                            resolution: res.and_then(|(item, vis)| {
+                                if vis.is_visible_from_module_tree(
+                                    &self.package_defs.module_tree,
+                                    import_module_id,
+                                ) {
+                                    Some((item, vis))
+                                } else {
+                                    None
+                                }
+                            }),
+                        })
+                        .filter(|res| !res.resolution.is_none())
+                        .collect::<Vec<_>>();
+
+                    self.update(
+                        import_module_id,
+                        import_visibility,
+                        ImportType::Glob,
+                        import.source,
+                        &resolutions,
+                    );
+
+                    // Record the wildcard import in case new items are added to the module we are importing
+                    let glob = self.glob_imports.entry(m.local_id).or_default();
+                    if !glob.iter().any(|(m, _, _)| *m == import_module_id) {
+                        glob.push((import_module_id, import_visibility, import.source));
+                    }
+                }
+                Some((_, _)) => {
+                    // Happens when wildcard importing something other than a module. I guess it's ok to do nothing here?
+                }
+                None => {
+                    // Happens if a wildcard import refers to something other than a type?
+                }
+            }
+        } else {
+            match import.path.segments.last() {
+                Some(last_segment) => {
+                    let name = match &import.alias {
+                        Some(ImportAlias::Alias(name)) => Some(name.clone()),
+                        Some(ImportAlias::Underscore) => None,
+                        None => Some(last_segment.clone()),
+                    };
+
+                    self.update(
+                        import_module_id,
+                        import_visibility,
+                        ImportType::Named,
+                        import.source,
+                        &[ImportResolution { name, resolution }],
+                    );
+                }
+                None => unreachable!(),
+            }
+        }
+    }
+
+    /// Updates the current state with the resolutions of an import statement.
+    fn update(
+        &mut self,
+        import_module_id: LocalModuleId,
+        import_visibility: Visibility,
+        import_type: ImportType,
+        import_source: ItemTreeId<item_tree::Import>,
+        resolutions: &[ImportResolution],
+    ) {
+        self.update_recursive(
+            import_module_id,
+            import_visibility,
+            import_type,
+            import_source,
+            resolutions,
+            0,
+        );
+    }
+
+    /// Updates the current state with the resolutions of an import statement. Also recursively
+    /// updates any wildcard imports.
+    fn update_recursive(
+        &mut self,
+        import_module_id: LocalModuleId,
+        import_visibility: Visibility,
+        import_type: ImportType,
+        import_source: ItemTreeId<item_tree::Import>,
+        resolutions: &[ImportResolution],
+        depth: usize,
+    ) {
+        if depth > 100 {
+            // prevent stack overflows (but this shouldn't be possible)
+            panic!("infinite recursion in glob imports!");
+        }
+
+        let scope = &mut self.package_defs.modules[import_module_id];
+
+        let mut changed = false;
+        for ImportResolution { name, resolution } in resolutions {
+            // TODO(#309): Add an error if the visibility of the item does not allow exposing with the
+            // import visibility. e.g.:
+            // ```mun
+            // //- foo.mun
+            // pub(package) struct Foo;
+            //
+            // //- main.mun
+            // pub foo::Foo; // This is not allowed because Foo is only public within the package.
+            // ```
+
+            match name {
+                Some(name) => {
+                    let add_result = scope.add_resolution_from_import(
+                        &mut self.from_glob_import,
+                        (import_module_id, name.clone()),
+                        resolution.map(|(item, _)| (item, import_visibility)),
+                        import_type,
+                    );
+
+                    if add_result.changed {
+                        changed = true;
+                    }
+                    if add_result.duplicate {
+                        let item_tree = self.db.item_tree(import_source.file_id);
+                        let import_data = &item_tree[import_source.value];
+                        self.package_defs
+                            .diagnostics
+                            .push(DefDiagnostic::duplicate_import(
+                                import_module_id,
+                                InFile::new(import_source.file_id, import_data.ast_id),
+                                import_data.index,
+                            ))
+                    }
+                }
+                None => {
+                    // This is not yet implemented (bringing in types into scope without a name).
+                    // This might be useful for traits. e.g.:
+                    // ```mun
+                    // use foo::SomeTrait as _; // Should be able to call methods added by SomeTrait.
+                    // ```
+                    continue;
+                }
+            }
+        }
+
+        // If nothing changed, there is also no point in updating the wildcard imports
+        if !changed {
+            return;
+        }
+
+        let glob_imports = self
+            .glob_imports
+            .get(&import_module_id)
+            .into_iter()
+            .flat_map(|v| v.iter())
+            .filter(|(glob_importing_module, _, _)| {
+                import_visibility.is_visible_from_module_tree(
+                    &self.package_defs.module_tree,
+                    *glob_importing_module,
+                )
+            })
+            .cloned()
+            .collect::<Vec<_>>();
+
+        for (glob_importing_module, glob_import_vis, glob_import_source) in glob_imports {
+            self.update_recursive(
+                glob_importing_module,
+                glob_import_vis,
+                ImportType::Glob,
+                glob_import_source,
+                resolutions,
+                depth + 1,
+            )
+        }
     }
 
     /// Create the `PackageDefs` struct that holds all the items
     fn finish(self) -> PackageDefs {
-        PackageDefs {
-            modules: self.modules,
-            module_tree: self.module_tree,
+        let mut package_defs = self.package_defs;
+
+        // Create diagnostics for all unresolved imports
+        for directive in self.unresolved_imports.iter() {
+            let import = &directive.import;
+            let item_tree = self.db.item_tree(import.source.file_id);
+            let import_data = &item_tree[import.source.value];
+
+            package_defs
+                .diagnostics
+                .push(DefDiagnostic::unresolved_import(
+                    directive.module_id,
+                    InFile::new(import.source.file_id, import_data.ast_id),
+                    import_data.index,
+                ))
         }
+
+        package_defs
     }
 }
 
@@ -113,6 +498,7 @@ impl<'a> ModCollectorContext<'a, '_> {
                 ModItem::Function(id) => self.collect_function(id),
                 ModItem::Struct(id) => self.collect_struct(id),
                 ModItem::TypeAlias(id) => self.collect_type_alias(id),
+                ModItem::Import(id) => self.collect_import(id),
             };
 
             if let Some(DefData {
@@ -122,20 +508,30 @@ impl<'a> ModCollectorContext<'a, '_> {
                 has_constructor,
             }) = definition
             {
-                self.def_collector.modules[self.module_id].add_definition(id);
-                let visibility = self.def_collector.module_tree.resolve_visibility(
-                    self.def_collector.db,
-                    self.module_id,
-                    visibility,
-                );
-                self.def_collector.modules[self.module_id].add_resolution(
+                self.def_collector.package_defs.modules[self.module_id].add_definition(id);
+                let visibility = self
+                    .def_collector
+                    .package_defs
+                    .module_tree
+                    .resolve_visibility(self.def_collector.db, self.module_id, visibility);
+                self.def_collector.package_defs.modules[self.module_id].add_resolution(
                     name.clone(),
                     PerNs::from_definition(id, visibility, has_constructor),
-                )
+                );
             }
         }
     }
 
+    /// Collects the definition data from an import statement.
+    fn collect_import(&mut self, id: LocalItemTreeId<item_tree::Import>) -> Option<DefData<'a>> {
+        self.def_collector.unresolved_imports.push(ImportDirective {
+            module_id: self.module_id,
+            import: Import::from_use(&self.item_tree, InFile::new(self.file_id, id)),
+            status: PartiallyResolvedImport::Unresolved,
+        });
+        None
+    }
+
     /// Collects the definition data from a `Function`
     fn collect_function(&self, id: LocalItemTreeId<Function>) -> Option<DefData<'a>> {
         let func = &self.item_tree[id];
diff --git a/crates/mun_hir/src/path.rs b/crates/mun_hir/src/path.rs
index c7f79ed60..b09f5dcab 100644
--- a/crates/mun_hir/src/path.rs
+++ b/crates/mun_hir/src/path.rs
@@ -1,5 +1,6 @@
-use crate::{AsName, Name};
+use crate::{AsName, InFile, Name};
 use mun_syntax::ast;
+use mun_syntax::ast::{NameOwner, PathSegmentKind};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Path {
@@ -15,6 +16,15 @@ pub enum PathKind {
     Package,
 }
 
+/// A possible import alias e.g. `Foo as Bar` or `Foo as _`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ImportAlias {
+    /// Unnamed alias, as in `use Foo as _;`
+    Underscore,
+    /// Named alias
+    Alias(Name),
+}
+
 impl Path {
     /// Converts an `ast::Path` to `Path`.
     pub fn from_ast(mut path: ast::Path) -> Option<Path> {
@@ -67,6 +77,118 @@ impl Path {
         }
         None
     }
+
+    /// Constructs a path from its segments.
+    pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
+        let segments = segments.into_iter().collect::<Vec<_>>();
+        Path { kind, segments }
+    }
+
+    /// Calls `cb` with all paths, represented by this use item. For the use statement:
+    /// ```mun
+    /// use foo::{self, Bar};
+    /// ```
+    /// the function will call the callback twice. Once for `foo` and once for `foo::Bar`.
+    pub(crate) fn expand_use_item(
+        item_src: InFile<ast::Use>,
+        mut cb: impl FnMut(Path, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
+    ) {
+        if let Some(tree) = item_src.value.use_tree() {
+            lower_use_tree(None, &tree, &mut cb);
+        }
+    }
+}
+
+/// Given an `ast::UseTree` and an optional prefix, call a callback function for every item that is
+/// contained in the import tree.
+///
+/// For the use statement:
+/// ```mun
+/// use foo::{self, Bar};
+/// ```
+/// the function will call the callback twice. Once for `foo` and once for `foo::Bar`.
+fn lower_use_tree(
+    prefix: Option<Path>,
+    tree: &ast::UseTree,
+    cb: &mut dyn FnMut(Path, &ast::UseTree, bool, Option<ImportAlias>),
+) {
+    if let Some(use_tree_list) = tree.use_tree_list() {
+        let prefix = match tree.path() {
+            None => prefix,
+            Some(path) => convert_path(prefix, &path),
+        };
+        for child_tree in use_tree_list.use_trees() {
+            lower_use_tree(prefix.clone(), &child_tree, cb);
+        }
+    } else {
+        let alias = tree.rename().map(|a| {
+            a.name()
+                .map(|it| it.as_name())
+                .map_or(ImportAlias::Underscore, ImportAlias::Alias)
+        });
+
+        let is_glob = tree.has_star_token();
+        if let Some(ast_path) = tree.path() {
+            // Handle self in a path.
+            if ast_path.qualifier().is_none() {
+                if let Some(segment) = ast_path.segment() {
+                    if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
+                        if let Some(prefix) = prefix {
+                            cb(prefix, &tree, false, alias);
+                            return;
+                        }
+                    }
+                }
+            }
+            if let Some(path) = convert_path(prefix, &ast_path) {
+                cb(path, &tree, is_glob, alias)
+            }
+        } else if is_glob {
+            if let Some(prefix) = prefix {
+                cb(prefix, &tree, is_glob, None)
+            }
+        }
+    }
+}
+
+/// Constructs a `hir::Path` from an `ast::Path` and an optional prefix.
+fn convert_path(prefix: Option<Path>, path: &ast::Path) -> Option<Path> {
+    let prefix = if let Some(qualifier) = path.qualifier() {
+        Some(convert_path(prefix, &qualifier)?)
+    } else {
+        prefix
+    };
+
+    let segment = path.segment()?;
+    let res = match segment.kind()? {
+        ast::PathSegmentKind::Name(name_ref) => {
+            let mut res = prefix.unwrap_or_else(|| Path {
+                kind: PathKind::Plain,
+                segments: Vec::with_capacity(1),
+            });
+            res.segments.push(name_ref.as_name());
+            res
+        }
+        ast::PathSegmentKind::PackageKw => {
+            if prefix.is_some() {
+                return None;
+            }
+            Path::from_segments(PathKind::Package, std::iter::empty())
+        }
+        PathSegmentKind::SelfKw => {
+            if prefix.is_some() {
+                return None;
+            }
+            Path::from_segments(PathKind::Super(0), std::iter::empty())
+        }
+        PathSegmentKind::SuperKw => {
+            if prefix.is_some() {
+                return None;
+            }
+            Path::from_segments(PathKind::Super(1), std::iter::empty())
+        }
+    };
+    Some(res)
 }
 
 impl From<Name> for Path {
diff --git a/crates/mun_hir/src/source_id.rs b/crates/mun_hir/src/source_id.rs
index 7bc7a3128..cf0fa449a 100644
--- a/crates/mun_hir/src/source_id.rs
+++ b/crates/mun_hir/src/source_id.rs
@@ -1,7 +1,6 @@
 use crate::{
     arena::{Arena, Idx},
     db::AstDatabase,
-    db::DefDatabase,
     in_file::InFile,
     FileId,
 };
@@ -18,7 +17,7 @@ use std::{
 pub(crate) type AstId<N> = InFile<FileAstId<N>>;
 
 impl<N: AstNode> AstId<N> {
-    pub fn to_node(&self, db: &dyn DefDatabase) -> N {
+    pub fn to_node(&self, db: &dyn AstDatabase) -> N {
         let root = db.parse(self.file_id);
         db.ast_id_map(self.file_id)
             .get(self.value)
diff --git a/crates/mun_hir/src/visibility.rs b/crates/mun_hir/src/visibility.rs
index de2742c0a..e11ded978 100644
--- a/crates/mun_hir/src/visibility.rs
+++ b/crates/mun_hir/src/visibility.rs
@@ -1,3 +1,4 @@
+use crate::module_tree::{LocalModuleId, ModuleTree};
 use crate::{ids::ModuleId, DefDatabase, HirDatabase, Resolver};
 use mun_syntax::ast;
 use std::iter::successors;
@@ -57,6 +58,23 @@ pub enum Visibility {
 }
 
 impl Visibility {
+    /// Returns true if an item with this visibility is accessible from the module of the
+    /// specified `PackageDefs`.
+    pub(crate) fn is_visible_from_module_tree(
+        self,
+        module_tree: &ModuleTree,
+        from_module: LocalModuleId,
+    ) -> bool {
+        let to_module = match self {
+            Visibility::Module(m) => m,
+            Visibility::Public => return true,
+        };
+
+        let mut ancestors = successors(Some(from_module), |m| module_tree[*m].parent);
+
+        ancestors.any(|m| m == to_module.local_id)
+    }
+
     /// Returns true if an item with this visibility is accessible from the given module.
     pub fn is_visible_from(self, db: &dyn HirDatabase, from_module: ModuleId) -> bool {
         let to_module = match self {
diff --git a/crates/mun_syntax/src/ast/extensions.rs b/crates/mun_syntax/src/ast/extensions.rs
index a7033e4d6..6aea3929f 100644
--- a/crates/mun_syntax/src/ast/extensions.rs
+++ b/crates/mun_syntax/src/ast/extensions.rs
@@ -199,3 +199,11 @@ impl ast::Visibility {
             .any(|it| it.kind() == T![super])
     }
 }
+
+impl ast::UseTree {
+    pub fn has_star_token(&self) -> bool {
+        self.syntax()
+            .children_with_tokens()
+            .any(|it| it.kind() == T![*])
+    }
+}
diff --git a/crates/mun_syntax/src/ast/generated.rs b/crates/mun_syntax/src/ast/generated.rs
index b9d6a0eeb..7a673d4f3 100644
--- a/crates/mun_syntax/src/ast/generated.rs
+++ b/crates/mun_syntax/src/ast/generated.rs
@@ -610,7 +610,7 @@ pub struct ModuleItem {
 
 impl AstNode for ModuleItem {
     fn can_cast(kind: SyntaxKind) -> bool {
-        matches!(kind, FUNCTION_DEF | STRUCT_DEF | TYPE_ALIAS_DEF)
+        matches!(kind, USE | FUNCTION_DEF | STRUCT_DEF | TYPE_ALIAS_DEF)
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
         if Self::can_cast(syntax.kind()) {
@@ -625,10 +625,16 @@ impl AstNode for ModuleItem {
 }
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum ModuleItemKind {
+    Use(Use),
     FunctionDef(FunctionDef),
     StructDef(StructDef),
     TypeAliasDef(TypeAliasDef),
 }
+impl From<Use> for ModuleItem {
+    fn from(n: Use) -> ModuleItem {
+        ModuleItem { syntax: n.syntax }
+    }
+}
 impl From<FunctionDef> for ModuleItem {
     fn from(n: FunctionDef) -> ModuleItem {
         ModuleItem { syntax: n.syntax }
@@ -648,6 +654,7 @@ impl From<TypeAliasDef> for ModuleItem {
 impl ModuleItem {
     pub fn kind(&self) -> ModuleItemKind {
         match self.syntax.kind() {
+            USE => ModuleItemKind::Use(Use::cast(self.syntax.clone()).unwrap()),
             FUNCTION_DEF => {
                 ModuleItemKind::FunctionDef(FunctionDef::cast(self.syntax.clone()).unwrap())
             }
@@ -1191,6 +1198,31 @@ impl RecordLit {
     }
 }
 
+// Rename
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Rename {
+    pub(crate) syntax: SyntaxNode,
+}
+
+impl AstNode for Rename {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        matches!(kind, RENAME)
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Rename { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode {
+        &self.syntax
+    }
+}
+impl ast::NameOwner for Rename {}
+impl Rename {}
+
 // RetType
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1492,6 +1524,99 @@ impl TypeRef {
 
 impl TypeRef {}
 
+// Use
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Use {
+    pub(crate) syntax: SyntaxNode,
+}
+
+impl AstNode for Use {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        matches!(kind, USE)
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Use { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode {
+        &self.syntax
+    }
+}
+impl ast::VisibilityOwner for Use {}
+impl Use {
+    pub fn use_tree(&self) -> Option<UseTree> {
+        super::child_opt(self)
+    }
+}
+
+// UseTree
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UseTree {
+    pub(crate) syntax: SyntaxNode,
+}
+
+impl AstNode for UseTree {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        matches!(kind, USE_TREE)
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(UseTree { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode {
+        &self.syntax
+    }
+}
+impl UseTree {
+    pub fn use_tree_list(&self) -> Option<UseTreeList> {
+        super::child_opt(self)
+    }
+
+    pub fn path(&self) -> Option<Path> {
+        super::child_opt(self)
+    }
+
+    pub fn rename(&self) -> Option<Rename> {
+        super::child_opt(self)
+    }
+}
+
+// UseTreeList
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UseTreeList {
+    pub(crate) syntax: SyntaxNode,
+}
+
+impl AstNode for UseTreeList {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        matches!(kind, USE_TREE_LIST)
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(UseTreeList { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode {
+        &self.syntax
+    }
+}
+impl UseTreeList {
+    pub fn use_trees(&self) -> impl Iterator<Item = UseTree> {
+        super::children(self)
+    }
+}
+
 // Visibility
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/mun_syntax/src/grammar.ron b/crates/mun_syntax/src/grammar.ron
index b7c628890..bb71927d3 100644
--- a/crates/mun_syntax/src/grammar.ron
+++ b/crates/mun_syntax/src/grammar.ron
@@ -72,6 +72,8 @@ Grammar(
         "fn",
         "if",
         "in",
+        "as",
+        "use",
         // "local",     // We use let
         "nil",
         // "not",        // We use !
@@ -169,13 +171,18 @@ Grammar(
         "RECORD_LIT",
         "RECORD_FIELD_LIST",
         "RECORD_FIELD",
+
+        "USE",
+        "USE_TREE",
+        "USE_TREE_LIST",
+        "RENAME"
     ],
     ast: {
         "SourceFile": (
             traits: [ "ModuleItemOwner", "FunctionDefOwner" ],
         ),
         "ModuleItem": (
-            enum: ["FunctionDef", "StructDef", "TypeAliasDef"]
+            enum: ["Use", "FunctionDef", "StructDef", "TypeAliasDef"]
         ),
         "Visibility": (),
         "FunctionDef": (
@@ -349,5 +356,25 @@ Grammar(
             options: [["spread", "Expr"]]
         ),
         "RecordField": (options: ["NameRef", "Expr"]),
+
+        "Use": (
+            options: [["use_tree", "UseTree"]],
+            traits: ("VisibilityOwner")
+        ),
+
+        "UseTree": (
+            options: [
+                ["use_tree_list", "UseTreeList"],
+                ["path", "Path"],
+                ["rename", "Rename"]
+            ]
+        ),
+        "UseTreeList": (
+            collections: [ ("use_trees", "UseTree") ]
+        ),
+
+        "Rename": (
+            traits: ("NameOwner")
+        )
     }
 )
diff --git a/crates/mun_syntax/src/lib.rs b/crates/mun_syntax/src/lib.rs
index 7d1b7f036..cb26985ea 100644
--- a/crates/mun_syntax/src/lib.rs
+++ b/crates/mun_syntax/src/lib.rs
@@ -189,6 +189,7 @@ fn api_walkthrough() {
             ast::ModuleItemKind::FunctionDef(f) => func = Some(f),
             ast::ModuleItemKind::StructDef(_) => (),
             ast::ModuleItemKind::TypeAliasDef(_) => (),
+            ast::ModuleItemKind::Use(_) => (),
         }
     }
 
diff --git a/crates/mun_syntax/src/parsing/grammar/declarations.rs b/crates/mun_syntax/src/parsing/grammar/declarations.rs
index 2b20acc0d..b06ef9f31 100644
--- a/crates/mun_syntax/src/parsing/grammar/declarations.rs
+++ b/crates/mun_syntax/src/parsing/grammar/declarations.rs
@@ -1,7 +1,8 @@
 use super::*;
-use crate::T;
+use crate::{parsing::grammar::paths::is_use_path_start, T};
 
-pub(super) const DECLARATION_RECOVERY_SET: TokenSet = TokenSet::new(&[T![fn], T![pub], T![struct]]);
+pub(super) const DECLARATION_RECOVERY_SET: TokenSet =
+    TokenSet::new(&[T![fn], T![pub], T![struct], T![use]]);
 
 pub(super) fn mod_contents(p: &mut Parser) {
     while !p.at(EOF) {
@@ -62,6 +63,9 @@ fn abi(p: &mut Parser) {
 
 fn declarations_without_modifiers(p: &mut Parser, m: Marker) -> Result<(), Marker> {
     match p.current() {
+        T![use] => {
+            use_(p, m);
+        }
         T![struct] => {
             adt::struct_def(p, m);
         }
@@ -105,3 +109,79 @@ fn opt_fn_ret_type(p: &mut Parser) -> bool {
         false
     }
 }
+
+fn use_(p: &mut Parser, m: Marker) {
+    assert!(p.at(T![use]));
+    p.bump(T![use]);
+    use_tree(p, true);
+    p.expect(T![;]);
+    m.complete(p, USE);
+}
+
+/// Parses a use "tree", such as `foo::bar` in `use foo::bar;`.
+fn use_tree(p: &mut Parser, top_level: bool) {
+    let m = p.start();
+
+    match p.current() {
+        T![*] if !top_level => p.bump(T![*]),
+        _ if is_use_path_start(p, top_level) => {
+            paths::use_path(p, top_level);
+            match p.current() {
+                T![as] => {
+                    opt_rename(p);
+                }
+                T![:] if p.at(T![::]) => {
+                    p.bump(T![::]);
+                    match p.current() {
+                        T![*] => {
+                            p.bump(T![*]);
+                        }
+                        T!['{'] => use_tree_list(p),
+                        _ => {
+                            p.error("expected `{` or `*`");
+                        }
+                    }
+                }
+                _ => (),
+            }
+        }
+        _ => {
+            m.abandon(p);
+            let msg = "expected one of `self`, `super`, `package` or an identifier";
+            if top_level {
+                p.error_recover(msg, DECLARATION_RECOVERY_SET);
+            } else {
+                // if we are parsing a nested tree, we have to eat a token to remain balanced `{}`
+                p.error_and_bump(msg);
+            }
+            return;
+        }
+    }
+
+    m.complete(p, USE_TREE);
+}
+
+fn use_tree_list(p: &mut Parser) {
+    assert!(p.at(T!['{']));
+    let m = p.start();
+    p.bump(T!['{']);
+    while !p.at(EOF) && !p.at(T!['}']) {
+        use_tree(p, false);
+        if !p.at(T!['}']) {
+            p.expect(T![,]);
+        }
+    }
+    p.expect(T!['}']);
+    m.complete(p, USE_TREE_LIST);
+}
+
+fn opt_rename(p: &mut Parser) {
+    if p.at(T![as]) {
+        let m = p.start();
+        p.bump(T![as]);
+        if !p.eat(T![_]) {
+            name(p);
+        }
+        m.complete(p, RENAME);
+    }
+}
diff --git a/crates/mun_syntax/src/parsing/grammar/paths.rs b/crates/mun_syntax/src/parsing/grammar/paths.rs
index a7c1fe49b..83288da4c 100644
--- a/crates/mun_syntax/src/parsing/grammar/paths.rs
+++ b/crates/mun_syntax/src/parsing/grammar/paths.rs
@@ -7,29 +7,41 @@ pub(super) fn is_path_start(p: &Parser) -> bool {
     matches!(p.current(), IDENT | T![self] | T![super] | T![package])
 }
 
+pub(super) fn is_use_path_start(p: &Parser, top_level: bool) -> bool {
+    if top_level {
+        matches!(p.current(), IDENT | T![self] | T![super] | T![package])
+    } else {
+        matches!(p.current(), IDENT | T![self])
+    }
+}
+
 pub(super) fn type_path(p: &mut Parser) {
-    path(p, Mode::Type)
+    path(p, Mode::Type, true)
 }
 pub(super) fn expr_path(p: &mut Parser) {
-    path(p, Mode::Expr)
+    path(p, Mode::Expr, true)
+}
+pub(super) fn use_path(p: &mut Parser, top_level: bool) {
+    path(p, Mode::Use, top_level)
 }
 
 #[derive(Clone, Copy, Eq, PartialEq)]
 enum Mode {
     Type,
     Expr,
+    Use,
 }
 
-fn path(p: &mut Parser, mode: Mode) {
+fn path(p: &mut Parser, mode: Mode, top_level: bool) {
     let path = p.start();
-    path_segment(p, mode);
+    path_segment(p, mode, top_level);
     let mut qualifier = path.complete(p, PATH);
     loop {
-        let import_tree = matches!(p.nth(1), T![*] | T!['{']);
-        if p.at(T![::]) && !import_tree {
+        let use_tree = matches!(p.nth(2), T![*] | T!['{']);
+        if p.at(T![::]) && !use_tree {
             let path = qualifier.precede(p);
             p.bump(T![::]);
-            path_segment(p, mode);
+            path_segment(p, mode, false);
             let path = path.complete(p, PATH);
             qualifier = path;
         } else {
@@ -38,13 +50,14 @@ fn path(p: &mut Parser, mode: Mode) {
     }
 }
 
-fn path_segment(p: &mut Parser, _mode: Mode) {
+fn path_segment(p: &mut Parser, _mode: Mode, top_level: bool) {
     let m = p.start();
     match p.current() {
         IDENT => {
             name_ref(p);
         }
-        T![self] | T![super] | T![package] => p.bump_any(),
+        T![super] | T![package] if top_level => p.bump_any(),
+        T![self] => p.bump(T![self]),
         _ => p.error_recover(
             "expected identifier",
             declarations::DECLARATION_RECOVERY_SET,
diff --git a/crates/mun_syntax/src/syntax_kind/generated.rs b/crates/mun_syntax/src/syntax_kind/generated.rs
index 5f512f872..43ed5cad9 100644
--- a/crates/mun_syntax/src/syntax_kind/generated.rs
+++ b/crates/mun_syntax/src/syntax_kind/generated.rs
@@ -73,6 +73,8 @@ pub enum SyntaxKind {
     FN_KW,
     IF_KW,
     IN_KW,
+    AS_KW,
+    USE_KW,
     NIL_KW,
     RETURN_KW,
     TRUE_KW,
@@ -141,6 +143,10 @@ pub enum SyntaxKind {
     RECORD_LIT,
     RECORD_FIELD_LIST,
     RECORD_FIELD,
+    USE,
+    USE_TREE,
+    USE_TREE_LIST,
+    RENAME,
     // Technical kind so that we can cast from u16 safely
     #[doc(hidden)]
     __LAST,
@@ -314,6 +320,12 @@ macro_rules! T {
     (in) => {
         $crate::SyntaxKind::IN_KW
     };
+    (as) => {
+        $crate::SyntaxKind::AS_KW
+    };
+    (use) => {
+        $crate::SyntaxKind::USE_KW
+    };
     (nil) => {
         $crate::SyntaxKind::NIL_KW
     };
@@ -389,6 +401,8 @@ impl SyntaxKind {
         | FN_KW
         | IF_KW
         | IN_KW
+        | AS_KW
+        | USE_KW
         | NIL_KW
         | RETURN_KW
         | TRUE_KW
@@ -528,6 +542,8 @@ impl SyntaxKind {
             FN_KW => &SyntaxInfo { name: "FN_KW" },
             IF_KW => &SyntaxInfo { name: "IF_KW" },
             IN_KW => &SyntaxInfo { name: "IN_KW" },
+            AS_KW => &SyntaxInfo { name: "AS_KW" },
+            USE_KW => &SyntaxInfo { name: "USE_KW" },
             NIL_KW => &SyntaxInfo { name: "NIL_KW" },
             RETURN_KW => &SyntaxInfo { name: "RETURN_KW" },
             TRUE_KW => &SyntaxInfo { name: "TRUE_KW" },
@@ -596,6 +612,10 @@ impl SyntaxKind {
             RECORD_LIT => &SyntaxInfo { name: "RECORD_LIT" },
             RECORD_FIELD_LIST => &SyntaxInfo { name: "RECORD_FIELD_LIST" },
             RECORD_FIELD => &SyntaxInfo { name: "RECORD_FIELD" },
+            USE => &SyntaxInfo { name: "USE" },
+            USE_TREE => &SyntaxInfo { name: "USE_TREE" },
+            USE_TREE_LIST => &SyntaxInfo { name: "USE_TREE_LIST" },
+            RENAME => &SyntaxInfo { name: "RENAME" },
             TOMBSTONE => &SyntaxInfo { name: "TOMBSTONE" },
             EOF => &SyntaxInfo { name: "EOF" },
             __LAST => &SyntaxInfo { name: "__LAST" },
@@ -612,6 +632,8 @@ impl SyntaxKind {
             "fn" => FN_KW,
             "if" => IF_KW,
             "in" => IN_KW,
+            "as" => AS_KW,
+            "use" => USE_KW,
             "nil" => NIL_KW,
             "return" => RETURN_KW,
             "true" => TRUE_KW,


Test patch:
diff --git a/crates/mun_hir/src/item_tree/tests.rs b/crates/mun_hir/src/item_tree/tests.rs
index 1b72d48d6..6a4bd76a1 100644
--- a/crates/mun_hir/src/item_tree/tests.rs
+++ b/crates/mun_hir/src/item_tree/tests.rs
@@ -44,6 +44,9 @@ fn format_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) -> fmt::Res
         ModItem::TypeAlias(item) => {
             write!(out, "{:?}", tree[item])?;
         }
+        ModItem::Import(item) => {
+            write!(out, "{:?}", tree[item])?;
+        }
     }
 
     for line in children.lines() {
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_.snap
new file mode 100644
index 000000000..0aadc8aae
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_.snap
@@ -0,0 +1,15 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /bar.mun\nuse package::Foo;\npub struct Bar(Foo);\n\n//- /mod.mun\npub use foo::Foo; // Re-export a child's definition\n\nstruct Baz;\n\n//- /foo.mun\nuse package::{bar::Bar, Baz};\n\npub struct Foo {\n    baz: Baz, // Can use private definitions from any of its ancestors\n}\n\npub fn foo_from_bar(bar: Bar) -> Foo {\n    bar.0\n}"
+---
+mod mod
++-- struct Baz
++-- use struct package::foo::Foo
++-- mod bar
+|   +-- struct Bar
+|   '-- use struct package::foo::Foo
+'-- mod foo
+    +-- fn foo_from_bar
+    +-- struct Foo
+    +-- use struct package::Baz
+    '-- use struct package::bar::Bar
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_alias.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_alias.snap
new file mode 100644
index 000000000..dc924e051
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_alias.snap
@@ -0,0 +1,12 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\npub struct Ok;\n\n//- /bar.mun\npub use package::foo::Ok as ReallyOk;\n\npub struct Ok;\n\n//- /baz.mun\nuse package::bar::ReallyOk;"
+---
+mod mod
++-- mod bar
+|   +-- struct Ok
+|   '-- use struct package::foo::Ok
++-- mod baz
+|   '-- use struct package::foo::Ok
+'-- mod foo
+    '-- struct Ok
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_cyclic.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_cyclic.snap
new file mode 100644
index 000000000..91999dc4f
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_cyclic.snap
@@ -0,0 +1,14 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\nuse super::baz::Cyclic;\n\npub struct Ok;\n\n//- /bar.mun\nuse super::foo::{Cyclic, Ok};\n\n//- /baz.mun\nuse super::bar::{Cyclic, Ok};"
+---
+mod mod
++-- mod bar
+|   +-- ERROR: [17; 23): unresolved import
+|   '-- use struct package::foo::Ok
++-- mod baz
+|   +-- ERROR: [17; 23): unresolved import
+|   '-- use struct package::foo::Ok
+'-- mod foo
+    +-- ERROR: [4; 22): unresolved import
+    '-- struct Ok
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_cyclic_wildcard.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_cyclic_wildcard.snap
new file mode 100644
index 000000000..cb15df62d
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_cyclic_wildcard.snap
@@ -0,0 +1,11 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\npub use super::baz::*;\n\npub struct Ok;\n\n//- /baz.mun\npub use super::foo::{self, *};"
+---
+mod mod
++-- mod baz
+|   +-- use struct package::foo::Ok
+|   '-- use mod package::foo
+'-- mod foo
+    +-- struct Ok
+    '-- use mod package::foo
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_duplicate_name.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_duplicate_name.snap
new file mode 100644
index 000000000..f4fee7f1e
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_duplicate_name.snap
@@ -0,0 +1,10 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\npub struct Ok;\n\n//- /bar.mun\nuse package::foo::Ok;\n\npub struct Ok;"
+---
+mod mod
++-- mod bar
+|   +-- ERROR: [4; 20): a second item with the same name imported. Try to use an alias.
+|   '-- struct Ok
+'-- mod foo
+    '-- struct Ok
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_self.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_self.snap
new file mode 100644
index 000000000..571b8cde5
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_self.snap
@@ -0,0 +1,10 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\npub struct Ok;\n\n//- /bar.mun\nuse super::foo::{self};\nuse foo::Ok;"
+---
+mod mod
++-- mod bar
+|   +-- use struct package::foo::Ok
+|   '-- use mod package::foo
+'-- mod foo
+    '-- struct Ok
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_unresolved.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_unresolved.snap
new file mode 100644
index 000000000..61f6ac0e7
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_unresolved.snap
@@ -0,0 +1,10 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\npub struct Foo;\n\n//- /mod.mun\nuse foo::Foo;   // works\nuse foo::Bar;   // doesnt work (Bar does not exist)\nuse baz::Baz;   // doesnt work (baz does not exist)"
+---
+mod mod
++-- ERROR: [29; 37): unresolved import
++-- ERROR: [81; 89): unresolved import
++-- use struct package::foo::Foo
+'-- mod foo
+    '-- struct Foo
diff --git a/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_wildcard.snap b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_wildcard.snap
new file mode 100644
index 000000000..a793b22fd
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/snapshots/mun_hir__package_defs__tests__use_wildcard.snap
@@ -0,0 +1,13 @@
+---
+source: crates/mun_hir/src/package_defs/tests.rs
+expression: "//- /foo.mun\npub struct Foo;\n\n//- /foo/bar.mun\npub use super::Foo;\npub struct FooBar;\n\n//- /bar.mun\nuse package::foo::bar::*;   // Should reference two structs (Foo and FooBar)"
+---
+mod mod
++-- mod bar
+|   +-- use struct package::foo::Foo
+|   '-- use struct package::foo::bar::FooBar
+'-- mod foo
+    +-- struct Foo
+    '-- mod bar
+        +-- struct FooBar
+        '-- use struct package::foo::Foo
diff --git a/crates/mun_hir/src/package_defs/tests.rs b/crates/mun_hir/src/package_defs/tests.rs
new file mode 100644
index 000000000..11453d332
--- /dev/null
+++ b/crates/mun_hir/src/package_defs/tests.rs
@@ -0,0 +1,288 @@
+use crate::{
+    db::DefDatabase, fixture::WithFixture, ids::ItemDefinitionId, mock::MockDatabase,
+    package_defs::PackageDefs, DiagnosticSink, Function, HirDatabase, Module, Package, Struct,
+    TypeAlias,
+};
+use itertools::Itertools;
+use rustc_hash::FxHashSet;
+
+#[test]
+fn use_alias() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    pub struct Ok;
+
+    //- /bar.mun
+    pub use package::foo::Ok as ReallyOk;
+
+    pub struct Ok;
+
+    //- /baz.mun
+    use package::bar::ReallyOk;
+    "#,
+    )
+}
+
+#[test]
+fn use_duplicate_name() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    pub struct Ok;
+
+    //- /bar.mun
+    use package::foo::Ok;
+
+    pub struct Ok;
+    "#,
+    )
+}
+
+#[test]
+fn use_cyclic_wildcard() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    pub use super::baz::*;
+
+    pub struct Ok;
+
+    //- /baz.mun
+    pub use super::foo::{self, *};
+    "#,
+    )
+}
+
+#[test]
+fn use_wildcard() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    pub struct Foo;
+
+    //- /foo/bar.mun
+    pub use super::Foo;
+    pub struct FooBar;
+
+    //- /bar.mun
+    use package::foo::bar::*;   // Should reference two structs (Foo and FooBar)
+    "#,
+    )
+}
+
+#[test]
+fn use_self() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    pub struct Ok;
+
+    //- /bar.mun
+    use super::foo::{self};
+    use foo::Ok;
+    "#,
+    )
+}
+
+#[test]
+fn use_cyclic() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    use super::baz::Cyclic;
+
+    pub struct Ok;
+
+    //- /bar.mun
+    use super::foo::{Cyclic, Ok};
+
+    //- /baz.mun
+    use super::bar::{Cyclic, Ok};
+    "#,
+    )
+}
+
+#[test]
+fn use_unresolved() {
+    resolve_snapshot(
+        r#"
+    //- /foo.mun
+    pub struct Foo;
+
+    //- /mod.mun
+    use foo::Foo;   // works
+    use foo::Bar;   // doesnt work (Bar does not exist)
+    use baz::Baz;   // doesnt work (baz does not exist)
+    "#,
+    )
+}
+
+#[test]
+fn use_() {
+    resolve_snapshot(
+        r#"
+    //- /bar.mun
+    use package::Foo;
+    pub struct Bar(Foo);
+
+    //- /mod.mun
+    pub use foo::Foo; // Re-export a child's definition
+
+    struct Baz;
+
+    //- /foo.mun
+    use package::{bar::Bar, Baz};
+
+    pub struct Foo {
+        baz: Baz, // Can use private definitions from any of its ancestors
+    }
+
+    pub fn foo_from_bar(bar: Bar) -> Foo {
+        bar.0
+    }
+    "#,
+    )
+}
+
+fn resolve_snapshot(text: &str) {
+    let text = text.trim().replace("\n    ", "\n");
+    let resolved = resolve(&text);
+    insta::assert_snapshot!(insta::_macro_support::AutoName, resolved.trim(), &text);
+}
+
+fn resolve(content: &str) -> String {
+    let db = MockDatabase::with_files(content);
+
+    Package::all(&db)
+        .iter()
+        .map(|package| {
+            let package_defs = db.package_defs(package.id);
+            tree_for_module(&db, &package_defs, package.root_module(&db)).to_string()
+        })
+        .intersperse("\n".to_owned())
+        .collect()
+}
+
+fn tree_for_module(
+    db: &dyn HirDatabase,
+    package_defs: &PackageDefs,
+    module: Module,
+) -> text_trees::StringTreeNode {
+    // Construct a tree node
+    let mut node = text_trees::StringTreeNode::new(format!(
+        "mod {}",
+        module
+            .name(db)
+            .map(|name| name.to_string())
+            .unwrap_or_else(|| "mod".to_owned())
+    ));
+
+    // Add module level diagnostics
+    let mut diag_sink = DiagnosticSink::new(|diag| {
+        node.push(format!(
+            "ERROR: {}: {}",
+            diag.highlight_range(),
+            diag.message()
+        ));
+    });
+    module.diagnostics(db, &mut diag_sink);
+    drop(diag_sink);
+
+    // Iterate over all declarations and add them as nodes
+    let scope = &package_defs[module.id.local_id];
+    let local_declarations = scope.declarations().collect::<FxHashSet<_>>();
+    let used_declarations = scope
+        .entries()
+        .filter_map(|entry| entry.1.take_types().map(|(def, _)| def))
+        .collect::<Vec<_>>();
+    for def in local_declarations.iter().chain(
+        used_declarations
+            .iter()
+            .filter(|decl| !local_declarations.contains(*decl)),
+    ) {
+        let is_local = local_declarations.contains(&def);
+        match def {
+            ItemDefinitionId::ModuleId(m) => {
+                if m.package == module.id.package
+                    && module
+                        .children(db)
+                        .into_iter()
+                        .find(|child_id| child_id.id == *m)
+                        .is_none()
+                {
+                    let module: Module = (*m).into();
+                    node.push(format!(
+                        "use mod {}",
+                        fully_qualified_module_path(db, module)
+                    ));
+                }
+            }
+            ItemDefinitionId::FunctionId(f) => {
+                let func: Function = (*f).into();
+                let name = func.name(db);
+                if is_local {
+                    node.push(format!("fn {}", name));
+                } else {
+                    let fully_qualified_name = format!(
+                        "{}::{}",
+                        fully_qualified_module_path(db, func.module(db)),
+                        name
+                    );
+                    node.push(format!("use fn {}", fully_qualified_name));
+                }
+            }
+            ItemDefinitionId::StructId(s) => {
+                let strukt: Struct = (*s).into();
+                let name = strukt.name(db);
+                if is_local {
+                    node.push(format!("struct {}", name));
+                } else {
+                    let fully_qualified_name = format!(
+                        "{}::{}",
+                        fully_qualified_module_path(db, strukt.module(db)),
+                        name
+                    );
+                    node.push(format!("use struct {}", fully_qualified_name));
+                }
+            }
+            ItemDefinitionId::TypeAliasId(alias) => {
+                let alias: TypeAlias = (*alias).into();
+                let name = alias.name(db);
+                if is_local {
+                    node.push(format!("type {}", name));
+                } else {
+                    let fully_qualified_name = format!(
+                        "{}::{}",
+                        fully_qualified_module_path(db, alias.module(db)),
+                        name
+                    );
+                    node.push(format!("use type {}", fully_qualified_name));
+                }
+            }
+            ItemDefinitionId::PrimitiveType(_) => {}
+        }
+    }
+
+    // Iterate over all children of this module
+    for child_module in module.children(db) {
+        node.push_node(tree_for_module(db, package_defs, child_module))
+    }
+
+    node
+}
+
+/// Returns a fully qualified path of a module e.g. `package::foo::bar::baz`
+fn fully_qualified_module_path(db: &dyn HirDatabase, module: Module) -> String {
+    module
+        .path_to_root(db)
+        .into_iter()
+        .map(|m| {
+            m.name(db)
+                .map(|name| name.to_string())
+                .unwrap_or_else(|| "package".to_owned())
+        })
+        .rev()
+        .intersperse("::".to_string())
+        .collect::<String>()
+}
diff --git a/crates/mun_hir/src/ty/snapshots/mun_hir__ty__tests__recursive_alias.snap b/crates/mun_hir/src/ty/snapshots/mun_hir__ty__tests__recursive_alias.snap
index 2e68fdb9a..a14bdb02b 100644
--- a/crates/mun_hir/src/ty/snapshots/mun_hir__ty__tests__recursive_alias.snap
+++ b/crates/mun_hir/src/ty/snapshots/mun_hir__ty__tests__recursive_alias.snap
@@ -2,13 +2,12 @@
 source: crates/mun_hir/src/ty/tests.rs
 expression: "struct Foo {}\ntype Foo = Foo;\n\ntype A = B;\ntype B = A;\n\nfn main() {\n    let a: Foo;  // error: unknown type\n    let b: A;    // error: unknown type\n    let c: B;    // error: unknown type\n}"
 ---
-[25; 28): cyclic type
+[14; 29): the name `Foo` is defined multiple times
 [40; 41): cyclic type
 [52; 53): cyclic type
-[79; 82): cyclic type
 [119; 120): cyclic type
 [159; 160): cyclic type
 [66; 189) '{     ...type }': nothing
-[76; 77) 'a': {unknown}
+[76; 77) 'a': Foo
 [116; 117) 'b': {unknown}
 [156; 157) 'c': {unknown}
diff --git a/crates/mun_hir/src/ty/tests.rs b/crates/mun_hir/src/ty/tests.rs
index 5d8dacf74..06222e6da 100644
--- a/crates/mun_hir/src/ty/tests.rs
+++ b/crates/mun_hir/src/ty/tests.rs
@@ -652,6 +652,12 @@ fn infer(content: &str) -> String {
         write!(diags, "{}: {}\n", diag.highlight_range(), diag.message()).unwrap();
     });
 
+    for package in Package::all(&db).iter() {
+        for module in package.modules(&db).iter() {
+            module.diagnostics(&db, &mut diag_sink);
+        }
+    }
+
     for item in Package::all(&db)
         .iter()
         .flat_map(|pkg| pkg.modules(&db))
@@ -662,13 +668,8 @@ fn infer(content: &str) -> String {
                 let source_map = fun.body_source_map(&db);
                 let infer_result = fun.infer(&db);
 
-                fun.diagnostics(&db, &mut diag_sink);
-
                 infer_def(infer_result, source_map);
             }
-            ModuleDef::TypeAlias(item) => {
-                item.diagnostics(&db, &mut diag_sink);
-            }
             _ => {}
         }
     }
diff --git a/crates/mun_syntax/src/tests/parser.rs b/crates/mun_syntax/src/tests/parser.rs
index 9b51f092f..ac30d2b3d 100644
--- a/crates/mun_syntax/src/tests/parser.rs
+++ b/crates/mun_syntax/src/tests/parser.rs
@@ -376,3 +376,38 @@ fn function_return_path() {
     "#,
     );
 }
+
+#[test]
+fn use_() {
+    snapshot_test(
+        r#"
+        // Simple paths
+        use package_name;
+        use self::item_in_scope_or_package_name;
+        use self::module::Item;
+        use package::Item;
+        use self::some::Struct;
+        use package::some_item;
+
+        // Use tree list
+        use crate::{Item};
+        use self::{Item};
+
+        // Wildcard import
+        use *; // Error
+        use ::*; // Error
+        use crate::*;
+        use crate::{*};
+
+        // Renames
+        use some::path as some_name;
+        use some::{
+            other::path as some_other_name,
+            different::path as different_name,
+            yet::another::path,
+            running::out::of::synonyms::for_::different::*
+        };
+        use Foo as _;
+        "#,
+    )
+}
diff --git a/crates/mun_syntax/src/tests/snapshots/mun_syntax__tests__parser__use_.snap b/crates/mun_syntax/src/tests/snapshots/mun_syntax__tests__parser__use_.snap
new file mode 100644
index 000000000..0c6cfe388
--- /dev/null
+++ b/crates/mun_syntax/src/tests/snapshots/mun_syntax__tests__parser__use_.snap
@@ -0,0 +1,334 @@
+---
+source: crates/mun_syntax/src/tests/parser.rs
+expression: "// Simple paths\n    use package_name;\n    use self::item_in_scope_or_package_name;\n    use self::module::Item;\n    use package::Item;\n    use self::some::Struct;\n    use package::some_item;\n\n    // Use tree list\n    use crate::{Item};\n    use self::{Item};\n\n    // Wildcard import\n    use *; // Error\n    use ::*; // Error\n    use crate::*;\n    use crate::{*};\n\n    // Renames\n    use some::path as some_name;\n    use some::{\n        other::path as some_other_name,\n        different::path as different_name,\n        yet::another::path,\n        running::out::of::synonyms::for_::different::*\n    };\n    use Foo as _;"
+---
+SOURCE_FILE@[0; 616)
+  COMMENT@[0; 15) "// Simple paths"
+  WHITESPACE@[15; 20) "\n    "
+  USE@[20; 37)
+    USE_KW@[20; 23) "use"
+    WHITESPACE@[23; 24) " "
+    USE_TREE@[24; 36)
+      PATH@[24; 36)
+        PATH_SEGMENT@[24; 36)
+          NAME_REF@[24; 36)
+            IDENT@[24; 36) "package_name"
+    SEMI@[36; 37) ";"
+  WHITESPACE@[37; 42) "\n    "
+  USE@[42; 82)
+    USE_KW@[42; 45) "use"
+    WHITESPACE@[45; 46) " "
+    USE_TREE@[46; 81)
+      PATH@[46; 81)
+        PATH@[46; 50)
+          PATH_SEGMENT@[46; 50)
+            SELF_KW@[46; 50) "self"
+        COLONCOLON@[50; 52) "::"
+        PATH_SEGMENT@[52; 81)
+          NAME_REF@[52; 81)
+            IDENT@[52; 81) "item_in_scope_or_pack ..."
+    SEMI@[81; 82) ";"
+  WHITESPACE@[82; 87) "\n    "
+  USE@[87; 110)
+    USE_KW@[87; 90) "use"
+    WHITESPACE@[90; 91) " "
+    USE_TREE@[91; 109)
+      PATH@[91; 109)
+        PATH@[91; 103)
+          PATH@[91; 95)
+            PATH_SEGMENT@[91; 95)
+              SELF_KW@[91; 95) "self"
+          COLONCOLON@[95; 97) "::"
+          PATH_SEGMENT@[97; 103)
+            NAME_REF@[97; 103)
+              IDENT@[97; 103) "module"
+        COLONCOLON@[103; 105) "::"
+        PATH_SEGMENT@[105; 109)
+          NAME_REF@[105; 109)
+            IDENT@[105; 109) "Item"
+    SEMI@[109; 110) ";"
+  WHITESPACE@[110; 115) "\n    "
+  USE@[115; 133)
+    USE_KW@[115; 118) "use"
+    WHITESPACE@[118; 119) " "
+    USE_TREE@[119; 132)
+      PATH@[119; 132)
+        PATH@[119; 126)
+          PATH_SEGMENT@[119; 126)
+            PACKAGE_KW@[119; 126) "package"
+        COLONCOLON@[126; 128) "::"
+        PATH_SEGMENT@[128; 132)
+          NAME_REF@[128; 132)
+            IDENT@[128; 132) "Item"
+    SEMI@[132; 133) ";"
+  WHITESPACE@[133; 138) "\n    "
+  USE@[138; 161)
+    USE_KW@[138; 141) "use"
+    WHITESPACE@[141; 142) " "
+    USE_TREE@[142; 160)
+      PATH@[142; 160)
+        PATH@[142; 152)
+          PATH@[142; 146)
+            PATH_SEGMENT@[142; 146)
+              SELF_KW@[142; 146) "self"
+          COLONCOLON@[146; 148) "::"
+          PATH_SEGMENT@[148; 152)
+            NAME_REF@[148; 152)
+              IDENT@[148; 152) "some"
+        COLONCOLON@[152; 154) "::"
+        PATH_SEGMENT@[154; 160)
+          NAME_REF@[154; 160)
+            IDENT@[154; 160) "Struct"
+    SEMI@[160; 161) ";"
+  WHITESPACE@[161; 166) "\n    "
+  USE@[166; 189)
+    USE_KW@[166; 169) "use"
+    WHITESPACE@[169; 170) " "
+    USE_TREE@[170; 188)
+      PATH@[170; 188)
+        PATH@[170; 177)
+          PATH_SEGMENT@[170; 177)
+            PACKAGE_KW@[170; 177) "package"
+        COLONCOLON@[177; 179) "::"
+        PATH_SEGMENT@[179; 188)
+          NAME_REF@[179; 188)
+            IDENT@[179; 188) "some_item"
+    SEMI@[188; 189) ";"
+  WHITESPACE@[189; 195) "\n\n    "
+  COMMENT@[195; 211) "// Use tree list"
+  WHITESPACE@[211; 216) "\n    "
+  USE@[216; 234)
+    USE_KW@[216; 219) "use"
+    WHITESPACE@[219; 220) " "
+    USE_TREE@[220; 233)
+      PATH@[220; 225)
+        PATH_SEGMENT@[220; 225)
+          NAME_REF@[220; 225)
+            IDENT@[220; 225) "crate"
+      COLONCOLON@[225; 227) "::"
+      USE_TREE_LIST@[227; 233)
+        L_CURLY@[227; 228) "{"
+        USE_TREE@[228; 232)
+          PATH@[228; 232)
+            PATH_SEGMENT@[228; 232)
+              NAME_REF@[228; 232)
+                IDENT@[228; 232) "Item"
+        R_CURLY@[232; 233) "}"
+    SEMI@[233; 234) ";"
+  WHITESPACE@[234; 239) "\n    "
+  USE@[239; 256)
+    USE_KW@[239; 242) "use"
+    WHITESPACE@[242; 243) " "
+    USE_TREE@[243; 255)
+      PATH@[243; 247)
+        PATH_SEGMENT@[243; 247)
+          SELF_KW@[243; 247) "self"
+      COLONCOLON@[247; 249) "::"
+      USE_TREE_LIST@[249; 255)
+        L_CURLY@[249; 250) "{"
+        USE_TREE@[250; 254)
+          PATH@[250; 254)
+            PATH_SEGMENT@[250; 254)
+              NAME_REF@[250; 254)
+                IDENT@[250; 254) "Item"
+        R_CURLY@[254; 255) "}"
+    SEMI@[255; 256) ";"
+  WHITESPACE@[256; 262) "\n\n    "
+  COMMENT@[262; 280) "// Wildcard import"
+  WHITESPACE@[280; 285) "\n    "
+  USE@[285; 291)
+    USE_KW@[285; 288) "use"
+    WHITESPACE@[288; 289) " "
+    ERROR@[289; 290)
+      STAR@[289; 290) "*"
+    SEMI@[290; 291) ";"
+  WHITESPACE@[291; 292) " "
+  COMMENT@[292; 300) "// Error"
+  WHITESPACE@[300; 305) "\n    "
+  USE@[305; 310)
+    USE_KW@[305; 308) "use"
+    WHITESPACE@[308; 309) " "
+    ERROR@[309; 310)
+      COLON@[309; 310) ":"
+  ERROR@[310; 311)
+    COLON@[310; 311) ":"
+  ERROR@[311; 312)
+    STAR@[311; 312) "*"
+  ERROR@[312; 313)
+    SEMI@[312; 313) ";"
+  WHITESPACE@[313; 314) " "
+  COMMENT@[314; 322) "// Error"
+  WHITESPACE@[322; 327) "\n    "
+  USE@[327; 340)
+    USE_KW@[327; 330) "use"
+    WHITESPACE@[330; 331) " "
+    USE_TREE@[331; 339)
+      PATH@[331; 336)
+        PATH_SEGMENT@[331; 336)
+          NAME_REF@[331; 336)
+            IDENT@[331; 336) "crate"
+      COLONCOLON@[336; 338) "::"
+      STAR@[338; 339) "*"
+    SEMI@[339; 340) ";"
+  WHITESPACE@[340; 345) "\n    "
+  USE@[345; 360)
+    USE_KW@[345; 348) "use"
+    WHITESPACE@[348; 349) " "
+    USE_TREE@[349; 359)
+      PATH@[349; 354)
+        PATH_SEGMENT@[349; 354)
+          NAME_REF@[349; 354)
+            IDENT@[349; 354) "crate"
+      COLONCOLON@[354; 356) "::"
+      USE_TREE_LIST@[356; 359)
+        L_CURLY@[356; 357) "{"
+        USE_TREE@[357; 358)
+          STAR@[357; 358) "*"
+        R_CURLY@[358; 359) "}"
+    SEMI@[359; 360) ";"
+  WHITESPACE@[360; 366) "\n\n    "
+  COMMENT@[366; 376) "// Renames"
+  WHITESPACE@[376; 381) "\n    "
+  USE@[381; 409)
+    USE_KW@[381; 384) "use"
+    WHITESPACE@[384; 385) " "
+    USE_TREE@[385; 408)
+      PATH@[385; 395)
+        PATH@[385; 389)
+          PATH_SEGMENT@[385; 389)
+            NAME_REF@[385; 389)
+              IDENT@[385; 389) "some"
+        COLONCOLON@[389; 391) "::"
+        PATH_SEGMENT@[391; 395)
+          NAME_REF@[391; 395)
+            IDENT@[391; 395) "path"
+      WHITESPACE@[395; 396) " "
+      RENAME@[396; 408)
+        AS_KW@[396; 398) "as"
+        WHITESPACE@[398; 399) " "
+        NAME@[399; 408)
+          IDENT@[399; 408) "some_name"
+    SEMI@[408; 409) ";"
+  WHITESPACE@[409; 414) "\n    "
+  USE@[414; 598)
+    USE_KW@[414; 417) "use"
+    WHITESPACE@[417; 418) " "
+    USE_TREE@[418; 597)
+      PATH@[418; 422)
+        PATH_SEGMENT@[418; 422)
+          NAME_REF@[418; 422)
+            IDENT@[418; 422) "some"
+      COLONCOLON@[422; 424) "::"
+      USE_TREE_LIST@[424; 597)
+        L_CURLY@[424; 425) "{"
+        WHITESPACE@[425; 434) "\n        "
+        USE_TREE@[434; 464)
+          PATH@[434; 445)
+            PATH@[434; 439)
+              PATH_SEGMENT@[434; 439)
+                NAME_REF@[434; 439)
+                  IDENT@[434; 439) "other"
+            COLONCOLON@[439; 441) "::"
+            PATH_SEGMENT@[441; 445)
+              NAME_REF@[441; 445)
+                IDENT@[441; 445) "path"
+          WHITESPACE@[445; 446) " "
+          RENAME@[446; 464)
+            AS_KW@[446; 448) "as"
+            WHITESPACE@[448; 449) " "
+            NAME@[449; 464)
+              IDENT@[449; 464) "some_other_name"
+        COMMA@[464; 465) ","
+        WHITESPACE@[465; 474) "\n        "
+        USE_TREE@[474; 507)
+          PATH@[474; 489)
+            PATH@[474; 483)
+              PATH_SEGMENT@[474; 483)
+                NAME_REF@[474; 483)
+                  IDENT@[474; 483) "different"
+            COLONCOLON@[483; 485) "::"
+            PATH_SEGMENT@[485; 489)
+              NAME_REF@[485; 489)
+                IDENT@[485; 489) "path"
+          WHITESPACE@[489; 490) " "
+          RENAME@[490; 507)
+            AS_KW@[490; 492) "as"
+            WHITESPACE@[492; 493) " "
+            NAME@[493; 507)
+              IDENT@[493; 507) "different_name"
+        COMMA@[507; 508) ","
+        WHITESPACE@[508; 517) "\n        "
+        USE_TREE@[517; 535)
+          PATH@[517; 535)
+            PATH@[517; 529)
+              PATH@[517; 520)
+                PATH_SEGMENT@[517; 520)
+                  NAME_REF@[517; 520)
+                    IDENT@[517; 520) "yet"
+              COLONCOLON@[520; 522) "::"
+              PATH_SEGMENT@[522; 529)
+                NAME_REF@[522; 529)
+                  IDENT@[522; 529) "another"
+            COLONCOLON@[529; 531) "::"
+            PATH_SEGMENT@[531; 535)
+              NAME_REF@[531; 535)
+                IDENT@[531; 535) "path"
+        COMMA@[535; 536) ","
+        WHITESPACE@[536; 545) "\n        "
+        USE_TREE@[545; 591)
+          PATH@[545; 588)
+            PATH@[545; 577)
+              PATH@[545; 571)
+                PATH@[545; 561)
+                  PATH@[545; 557)
+                    PATH@[545; 552)
+                      PATH_SEGMENT@[545; 552)
+                        NAME_REF@[545; 552)
+                          IDENT@[545; 552) "running"
+                    COLONCOLON@[552; 554) "::"
+                    PATH_SEGMENT@[554; 557)
+                      NAME_REF@[554; 557)
+                        IDENT@[554; 557) "out"
+                  COLONCOLON@[557; 559) "::"
+                  PATH_SEGMENT@[559; 561)
+                    NAME_REF@[559; 561)
+                      IDENT@[559; 561) "of"
+                COLONCOLON@[561; 563) "::"
+                PATH_SEGMENT@[563; 571)
+                  NAME_REF@[563; 571)
+                    IDENT@[563; 571) "synonyms"
+              COLONCOLON@[571; 573) "::"
+              PATH_SEGMENT@[573; 577)
+                NAME_REF@[573; 577)
+                  IDENT@[573; 577) "for_"
+            COLONCOLON@[577; 579) "::"
+            PATH_SEGMENT@[579; 588)
+              NAME_REF@[579; 588)
+                IDENT@[579; 588) "different"
+          COLONCOLON@[588; 590) "::"
+          STAR@[590; 591) "*"
+        WHITESPACE@[591; 596) "\n    "
+        R_CURLY@[596; 597) "}"
+    SEMI@[597; 598) ";"
+  WHITESPACE@[598; 603) "\n    "
+  USE@[603; 616)
+    USE_KW@[603; 606) "use"
+    WHITESPACE@[606; 607) " "
+    USE_TREE@[607; 615)
+      PATH@[607; 610)
+        PATH_SEGMENT@[607; 610)
+          NAME_REF@[607; 610)
+            IDENT@[607; 610) "Foo"
+      WHITESPACE@[610; 611) " "
+      RENAME@[611; 615)
+        AS_KW@[611; 613) "as"
+        WHITESPACE@[613; 614) " "
+        UNDERSCORE@[614; 615) "_"
+    SEMI@[615; 616) ";"
+error Offset(289): expected one of `self`, `super`, `package` or an identifier
+error Offset(309): expected one of `self`, `super`, `package` or an identifier
+error Offset(310): expected SEMI
+error Offset(310): expected a declaration
+error Offset(311): expected a declaration
+error Offset(312): expected a declaration
+