diff --git a/Cargo.lock b/Cargo.lock
index 91dec9f3b9b..87150226ecd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -74,6 +74,7 @@ dependencies = [
  "profile",
  "rust-analyzer-salsa",
  "rustc-hash",
+ "span",
  "stdx",
  "syntax",
  "test-utils",
@@ -516,6 +517,7 @@ dependencies = [
  "rustc-dependencies",
  "rustc-hash",
  "smallvec",
+ "span",
  "stdx",
  "syntax",
  "test-utils",
@@ -542,6 +544,7 @@ dependencies = [
  "profile",
  "rustc-hash",
  "smallvec",
+ "span",
  "stdx",
  "syntax",
  "test-utils",
@@ -695,6 +698,7 @@ dependencies = [
  "rayon",
  "rustc-hash",
  "sourcegen",
+ "span",
  "stdx",
  "syntax",
  "test-utils",
@@ -910,6 +914,7 @@ dependencies = [
  "itertools",
  "proc-macro-api",
  "project-model",
+ "span",
  "tracing",
  "tt",
  "vfs",
@@ -977,6 +982,7 @@ dependencies = [
  "parser",
  "rustc-hash",
  "smallvec",
+ "span",
  "stdx",
  "syntax",
  "test-utils",
@@ -1253,6 +1259,7 @@ dependencies = [
  "serde",
  "serde_json",
  "snap",
+ "span",
  "stdx",
  "text-size",
  "tracing",
@@ -1728,6 +1735,17 @@ dependencies = [
  "xshell",
 ]
 
+[[package]]
+name = "span"
+version = "0.0.0"
+dependencies = [
+ "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rust-analyzer-salsa",
+ "stdx",
+ "syntax",
+ "vfs",
+]
+
 [[package]]
 name = "static_assertions"
 version = "1.1.0"
@@ -2000,6 +2018,7 @@ name = "tt"
 version = "0.0.0"
 dependencies = [
  "smol_str",
+ "span",
  "stdx",
  "text-size",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 1213979c390..f1f61d8ec7c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -71,6 +71,7 @@ proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" }
 profile = { path = "./crates/profile", version = "0.0.0" }
 project-model = { path = "./crates/project-model", version = "0.0.0" }
 sourcegen = { path = "./crates/sourcegen", version = "0.0.0" }
+span = { path = "./crates/span", version = "0.0.0" }
 stdx = { path = "./crates/stdx", version = "0.0.0" }
 syntax = { path = "./crates/syntax", version = "0.0.0" }
 test-utils = { path = "./crates/test-utils", version = "0.0.0" }
diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml
index 393ffe155ba..6558bdcaf72 100644
--- a/crates/base-db/Cargo.toml
+++ b/crates/base-db/Cargo.toml
@@ -25,3 +25,4 @@ syntax.workspace = true
 test-utils.workspace = true
 tt.workspace = true
 vfs.workspace = true
+span.workspace = true
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 3e874adbe54..918c1cd3e06 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -4,12 +4,11 @@
 
 mod input;
 mod change;
-pub mod span;
 
 use std::panic;
 
 use rustc_hash::FxHashSet;
-use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
+use syntax::{ast, Parse, SourceFile};
 use triomphe::Arc;
 
 pub use crate::{
@@ -21,6 +20,7 @@ pub use crate::{
     },
 };
 pub use salsa::{self, Cancelled};
+pub use span::{FilePosition, FileRange};
 pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath};
 
 #[macro_export]
@@ -41,18 +41,6 @@ pub trait Upcast<T: ?Sized> {
     fn upcast(&self) -> &T;
 }
 
-#[derive(Clone, Copy, Debug)]
-pub struct FilePosition {
-    pub file_id: FileId,
-    pub offset: TextSize,
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
-pub struct FileRange {
-    pub file_id: FileId,
-    pub range: TextRange,
-}
-
 pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
 
 pub trait FileLoader {
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index 2d174517605..7ebaffcc13d 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -42,6 +42,7 @@ mbe.workspace = true
 cfg.workspace = true
 tt.workspace = true
 limit.workspace = true
+span.workspace = true
 
 
 [dev-dependencies]
diff --git a/crates/hir-def/src/attr/tests.rs b/crates/hir-def/src/attr/tests.rs
index 0f98a4ec93c..20d53f31fb1 100644
--- a/crates/hir-def/src/attr/tests.rs
+++ b/crates/hir-def/src/attr/tests.rs
@@ -2,7 +2,7 @@
 //! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
 
 use base_db::FileId;
-use hir_expand::span::{RealSpanMap, SpanMapRef};
+use hir_expand::span_map::{RealSpanMap, SpanMapRef};
 use mbe::syntax_node_to_token_tree;
 use syntax::{ast, AstNode};
 
diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs
index 398f116d831..490fd5382cd 100644
--- a/crates/hir-def/src/expander.rs
+++ b/crates/hir-def/src/expander.rs
@@ -4,7 +4,7 @@ use base_db::CrateId;
 use cfg::CfgOptions;
 use drop_bomb::DropBomb;
 use hir_expand::{
-    attrs::RawAttrs, mod_path::ModPath, span::SpanMap, ExpandError, ExpandResult, HirFileId,
+    attrs::RawAttrs, mod_path::ModPath, span_map::SpanMap, ExpandError, ExpandResult, HirFileId,
     InFile, MacroCallId,
 };
 use limit::Limit;
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 3d2cddffa3b..50253e4c8fd 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -42,7 +42,7 @@ use std::{
 };
 
 use ast::{AstNode, HasName, StructKind};
-use base_db::{span::SyntaxContextId, CrateId};
+use base_db::CrateId;
 use either::Either;
 use hir_expand::{
     ast_id_map::{AstIdNode, FileAstId},
@@ -55,6 +55,7 @@ use la_arena::{Arena, Idx, IdxRange, RawIdx};
 use profile::Count;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
+use span::SyntaxContextId;
 use stdx::never;
 use syntax::{ast, match_ast, SyntaxKind};
 use triomphe::Arc;
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 83a2790ce8f..ed40f299d68 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -2,7 +2,7 @@
 
 use std::collections::hash_map::Entry;
 
-use hir_expand::{ast_id_map::AstIdMap, span::SpanMapRef, HirFileId};
+use hir_expand::{ast_id_map::AstIdMap, span_map::SpanMapRef, HirFileId};
 use syntax::ast::{self, HasModuleItem, HasTypeBounds};
 
 use crate::{
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 1e68fd932cc..41a41d6df31 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -63,7 +63,7 @@ use std::{
     panic::{RefUnwindSafe, UnwindSafe},
 };
 
-use base_db::{impl_intern_key, salsa, span::SyntaxContextId, CrateId};
+use base_db::{impl_intern_key, salsa, CrateId};
 use hir_expand::{
     ast_id_map::{AstIdNode, FileAstId},
     attrs::{Attr, AttrId, AttrInput},
@@ -80,6 +80,7 @@ use hir_expand::{
 use item_tree::ExternBlock;
 use la_arena::Idx;
 use nameres::DefMap;
+use span::SyntaxContextId;
 use stdx::impl_from;
 use syntax::{ast, AstNode};
 
diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs
index a3505b65fe7..395b69d284f 100644
--- a/crates/hir-def/src/lower.rs
+++ b/crates/hir-def/src/lower.rs
@@ -3,7 +3,7 @@ use std::cell::OnceCell;
 
 use hir_expand::{
     ast_id_map::{AstIdMap, AstIdNode},
-    span::{SpanMap, SpanMapRef},
+    span_map::{SpanMap, SpanMapRef},
     AstId, HirFileId, InFile,
 };
 use syntax::ast;
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 804645ff43d..1d690d2575e 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -16,15 +16,16 @@ mod proc_macros;
 
 use std::{iter, ops::Range, sync};
 
-use base_db::{span::SpanData, SourceDatabase};
+use base_db::SourceDatabase;
 use expect_test::Expect;
 use hir_expand::{
     db::ExpandDatabase,
     fixture::WithFixture,
     proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
-    span::SpanMapRef,
+    span_map::SpanMapRef,
     InFile, MacroFileId, MacroFileIdExt,
 };
+use span::Span;
 use stdx::format_to;
 use syntax::{
     ast::{self, edit::IndentLevel},
@@ -319,9 +320,9 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
         subtree: &Subtree,
         _: Option<&Subtree>,
         _: &base_db::Env,
-        _: SpanData,
-        _: SpanData,
-        _: SpanData,
+        _: Span,
+        _: Span,
+        _: Span,
     ) -> Result<Subtree, ProcMacroExpansionError> {
         let (parse, _) =
             ::mbe::token_tree_to_syntax_node(subtree, ::mbe::TopEntryPoint::MacroItems);
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index d77be4a8a04..0f3dbdfd416 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -5,7 +5,7 @@
 
 use std::{cmp::Ordering, iter, mem};
 
-use base_db::{span::SyntaxContextId, CrateId, Dependency, Edition, FileId};
+use base_db::{CrateId, Dependency, Edition, FileId};
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
@@ -23,6 +23,7 @@ use itertools::{izip, Itertools};
 use la_arena::Idx;
 use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
+use span::{Span, SyntaxContextId};
 use stdx::always;
 use syntax::{ast, SmolStr};
 use triomphe::Arc;
@@ -86,11 +87,11 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
                         // FIXME: a hacky way to create a Name from string.
                         let name = tt::Ident {
                             text: it.name.clone(),
-                            span: tt::SpanData {
+                            span: Span {
                                 range: syntax::TextRange::empty(syntax::TextSize::new(0)),
-                                anchor: base_db::span::SpanAnchor {
+                                anchor: span::SpanAnchor {
                                     file_id: FileId::BOGUS,
-                                    ast_id: base_db::span::ROOT_ERASED_FILE_AST_ID,
+                                    ast_id: span::ROOT_ERASED_FILE_AST_ID,
                                 },
                                 ctx: SyntaxContextId::ROOT,
                             },
@@ -2095,11 +2096,11 @@ impl ModCollector<'_, '_> {
                     // FIXME: a hacky way to create a Name from string.
                     name = tt::Ident {
                         text: it.clone(),
-                        span: tt::SpanData {
+                        span: Span {
                             range: syntax::TextRange::empty(syntax::TextSize::new(0)),
-                            anchor: base_db::span::SpanAnchor {
+                            anchor: span::SpanAnchor {
                                 file_id: FileId::BOGUS,
-                                ast_id: base_db::span::ROOT_ERASED_FILE_AST_ID,
+                                ast_id: span::ROOT_ERASED_FILE_AST_ID,
                             },
                             ctx: SyntaxContextId::ROOT,
                         },
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index f5803653c73..49688c5ee9c 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -2,7 +2,7 @@
 
 use std::iter;
 
-use hir_expand::{span::SpanMapRef, InFile};
+use hir_expand::{span_map::SpanMapRef, InFile};
 use la_arena::ArenaMap;
 use syntax::ast;
 use triomphe::Arc;
diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml
index 3d1549225fa..b39f0907364 100644
--- a/crates/hir-expand/Cargo.toml
+++ b/crates/hir-expand/Cargo.toml
@@ -32,6 +32,7 @@ profile.workspace = true
 tt.workspace = true
 mbe.workspace = true
 limit.workspace = true
+span.workspace = true
 test-utils.workspace = true
 
 [dev-dependencies]
diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs
index be0b72f9dfa..2abeaaeec45 100644
--- a/crates/hir-expand/src/ast_id_map.rs
+++ b/crates/hir-expand/src/ast_id_map.rs
@@ -5,6 +5,8 @@
 //! item as an ID. That way, id's don't change unless the set of items itself
 //! changes.
 
+// FIXME: Consider moving this into the span crate
+
 use std::{
     any::type_name,
     fmt,
@@ -17,9 +19,9 @@ use profile::Count;
 use rustc_hash::FxHasher;
 use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 
-use crate::db;
+use crate::db::ExpandDatabase;
 
-pub use base_db::span::ErasedFileAstId;
+pub use span::ErasedFileAstId;
 
 /// `AstId` points to an AST node in any file.
 ///
@@ -27,13 +29,13 @@ pub use base_db::span::ErasedFileAstId;
 pub type AstId<N> = crate::InFile<FileAstId<N>>;
 
 impl<N: AstIdNode> AstId<N> {
-    pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
+    pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
         self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
     }
-    pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> crate::InFile<N> {
+    pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile<N> {
         crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
     }
-    pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr<N> {
+    pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> AstPtr<N> {
         db.ast_id_map(self.file_id).get(self.value)
     }
 }
@@ -41,7 +43,7 @@ impl<N: AstIdNode> AstId<N> {
 pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
 
 impl ErasedAstId {
-    pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
+    pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr {
         db.ast_id_map(self.file_id).get_erased(self.value)
     }
 }
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index b8fc30c9118..16a8518bc39 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -1,19 +1,20 @@
 //! A higher level attributes based on TokenTree, with also some shortcuts.
 use std::{fmt, ops};
 
-use base_db::{span::SyntaxContextId, CrateId};
+use base_db::CrateId;
 use cfg::CfgExpr;
 use either::Either;
 use intern::Interned;
 use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
 use smallvec::{smallvec, SmallVec};
+use span::SyntaxContextId;
 use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
 use triomphe::Arc;
 
 use crate::{
     db::ExpandDatabase,
     mod_path::ModPath,
-    span::SpanMapRef,
+    span_map::SpanMapRef,
     tt::{self, Subtree},
     InFile,
 };
diff --git a/crates/hir-expand/src/builtin_attr_macro.rs b/crates/hir-expand/src/builtin_attr_macro.rs
index de58a495fef..33ba6d37d57 100644
--- a/crates/hir-expand/src/builtin_attr_macro.rs
+++ b/crates/hir-expand/src/builtin_attr_macro.rs
@@ -1,12 +1,8 @@
 //! Builtin attributes.
-
-use base_db::{
-    span::{SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
-    FileId,
-};
+use span::{FileId, MacroCallId, Span, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
 use syntax::{TextRange, TextSize};
 
-use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallId, MacroCallKind};
+use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind};
 
 macro_rules! register_builtin {
     ($expand_fn:ident: $(($name:ident, $variant:ident) => $expand:ident),* ) => {
@@ -120,9 +116,9 @@ pub fn pseudo_derive_attr_expansion(
         tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
             char,
             spacing: tt::Spacing::Alone,
-            span: tt::SpanData {
+            span: Span {
                 range: TextRange::empty(TextSize::new(0)),
-                anchor: base_db::span::SpanAnchor {
+                anchor: span::SpanAnchor {
                     file_id: FileId::BOGUS,
                     ast_id: ROOT_ERASED_FILE_AST_ID,
                 },
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index 410aa4d289e..867b75738a4 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -1,20 +1,21 @@
 //! Builtin derives.
 
-use base_db::{span::SpanData, CrateOrigin, LangCrateOrigin};
+use base_db::{CrateOrigin, LangCrateOrigin};
 use itertools::izip;
 use rustc_hash::FxHashSet;
+use span::{MacroCallId, Span};
 use stdx::never;
 use tracing::debug;
 
 use crate::{
     hygiene::span_with_def_site_ctxt,
     name::{AsName, Name},
-    span::SpanMapRef,
+    span_map::SpanMapRef,
     tt,
 };
 use syntax::ast::{self, AstNode, FieldList, HasAttrs, HasGenericParams, HasName, HasTypeBounds};
 
-use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
+use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult};
 
 macro_rules! register_builtin {
     ( $($trait:ident => $expand:ident),* ) => {
@@ -73,16 +74,16 @@ enum VariantShape {
     Unit,
 }
 
-fn tuple_field_iterator(span: SpanData, n: usize) -> impl Iterator<Item = tt::Ident> {
+fn tuple_field_iterator(span: Span, n: usize) -> impl Iterator<Item = tt::Ident> {
     (0..n).map(move |it| tt::Ident::new(format!("f{it}"), span))
 }
 
 impl VariantShape {
-    fn as_pattern(&self, path: tt::Subtree, span: SpanData) -> tt::Subtree {
+    fn as_pattern(&self, path: tt::Subtree, span: Span) -> tt::Subtree {
         self.as_pattern_map(path, span, |it| quote!(span => #it))
     }
 
-    fn field_names(&self, span: SpanData) -> Vec<tt::Ident> {
+    fn field_names(&self, span: Span) -> Vec<tt::Ident> {
         match self {
             VariantShape::Struct(s) => s.clone(),
             VariantShape::Tuple(n) => tuple_field_iterator(span, *n).collect(),
@@ -93,7 +94,7 @@ impl VariantShape {
     fn as_pattern_map(
         &self,
         path: tt::Subtree,
-        span: SpanData,
+        span: Span,
         field_map: impl Fn(&tt::Ident) -> tt::Subtree,
     ) -> tt::Subtree {
         match self {
@@ -143,11 +144,11 @@ enum AdtShape {
 }
 
 impl AdtShape {
-    fn as_pattern(&self, span: SpanData, name: &tt::Ident) -> Vec<tt::Subtree> {
+    fn as_pattern(&self, span: Span, name: &tt::Ident) -> Vec<tt::Subtree> {
         self.as_pattern_map(name, |it| quote!(span =>#it), span)
     }
 
-    fn field_names(&self, span: SpanData) -> Vec<Vec<tt::Ident>> {
+    fn field_names(&self, span: Span) -> Vec<Vec<tt::Ident>> {
         match self {
             AdtShape::Struct(s) => {
                 vec![s.field_names(span)]
@@ -166,7 +167,7 @@ impl AdtShape {
         &self,
         name: &tt::Ident,
         field_map: impl Fn(&tt::Ident) -> tt::Subtree,
-        span: SpanData,
+        span: Span,
     ) -> Vec<tt::Subtree> {
         match self {
             AdtShape::Struct(s) => {
@@ -199,7 +200,7 @@ struct BasicAdtInfo {
 fn parse_adt(
     tm: SpanMapRef<'_>,
     adt: &ast::Adt,
-    call_site: SpanData,
+    call_site: Span,
 ) -> Result<BasicAdtInfo, ExpandError> {
     let (name, generic_param_list, shape) = match adt {
         ast::Adt::Struct(it) => (
@@ -349,7 +350,7 @@ fn name_to_token(
 /// therefore does not get bound by the derived trait.
 fn expand_simple_derive(
     // FIXME: use
-    invoc_span: SpanData,
+    invoc_span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
     trait_path: tt::Subtree,
@@ -397,7 +398,7 @@ fn expand_simple_derive(
     ExpandResult::ok(expanded)
 }
 
-fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: SpanData) -> tt::TokenTree {
+fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: Span) -> tt::TokenTree {
     // FIXME: make hygiene works for builtin derive macro
     // such that $crate can be used here.
     let cg = db.crate_graph();
@@ -416,7 +417,7 @@ fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId, span: SpanData)
 fn copy_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -427,7 +428,7 @@ fn copy_expand(
 fn clone_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -470,13 +471,13 @@ fn clone_expand(
 }
 
 /// This function exists since `quote! {span => => }` doesn't work.
-fn fat_arrow(span: SpanData) -> tt::Subtree {
+fn fat_arrow(span: Span) -> tt::Subtree {
     let eq = tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span };
     quote! {span => #eq> }
 }
 
 /// This function exists since `quote! {span => && }` doesn't work.
-fn and_and(span: SpanData) -> tt::Subtree {
+fn and_and(span: Span) -> tt::Subtree {
     let and = tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span };
     quote! {span => #and& }
 }
@@ -484,7 +485,7 @@ fn and_and(span: SpanData) -> tt::Subtree {
 fn default_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -529,7 +530,7 @@ fn default_expand(
 fn debug_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -607,7 +608,7 @@ fn debug_expand(
 fn hash_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -660,7 +661,7 @@ fn hash_expand(
 fn eq_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -671,7 +672,7 @@ fn eq_expand(
 fn partial_eq_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -725,7 +726,7 @@ fn partial_eq_expand(
 fn self_and_other_patterns(
     adt: &BasicAdtInfo,
     name: &tt::Ident,
-    span: SpanData,
+    span: Span,
 ) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
     let self_patterns = adt.shape.as_pattern_map(
         name,
@@ -749,7 +750,7 @@ fn self_and_other_patterns(
 fn ord_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -760,7 +761,7 @@ fn ord_expand(
             left: tt::Subtree,
             right: tt::Subtree,
             rest: tt::Subtree,
-            span: SpanData,
+            span: Span,
         ) -> tt::Subtree {
             let fat_arrow1 = fat_arrow(span);
             let fat_arrow2 = fat_arrow(span);
@@ -813,7 +814,7 @@ fn ord_expand(
 fn partial_ord_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-    span: SpanData,
+    span: Span,
     tt: &ast::Adt,
     tm: SpanMapRef<'_>,
 ) -> ExpandResult<tt::Subtree> {
@@ -824,7 +825,7 @@ fn partial_ord_expand(
             left: tt::Subtree,
             right: tt::Subtree,
             rest: tt::Subtree,
-            span: SpanData,
+            span: Span,
         ) -> tt::Subtree {
             let fat_arrow1 = fat_arrow(span);
             let fat_arrow2 = fat_arrow(span);
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index c8f04bfee54..36778cb72df 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,13 +1,11 @@
 //! Builtin macro
 
-use base_db::{
-    span::{SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
-    AnchoredPath, Edition, FileId,
-};
+use base_db::{AnchoredPath, Edition, FileId};
 use cfg::CfgExpr;
 use either::Either;
 use itertools::Itertools;
 use mbe::{parse_exprs_with_sep, parse_to_token_tree};
+use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
 use syntax::{
     ast::{self, AstToken},
     SmolStr,
@@ -122,7 +120,7 @@ register_builtin! {
     (option_env, OptionEnv) => option_env_expand
 }
 
-fn mk_pound(span: SpanData) -> tt::Subtree {
+fn mk_pound(span: Span) -> tt::Subtree {
     crate::quote::IntoTt::to_subtree(
         vec![crate::tt::Leaf::Punct(crate::tt::Punct {
             char: '#',
@@ -138,7 +136,7 @@ fn module_path_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     // Just return a dummy result.
     ExpandResult::ok(quote! {span =>
@@ -150,7 +148,7 @@ fn line_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     // dummy implementation for type-checking purposes
     // Note that `line!` and `column!` will never be implemented properly, as they are by definition
@@ -168,7 +166,7 @@ fn log_syntax_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     ExpandResult::ok(quote! {span =>})
 }
@@ -177,7 +175,7 @@ fn trace_macros_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     ExpandResult::ok(quote! {span =>})
 }
@@ -186,7 +184,7 @@ fn stringify_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let pretty = ::tt::pretty(&tt.token_trees);
 
@@ -201,7 +199,7 @@ fn assert_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let args = parse_exprs_with_sep(tt, ',');
     let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
@@ -233,7 +231,7 @@ fn file_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     // FIXME: RA purposefully lacks knowledge of absolute file names
     // so just return "".
@@ -250,7 +248,7 @@ fn format_args_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     format_args_expand_general(db, id, tt, "", span)
 }
@@ -259,7 +257,7 @@ fn format_args_nl_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     format_args_expand_general(db, id, tt, "\\n", span)
 }
@@ -270,7 +268,7 @@ fn format_args_expand_general(
     tt: &tt::Subtree,
     // FIXME: Make use of this so that mir interpretation works properly
     _end_string: &str,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let pound = mk_pound(span);
     let mut tt = tt.clone();
@@ -284,7 +282,7 @@ fn asm_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     // We expand all assembly snippets to `format_args!` invocations to get format syntax
     // highlighting for them.
@@ -314,7 +312,7 @@ fn global_asm_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     // Expand to nothing (at item-level)
     ExpandResult::ok(quote! {span =>})
@@ -324,7 +322,7 @@ fn cfg_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let loc = db.lookup_intern_macro_call(id);
     let expr = CfgExpr::parse(tt);
@@ -337,7 +335,7 @@ fn panic_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
     let dollar_crate = tt::Ident { text: SmolStr::new_inline("$crate"), span };
@@ -357,7 +355,7 @@ fn unreachable_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
     // Expand to a macro call `$crate::panic::unreachable_{edition}`
@@ -395,7 +393,7 @@ fn compile_error_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let err = match &*tt.token_trees {
         [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
@@ -412,7 +410,7 @@ fn concat_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let mut err = None;
     let mut text = String::new();
@@ -459,7 +457,7 @@ fn concat_bytes_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let mut bytes = Vec::new();
     let mut err = None;
@@ -543,7 +541,7 @@ fn concat_idents_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let mut err = None;
     let mut ident = String::new();
@@ -596,7 +594,7 @@ fn include_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let file_id = match include_input_to_file_id(db, arg_id, tt) {
         Ok(it) => it,
@@ -629,7 +627,7 @@ fn include_bytes_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     _tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     // FIXME: actually read the file here if the user asked for macro expansion
     let res = tt::Subtree {
@@ -646,7 +644,7 @@ fn include_str_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let path = match parse_string(tt) {
         Ok(it) => it,
@@ -681,7 +679,7 @@ fn env_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let key = match parse_string(tt) {
         Ok(it) => it,
@@ -713,7 +711,7 @@ fn option_env_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-    span: SpanData,
+    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let key = match parse_string(tt) {
         Ok(it) => it,
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 1e86618ce87..23c7e1071ae 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -2,13 +2,13 @@
 
 use base_db::{
     salsa::{self, debug::DebugQueryTable},
-    span::SyntaxContextId,
     CrateId, Edition, FileId, SourceDatabase,
 };
 use either::Either;
 use limit::Limit;
 use mbe::{syntax_node_to_token_tree, ValueResult};
 use rustc_hash::FxHashSet;
+use span::SyntaxContextId;
 use syntax::{
     ast::{self, HasAttrs},
     AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
@@ -23,7 +23,7 @@ use crate::{
     fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
     hygiene::{apply_mark, SyntaxContextData, Transparency},
     proc_macro::ProcMacros,
-    span::{RealSpanMap, SpanMap, SpanMapRef},
+    span_map::{RealSpanMap, SpanMap, SpanMapRef},
     tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
     CustomProcMacroExpander, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap,
     HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
@@ -41,7 +41,7 @@ static TOKEN_LIMIT: Limit = Limit::new(1_048_576);
 #[derive(Debug, Clone, Eq, PartialEq)]
 /// Old-style `macro_rules` or the new macros 2.0
 pub struct DeclarativeMacroExpander {
-    pub mac: mbe::DeclarativeMacro<base_db::span::SpanData>,
+    pub mac: mbe::DeclarativeMacro<span::Span>,
     pub transparency: Transparency,
 }
 
diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs
index 8d55240aef5..03b664fd951 100644
--- a/crates/hir-expand/src/eager.rs
+++ b/crates/hir-expand/src/eager.rs
@@ -18,7 +18,8 @@
 //!
 //!
 //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
-use base_db::{span::SyntaxContextId, CrateId};
+use base_db::CrateId;
+use span::SyntaxContextId;
 use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
 use triomphe::Arc;
 
@@ -26,7 +27,7 @@ use crate::{
     ast::{self, AstNode},
     db::ExpandDatabase,
     mod_path::ModPath,
-    span::SpanMapRef,
+    span_map::SpanMapRef,
     EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, MacroCallId,
     MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
 };
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 89f0685d5b6..40553d3e964 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -1,11 +1,8 @@
 //! Things to wrap other things in file ids.
 use std::iter;
 
-use base_db::{
-    span::{HirFileId, HirFileIdRepr, MacroFileId, SyntaxContextId},
-    FileId, FileRange,
-};
 use either::Either;
+use span::{FileId, FileRange, HirFileId, HirFileIdRepr, MacroFileId, SyntaxContextId};
 use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize};
 
 use crate::{db, ExpansionInfo, MacroFileIdExt};
diff --git a/crates/hir-expand/src/fixture.rs b/crates/hir-expand/src/fixture.rs
index 9a65a5c5b72..0c194a1b1e9 100644
--- a/crates/hir-expand/src/fixture.rs
+++ b/crates/hir-expand/src/fixture.rs
@@ -2,12 +2,13 @@
 use std::{mem, ops::Not, str::FromStr, sync};
 
 use base_db::{
-    salsa::Durability, span::SpanData, CrateDisplayName, CrateGraph, CrateId, CrateName,
-    CrateOrigin, Dependency, DependencyKind, Edition, Env, FileChange, FileId, FilePosition,
-    FileRange, FileSet, LangCrateOrigin, ReleaseChannel, SourceDatabaseExt, SourceRoot, VfsPath,
+    salsa::Durability, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
+    DependencyKind, Edition, Env, FileChange, FileSet, LangCrateOrigin, ReleaseChannel,
+    SourceDatabaseExt, SourceRoot, VfsPath,
 };
 use cfg::CfgOptions;
 use rustc_hash::FxHashMap;
+use span::{FileId, FilePosition, FileRange, Span};
 use test_utils::{
     extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
     ESCAPED_CURSOR_MARKER,
@@ -580,13 +581,13 @@ struct IdentityProcMacroExpander;
 impl ProcMacroExpander for IdentityProcMacroExpander {
     fn expand(
         &self,
-        subtree: &Subtree<SpanData>,
-        _: Option<&Subtree<SpanData>>,
+        subtree: &Subtree<Span>,
+        _: Option<&Subtree<Span>>,
         _: &Env,
-        _: SpanData,
-        _: SpanData,
-        _: SpanData,
-    ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
+        _: Span,
+        _: Span,
+        _: Span,
+    ) -> Result<Subtree<Span>, ProcMacroExpansionError> {
         Ok(subtree.clone())
     }
 }
@@ -597,13 +598,13 @@ struct AttributeInputReplaceProcMacroExpander;
 impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
     fn expand(
         &self,
-        _: &Subtree<SpanData>,
-        attrs: Option<&Subtree<SpanData>>,
+        _: &Subtree<Span>,
+        attrs: Option<&Subtree<Span>>,
         _: &Env,
-        _: SpanData,
-        _: SpanData,
-        _: SpanData,
-    ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
+        _: Span,
+        _: Span,
+        _: Span,
+    ) -> Result<Subtree<Span>, ProcMacroExpansionError> {
         attrs
             .cloned()
             .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
@@ -615,14 +616,14 @@ struct MirrorProcMacroExpander;
 impl ProcMacroExpander for MirrorProcMacroExpander {
     fn expand(
         &self,
-        input: &Subtree<SpanData>,
-        _: Option<&Subtree<SpanData>>,
+        input: &Subtree<Span>,
+        _: Option<&Subtree<Span>>,
         _: &Env,
-        _: SpanData,
-        _: SpanData,
-        _: SpanData,
-    ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
-        fn traverse(input: &Subtree<SpanData>) -> Subtree<SpanData> {
+        _: Span,
+        _: Span,
+        _: Span,
+    ) -> Result<Subtree<Span>, ProcMacroExpansionError> {
+        fn traverse(input: &Subtree<Span>) -> Subtree<Span> {
             let mut token_trees = vec![];
             for tt in input.token_trees.iter().rev() {
                 let tt = match tt {
@@ -645,16 +646,16 @@ struct ShortenProcMacroExpander;
 impl ProcMacroExpander for ShortenProcMacroExpander {
     fn expand(
         &self,
-        input: &Subtree<SpanData>,
-        _: Option<&Subtree<SpanData>>,
+        input: &Subtree<Span>,
+        _: Option<&Subtree<Span>>,
         _: &Env,
-        _: SpanData,
-        _: SpanData,
-        _: SpanData,
-    ) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
+        _: Span,
+        _: Span,
+        _: Span,
+    ) -> Result<Subtree<Span>, ProcMacroExpansionError> {
         return Ok(traverse(input));
 
-        fn traverse(input: &Subtree<SpanData>) -> Subtree<SpanData> {
+        fn traverse(input: &Subtree<Span>) -> Subtree<Span> {
             let token_trees = input
                 .token_trees
                 .iter()
@@ -666,7 +667,7 @@ impl ProcMacroExpander for ShortenProcMacroExpander {
             Subtree { delimiter: input.delimiter, token_trees }
         }
 
-        fn modify_leaf(leaf: &Leaf<SpanData>) -> Leaf<SpanData> {
+        fn modify_leaf(leaf: &Leaf<Span>) -> Leaf<Span> {
             let mut leaf = leaf.clone();
             match &mut leaf {
                 Leaf::Literal(it) => {
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index 346cd39a767..3705ebc5540 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -1,23 +1,20 @@
 //! To make attribute macros work reliably when typing, we need to take care to
 //! fix up syntax errors in the code we're passing to them.
 
-use base_db::{
-    span::{ErasedFileAstId, SpanAnchor, SpanData},
-    FileId,
-};
 use la_arena::RawIdx;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
+use span::{ErasedFileAstId, FileId, SpanAnchor, SpanData};
 use stdx::never;
 use syntax::{
     ast::{self, AstNode, HasLoopBody},
     match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
 };
 use triomphe::Arc;
-use tt::{Spacing, Span};
+use tt::Spacing;
 
 use crate::{
-    span::SpanMapRef,
+    span_map::SpanMapRef,
     tt::{Ident, Leaf, Punct, Subtree},
 };
 
@@ -301,6 +298,7 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool {
 pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) {
     let Some(undo_info) = undo_info.original.as_deref() else { return };
     let undo_info = &**undo_info;
+    #[allow(deprecated)]
     if never!(
         tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
             || tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
@@ -364,7 +362,7 @@ mod tests {
 
     use crate::{
         fixup::reverse_fixups,
-        span::{RealSpanMap, SpanMap},
+        span_map::{RealSpanMap, SpanMap},
         tt,
     };
 
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index 7b03709aced..fb9db3333f5 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -4,7 +4,7 @@
 //! this moment, this is horribly incomplete and handles only `$crate`.
 use std::iter;
 
-use base_db::span::{MacroCallId, SpanData, SyntaxContextId};
+use span::{MacroCallId, Span, SyntaxContextId};
 
 use crate::db::ExpandDatabase;
 
@@ -78,37 +78,29 @@ pub enum Transparency {
     Opaque,
 }
 
-pub fn span_with_def_site_ctxt(
-    db: &dyn ExpandDatabase,
-    span: SpanData,
-    expn_id: MacroCallId,
-) -> SpanData {
+pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
     span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
 }
 
-pub fn span_with_call_site_ctxt(
-    db: &dyn ExpandDatabase,
-    span: SpanData,
-    expn_id: MacroCallId,
-) -> SpanData {
+pub fn span_with_call_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
     span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent)
 }
 
 pub fn span_with_mixed_site_ctxt(
     db: &dyn ExpandDatabase,
-    span: SpanData,
+    span: Span,
     expn_id: MacroCallId,
-) -> SpanData {
+) -> Span {
     span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent)
 }
 
 fn span_with_ctxt_from_mark(
     db: &dyn ExpandDatabase,
-    span: SpanData,
+    span: Span,
     expn_id: MacroCallId,
     transparency: Transparency,
-) -> SpanData {
-    SpanData { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span }
+) -> Span {
+    Span { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span }
 }
 
 pub(super) fn apply_mark(
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index bb2cb3f5b5b..5496f2d661b 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -20,7 +20,7 @@ pub mod mod_path;
 pub mod name;
 pub mod proc_macro;
 pub mod quote;
-pub mod span;
+pub mod span_map;
 mod fixup;
 
 use attrs::collect_attrs;
@@ -28,11 +28,9 @@ use triomphe::Arc;
 
 use std::{fmt, hash::Hash};
 
-use base_db::{
-    span::{HirFileIdRepr, SpanData, SyntaxContextId},
-    CrateId, FileId, FileRange,
-};
+use base_db::{CrateId, FileId};
 use either::Either;
+use span::{FileRange, HirFileIdRepr, Span, SyntaxContextId};
 use syntax::{
     ast::{self, AstNode},
     SyntaxNode, SyntaxToken, TextRange, TextSize,
@@ -47,29 +45,29 @@ use crate::{
     fixup::SyntaxFixupUndoInfo,
     mod_path::ModPath,
     proc_macro::{CustomProcMacroExpander, ProcMacroKind},
-    span::{ExpansionSpanMap, SpanMap},
+    span_map::{ExpansionSpanMap, SpanMap},
 };
 
 pub use crate::ast_id_map::{AstId, ErasedAstId, ErasedFileAstId};
 pub use crate::files::{InFile, InMacroFile, InRealFile};
 
-pub use base_db::span::{HirFileId, MacroCallId, MacroFileId};
 pub use mbe::ValueResult;
+pub use span::{HirFileId, MacroCallId, MacroFileId};
 
-pub type DeclarativeMacro = ::mbe::DeclarativeMacro<tt::SpanData>;
+pub type DeclarativeMacro = ::mbe::DeclarativeMacro<tt::Span>;
 
 pub mod tt {
-    pub use base_db::span::SpanData;
-    pub use tt::{DelimiterKind, Spacing, Span, SpanAnchor};
+    pub use span::Span;
+    pub use tt::{DelimiterKind, Spacing};
 
-    pub type Delimiter = ::tt::Delimiter<SpanData>;
-    pub type DelimSpan = ::tt::DelimSpan<SpanData>;
-    pub type Subtree = ::tt::Subtree<SpanData>;
-    pub type Leaf = ::tt::Leaf<SpanData>;
-    pub type Literal = ::tt::Literal<SpanData>;
-    pub type Punct = ::tt::Punct<SpanData>;
-    pub type Ident = ::tt::Ident<SpanData>;
-    pub type TokenTree = ::tt::TokenTree<SpanData>;
+    pub type Delimiter = ::tt::Delimiter<Span>;
+    pub type DelimSpan = ::tt::DelimSpan<Span>;
+    pub type Subtree = ::tt::Subtree<Span>;
+    pub type Leaf = ::tt::Leaf<Span>;
+    pub type Literal = ::tt::Literal<Span>;
+    pub type Punct = ::tt::Punct<Span>;
+    pub type Ident = ::tt::Ident<Span>;
+    pub type TokenTree = ::tt::TokenTree<Span>;
 }
 
 pub type ExpandResult<T> = ValueResult<T, ExpandError>;
@@ -212,8 +210,8 @@ impl HirFileIdExt for HirFileId {
     fn original_file_respecting_includes(mut self, db: &dyn db::ExpandDatabase) -> FileId {
         loop {
             match self.repr() {
-                base_db::span::HirFileIdRepr::FileId(id) => break id,
-                base_db::span::HirFileIdRepr::MacroFile(file) => {
+                HirFileIdRepr::FileId(id) => break id,
+                HirFileIdRepr::MacroFile(file) => {
                     let loc = db.lookup_intern_macro_call(file.macro_call_id);
                     if loc.def.is_include() {
                         if let Some(eager) = &loc.eager {
@@ -420,7 +418,7 @@ impl MacroDefId {
 }
 
 impl MacroCallLoc {
-    pub fn span(&self, db: &dyn db::ExpandDatabase) -> SpanData {
+    pub fn span(&self, db: &dyn db::ExpandDatabase) -> Span {
         let ast_id = self.kind.erased_ast_id();
         let file_id = self.kind.file_id();
         let range = db.ast_id_map(file_id).get_erased(ast_id).text_range();
@@ -618,7 +616,7 @@ impl ExpansionInfo {
     /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
     pub fn map_range_down<'a>(
         &'a self,
-        span: SpanData,
+        span: Span,
     ) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + 'a>> {
         let tokens = self
             .exp_map
@@ -652,7 +650,7 @@ impl ExpansionInfo {
     ) -> Option<(FileRange, SyntaxContextId)> {
         debug_assert!(self.expanded.value.text_range().contains_range(range));
         let mut spans = self.exp_map.spans_for_range(range);
-        let SpanData { range, anchor, ctx } = spans.next()?;
+        let Span { range, anchor, ctx } = spans.next()?;
         let mut start = range.start();
         let mut end = range.end();
 
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 9534b5039f6..30b8c189f52 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -9,10 +9,11 @@ use crate::{
     db::ExpandDatabase,
     hygiene::{marks_rev, SyntaxContextExt, Transparency},
     name::{known, AsName, Name},
-    span::SpanMapRef,
+    span_map::SpanMapRef,
 };
-use base_db::{span::SyntaxContextId, CrateId};
+use base_db::CrateId;
 use smallvec::SmallVec;
+use span::SyntaxContextId;
 use syntax::{ast, AstNode};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs
index 2707df9d1ae..25c78fade82 100644
--- a/crates/hir-expand/src/proc_macro.rs
+++ b/crates/hir-expand/src/proc_macro.rs
@@ -3,8 +3,9 @@
 use core::fmt;
 use std::{panic::RefUnwindSafe, sync};
 
-use base_db::{span::SpanData, CrateId, Env};
+use base_db::{CrateId, Env};
 use rustc_hash::FxHashMap;
+use span::Span;
 use stdx::never;
 use syntax::SmolStr;
 
@@ -26,9 +27,9 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
         subtree: &tt::Subtree,
         attrs: Option<&tt::Subtree>,
         env: &Env,
-        def_site: SpanData,
-        call_site: SpanData,
-        mixed_site: SpanData,
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
     ) -> Result<tt::Subtree, ProcMacroExpansionError>;
 }
 
@@ -78,9 +79,9 @@ impl CustomProcMacroExpander {
         calling_crate: CrateId,
         tt: &tt::Subtree,
         attr_arg: Option<&tt::Subtree>,
-        def_site: SpanData,
-        call_site: SpanData,
-        mixed_site: SpanData,
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
     ) -> ExpandResult<tt::Subtree> {
         match self.proc_macro_id {
             ProcMacroId(DUMMY_ID) => ExpandResult::new(
diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs
index acbde26c8dd..2684bd80c7d 100644
--- a/crates/hir-expand/src/quote.rs
+++ b/crates/hir-expand/src/quote.rs
@@ -1,6 +1,6 @@
 //! A simplified version of quote-crate like quasi quote macro
 
-use base_db::span::SpanData;
+use span::Span;
 
 // A helper macro quote macro
 // FIXME:
@@ -130,12 +130,12 @@ macro_rules! quote {
 }
 
 pub(crate) trait IntoTt {
-    fn to_subtree(self, span: SpanData) -> crate::tt::Subtree;
+    fn to_subtree(self, span: Span) -> crate::tt::Subtree;
     fn to_tokens(self) -> Vec<crate::tt::TokenTree>;
 }
 
 impl IntoTt for Vec<crate::tt::TokenTree> {
-    fn to_subtree(self, span: SpanData) -> crate::tt::Subtree {
+    fn to_subtree(self, span: Span) -> crate::tt::Subtree {
         crate::tt::Subtree {
             delimiter: crate::tt::Delimiter::invisible_spanned(span),
             token_trees: self,
@@ -148,7 +148,7 @@ impl IntoTt for Vec<crate::tt::TokenTree> {
 }
 
 impl IntoTt for crate::tt::Subtree {
-    fn to_subtree(self, _: SpanData) -> crate::tt::Subtree {
+    fn to_subtree(self, _: Span) -> crate::tt::Subtree {
         self
     }
 
@@ -158,23 +158,23 @@ impl IntoTt for crate::tt::Subtree {
 }
 
 pub(crate) trait ToTokenTree {
-    fn to_token(self, span: SpanData) -> crate::tt::TokenTree;
+    fn to_token(self, span: Span) -> crate::tt::TokenTree;
 }
 
 impl ToTokenTree for crate::tt::TokenTree {
-    fn to_token(self, _: SpanData) -> crate::tt::TokenTree {
+    fn to_token(self, _: Span) -> crate::tt::TokenTree {
         self
     }
 }
 
 impl ToTokenTree for &crate::tt::TokenTree {
-    fn to_token(self, _: SpanData) -> crate::tt::TokenTree {
+    fn to_token(self, _: Span) -> crate::tt::TokenTree {
         self.clone()
     }
 }
 
 impl ToTokenTree for crate::tt::Subtree {
-    fn to_token(self, _: SpanData) -> crate::tt::TokenTree {
+    fn to_token(self, _: Span) -> crate::tt::TokenTree {
         self.into()
     }
 }
@@ -183,14 +183,14 @@ macro_rules! impl_to_to_tokentrees {
     ($($span:ident: $ty:ty => $this:ident $im:block);*) => {
         $(
             impl ToTokenTree for $ty {
-                fn to_token($this, $span: SpanData) -> crate::tt::TokenTree {
+                fn to_token($this, $span: Span) -> crate::tt::TokenTree {
                     let leaf: crate::tt::Leaf = $im.into();
                     leaf.into()
                 }
             }
 
             impl ToTokenTree for &$ty {
-                fn to_token($this, $span: SpanData) -> crate::tt::TokenTree {
+                fn to_token($this, $span: Span) -> crate::tt::TokenTree {
                     let leaf: crate::tt::Leaf = $im.clone().into();
                     leaf.into()
                 }
@@ -215,14 +215,12 @@ impl_to_to_tokentrees! {
 #[cfg(test)]
 mod tests {
     use crate::tt;
-    use base_db::{
-        span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
-        FileId,
-    };
+    use base_db::FileId;
     use expect_test::expect;
+    use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
     use syntax::{TextRange, TextSize};
 
-    const DUMMY: tt::SpanData = tt::SpanData {
+    const DUMMY: tt::Span = tt::Span {
         range: TextRange::empty(TextSize::new(0)),
         anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
         ctx: SyntaxContextId::ROOT,
@@ -261,8 +259,8 @@ mod tests {
         assert_eq!(quoted.to_string(), "hello");
         let t = format!("{quoted:?}");
         expect![[r#"
-            SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }
-              IDENT   hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
+            SUBTREE $$ Span { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } Span { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }
+              IDENT   hello Span { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
     }
 
     #[test]
diff --git a/crates/hir-expand/src/span.rs b/crates/hir-expand/src/span_map.rs
similarity index 82%
rename from crates/hir-expand/src/span.rs
rename to crates/hir-expand/src/span_map.rs
index fe476a40feb..386063155dd 100644
--- a/crates/hir-expand/src/span.rs
+++ b/crates/hir-expand/src/span_map.rs
@@ -1,15 +1,16 @@
 //! Spanmaps allow turning absolute ranges into relative ranges for incrementality purposes as well
 //! as associating spans with text ranges in a particular file.
-use base_db::{
-    span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
-    FileId,
-};
+
+// FIXME: Consider moving this into the span crate
+
+use base_db::FileId;
+use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
 use syntax::{ast::HasModuleItem, AstNode, TextRange, TextSize};
 use triomphe::Arc;
 
 use crate::db::ExpandDatabase;
 
-pub type ExpansionSpanMap = mbe::SpanMap<SpanData>;
+pub type ExpansionSpanMap = span::SpanMap<Span>;
 
 /// Spanmap for a macro file or a real file
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -28,24 +29,24 @@ pub enum SpanMapRef<'a> {
     RealSpanMap(&'a RealSpanMap),
 }
 
-impl mbe::SpanMapper<SpanData> for SpanMap {
-    fn span_for(&self, range: TextRange) -> SpanData {
+impl mbe::SpanMapper<Span> for SpanMap {
+    fn span_for(&self, range: TextRange) -> Span {
         self.span_for_range(range)
     }
 }
-impl mbe::SpanMapper<SpanData> for SpanMapRef<'_> {
-    fn span_for(&self, range: TextRange) -> SpanData {
+impl mbe::SpanMapper<Span> for SpanMapRef<'_> {
+    fn span_for(&self, range: TextRange) -> Span {
         self.span_for_range(range)
     }
 }
-impl mbe::SpanMapper<SpanData> for RealSpanMap {
-    fn span_for(&self, range: TextRange) -> SpanData {
+impl mbe::SpanMapper<Span> for RealSpanMap {
+    fn span_for(&self, range: TextRange) -> Span {
         self.span_for_range(range)
     }
 }
 
 impl SpanMap {
-    pub fn span_for_range(&self, range: TextRange) -> SpanData {
+    pub fn span_for_range(&self, range: TextRange) -> Span {
         match self {
             Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
             Self::RealSpanMap(span_map) => span_map.span_for_range(range),
@@ -61,7 +62,7 @@ impl SpanMap {
 }
 
 impl SpanMapRef<'_> {
-    pub fn span_for_range(self, range: TextRange) -> SpanData {
+    pub fn span_for_range(self, range: TextRange) -> Span {
         match self {
             Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
             Self::RealSpanMap(span_map) => span_map.span_for_range(range),
@@ -103,7 +104,7 @@ impl RealSpanMap {
         }
     }
 
-    pub fn span_for_range(&self, range: TextRange) -> SpanData {
+    pub fn span_for_range(&self, range: TextRange) -> Span {
         assert!(
             range.end() <= self.end,
             "range {range:?} goes beyond the end of the file {:?}",
@@ -115,7 +116,7 @@ impl RealSpanMap {
             .binary_search_by(|&(it, _)| it.cmp(&start).then(std::cmp::Ordering::Less))
             .unwrap_err();
         let (offset, ast_id) = self.pairs[idx - 1];
-        SpanData {
+        Span {
             range: range - offset,
             anchor: SpanAnchor { file_id: self.file_id, ast_id },
             ctx: SyntaxContextId::ROOT,
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 18585335318..d60d20f5b7e 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -11,7 +11,7 @@ use hir_def::{
 };
 use hir_expand::{
     name::Name,
-    span::{RealSpanMap, SpanMapRef},
+    span_map::{RealSpanMap, SpanMapRef},
 };
 use hir_ty::db::HirDatabase;
 use syntax::{ast, AstNode};
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index ccf031df0ce..5096cf2e9c8 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -148,7 +148,7 @@ use {
     hir_def::path::Path,
     hir_expand::{
         name::AsName,
-        span::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
+        span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
     },
 };
 
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index 4a2e770f193..e14117034f5 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -34,6 +34,7 @@ profile.workspace = true
 stdx.workspace = true
 syntax.workspace = true
 text-edit.workspace = true
+span.workspace = true
 # ide should depend only on the top-level `hir` package. if you need
 # something from some `hir-xxx` subpackage, reexport the API via `hir`.
 hir.workspace = true
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index d2b6a732689..c6532640067 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -22,9 +22,10 @@
 //! Our current behavior is ¯\_(ツ)_/¯.
 use std::fmt;
 
-use base_db::{span::SyntaxContextId, AnchoredPathBuf, FileId, FileRange};
+use base_db::{AnchoredPathBuf, FileId, FileRange};
 use either::Either;
 use hir::{FieldSource, HasSource, HirFileIdExt, InFile, ModuleSource, Semantics};
+use span::SyntaxContextId;
 use stdx::{never, TupleExt};
 use syntax::{
     ast::{self, HasName},
diff --git a/crates/load-cargo/Cargo.toml b/crates/load-cargo/Cargo.toml
index 06b3b945876..346c234f097 100644
--- a/crates/load-cargo/Cargo.toml
+++ b/crates/load-cargo/Cargo.toml
@@ -23,5 +23,6 @@ project-model.workspace = true
 tt.workspace = true
 vfs.workspace = true
 vfs-notify.workspace = true
+span.workspace = true
 
 hir-expand.workspace = true
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index cab8d609ead..ee42640cebc 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -11,13 +11,14 @@ use hir_expand::proc_macro::{
 };
 use ide::{AnalysisHost, SourceRoot};
 use ide_db::{
-    base_db::{span::SpanData, CrateGraph, Env},
+    base_db::{CrateGraph, Env},
     fixture::Change,
     FxHashMap,
 };
 use itertools::Itertools;
 use proc_macro_api::{MacroDylib, ProcMacroServer};
 use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
+use span::Span;
 use tt::DelimSpan;
 use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath};
 
@@ -376,13 +377,13 @@ struct Expander(proc_macro_api::ProcMacro);
 impl ProcMacroExpander for Expander {
     fn expand(
         &self,
-        subtree: &tt::Subtree<SpanData>,
-        attrs: Option<&tt::Subtree<SpanData>>,
+        subtree: &tt::Subtree<Span>,
+        attrs: Option<&tt::Subtree<Span>>,
         env: &Env,
-        def_site: SpanData,
-        call_site: SpanData,
-        mixed_site: SpanData,
-    ) -> Result<tt::Subtree<SpanData>, ProcMacroExpansionError> {
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
+    ) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
         let env = env.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect();
         match self.0.expand(subtree, attrs, env, def_site, call_site, mixed_site) {
             Ok(Ok(subtree)) => Ok(subtree),
@@ -399,13 +400,13 @@ struct IdentityExpander;
 impl ProcMacroExpander for IdentityExpander {
     fn expand(
         &self,
-        subtree: &tt::Subtree<SpanData>,
-        _: Option<&tt::Subtree<SpanData>>,
+        subtree: &tt::Subtree<Span>,
+        _: Option<&tt::Subtree<Span>>,
         _: &Env,
-        _: SpanData,
-        _: SpanData,
-        _: SpanData,
-    ) -> Result<tt::Subtree<SpanData>, ProcMacroExpansionError> {
+        _: Span,
+        _: Span,
+        _: Span,
+    ) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
         Ok(subtree.clone())
     }
 }
@@ -417,13 +418,13 @@ struct EmptyExpander;
 impl ProcMacroExpander for EmptyExpander {
     fn expand(
         &self,
-        _: &tt::Subtree<SpanData>,
-        _: Option<&tt::Subtree<SpanData>>,
+        _: &tt::Subtree<Span>,
+        _: Option<&tt::Subtree<Span>>,
         _: &Env,
-        call_site: SpanData,
-        _: SpanData,
-        _: SpanData,
-    ) -> Result<tt::Subtree<SpanData>, ProcMacroExpansionError> {
+        call_site: Span,
+        _: Span,
+        _: Span,
+    ) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
         Ok(tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }))
     }
 }
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index adab1003d10..cc0a47291e3 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -22,6 +22,7 @@ syntax.workspace = true
 parser.workspace = true
 tt.workspace = true
 stdx.workspace = true
+span.workspace = true
 
 [dev-dependencies]
 test-utils.workspace = true
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 7a3e8653c28..9fccf5f424d 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -226,6 +226,7 @@ fn expand_subtree<S: Span>(
                     tt::Leaf::Literal(tt::Literal {
                         text: index.to_string().into(),
                         // FIXME
+                        #[allow(deprecated)]
                         span: S::DUMMY,
                     })
                     .into(),
@@ -286,6 +287,7 @@ fn expand_subtree<S: Span>(
                     tt::Leaf::Literal(tt::Literal {
                         text: c.to_string().into(),
                         // FIXME
+                        #[allow(deprecated)]
                         span: S::DUMMY,
                     })
                     .into(),
@@ -343,8 +345,10 @@ fn expand_var<S: Span>(
         Err(e) => ExpandResult {
             value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty(tt::DelimSpan {
                 // FIXME
+                #[allow(deprecated)]
                 open: S::DUMMY,
                 // FIXME
+                #[allow(deprecated)]
                 close: S::DUMMY,
             }))),
             err: Some(e),
@@ -487,6 +491,7 @@ fn fix_up_and_push_path_tt<S: Span>(buf: &mut Vec<tt::TokenTree<S>>, subtree: tt
                         char: ':',
                         spacing: tt::Spacing::Joint,
                         // FIXME
+                        #[allow(deprecated)]
                         span: S::DUMMY,
                     })
                     .into(),
@@ -496,6 +501,7 @@ fn fix_up_and_push_path_tt<S: Span>(buf: &mut Vec<tt::TokenTree<S>>, subtree: tt
                         char: ':',
                         spacing: tt::Spacing::Alone,
                         // FIXME
+                        #[allow(deprecated)]
                         span: S::DUMMY,
                     })
                     .into(),
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 9331798589f..ee9b2bf700c 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -16,7 +16,6 @@ mod to_parser_input;
 
 #[cfg(test)]
 mod benchmark;
-mod token_map;
 
 use stdx::impl_from;
 use tt::Span;
@@ -30,15 +29,12 @@ use crate::{
 
 // FIXME: we probably should re-think  `token_tree_to_syntax_node` interfaces
 pub use ::parser::TopEntryPoint;
-pub use tt::{Delimiter, DelimiterKind, Punct, SyntaxContext};
+pub use tt::{Delimiter, DelimiterKind, Punct};
 
-pub use crate::{
-    syntax_bridge::{
-        parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span,
-        syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node,
-        SpanMapper,
-    },
-    token_map::SpanMap,
+pub use crate::syntax_bridge::{
+    parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span,
+    syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node,
+    SpanMapper,
 };
 
 pub use crate::syntax_bridge::dummy_test_span_utils::*;
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index b89bfd74a6e..3440c1dd8ca 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -1,6 +1,7 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
 use rustc_hash::{FxHashMap, FxHashSet};
+use span::{SpanAnchor, SpanData, SpanMap};
 use stdx::{never, non_empty_vec::NonEmptyVec};
 use syntax::{
     ast::{self, make::tokens::doc_comment},
@@ -10,10 +11,10 @@ use syntax::{
 };
 use tt::{
     buffer::{Cursor, TokenBuffer},
-    Span, SpanData, SyntaxContext,
+    Span, SyntaxContext,
 };
 
-use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, SpanMap};
+use crate::{to_parser_input::to_parser_input, tt_iter::TtIter};
 
 #[cfg(test)]
 mod tests;
@@ -36,16 +37,20 @@ impl<S: Span, SM: SpanMapper<S>> SpanMapper<S> for &SM {
 
 /// Dummy things for testing where spans don't matter.
 pub(crate) mod dummy_test_span_utils {
+    use tt::SyntaxContext;
+
     use super::*;
 
-    pub type DummyTestSpanData = tt::SpanData<DummyTestSpanAnchor, DummyTestSyntaxContext>;
-    pub const DUMMY: DummyTestSpanData = DummyTestSpanData::DUMMY;
+    pub type DummyTestSpanData = span::SpanData<DummyTestSyntaxContext>;
+    pub const DUMMY: DummyTestSpanData = span::SpanData {
+        range: TextRange::empty(TextSize::new(0)),
+        anchor: span::SpanAnchor {
+            file_id: span::FileId::BOGUS,
+            ast_id: span::ROOT_ERASED_FILE_AST_ID,
+        },
+        ctx: DummyTestSyntaxContext,
+    };
 
-    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-    pub struct DummyTestSpanAnchor;
-    impl tt::SpanAnchor for DummyTestSpanAnchor {
-        const DUMMY: Self = DummyTestSpanAnchor;
-    }
     #[derive(Debug, Copy, Clone, PartialEq, Eq)]
     pub struct DummyTestSyntaxContext;
     impl SyntaxContext for DummyTestSyntaxContext {
@@ -54,27 +59,30 @@ pub(crate) mod dummy_test_span_utils {
 
     pub struct DummyTestSpanMap;
 
-    impl SpanMapper<tt::SpanData<DummyTestSpanAnchor, DummyTestSyntaxContext>> for DummyTestSpanMap {
-        fn span_for(
-            &self,
-            range: syntax::TextRange,
-        ) -> tt::SpanData<DummyTestSpanAnchor, DummyTestSyntaxContext> {
-            tt::SpanData { range, anchor: DummyTestSpanAnchor, ctx: DummyTestSyntaxContext }
+    impl SpanMapper<span::SpanData<DummyTestSyntaxContext>> for DummyTestSpanMap {
+        fn span_for(&self, range: syntax::TextRange) -> span::SpanData<DummyTestSyntaxContext> {
+            span::SpanData {
+                range,
+                anchor: span::SpanAnchor {
+                    file_id: span::FileId::BOGUS,
+                    ast_id: span::ROOT_ERASED_FILE_AST_ID,
+                },
+                ctx: DummyTestSyntaxContext,
+            }
         }
     }
 }
 
 /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the
 /// subtree's spans.
-pub fn syntax_node_to_token_tree<Anchor, Ctx, SpanMap>(
+pub fn syntax_node_to_token_tree<Ctx, SpanMap>(
     node: &SyntaxNode,
     map: SpanMap,
-) -> tt::Subtree<SpanData<Anchor, Ctx>>
+) -> tt::Subtree<SpanData<Ctx>>
 where
-    SpanData<Anchor, Ctx>: Span,
-    Anchor: Copy,
+    SpanData<Ctx>: Span,
     Ctx: SyntaxContext,
-    SpanMap: SpanMapper<SpanData<Anchor, Ctx>>,
+    SpanMap: SpanMapper<SpanData<Ctx>>,
 {
     let mut c = Converter::new(node, map, Default::default(), Default::default());
     convert_tokens(&mut c)
@@ -83,16 +91,15 @@ where
 /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the
 /// subtree's spans. Additionally using the append and remove parameters, the additional tokens can
 /// be injected or hidden from the output.
-pub fn syntax_node_to_token_tree_modified<Anchor, Ctx, SpanMap>(
+pub fn syntax_node_to_token_tree_modified<Ctx, SpanMap>(
     node: &SyntaxNode,
     map: SpanMap,
-    append: FxHashMap<SyntaxElement, Vec<tt::Leaf<SpanData<Anchor, Ctx>>>>,
+    append: FxHashMap<SyntaxElement, Vec<tt::Leaf<SpanData<Ctx>>>>,
     remove: FxHashSet<SyntaxNode>,
-) -> tt::Subtree<SpanData<Anchor, Ctx>>
+) -> tt::Subtree<SpanData<Ctx>>
 where
-    SpanMap: SpanMapper<SpanData<Anchor, Ctx>>,
-    SpanData<Anchor, Ctx>: Span,
-    Anchor: Copy,
+    SpanMap: SpanMapper<SpanData<Ctx>>,
+    SpanData<Ctx>: Span,
     Ctx: SyntaxContext,
 {
     let mut c = Converter::new(node, map, append, remove);
@@ -113,13 +120,12 @@ where
 
 /// Converts a [`tt::Subtree`] back to a [`SyntaxNode`].
 /// The produced `SpanMap` contains a mapping from the syntax nodes offsets to the subtree's spans.
-pub fn token_tree_to_syntax_node<Anchor, Ctx>(
-    tt: &tt::Subtree<SpanData<Anchor, Ctx>>,
+pub fn token_tree_to_syntax_node<Ctx>(
+    tt: &tt::Subtree<SpanData<Ctx>>,
     entry_point: parser::TopEntryPoint,
-) -> (Parse<SyntaxNode>, SpanMap<SpanData<Anchor, Ctx>>)
+) -> (Parse<SyntaxNode>, SpanMap<SpanData<Ctx>>)
 where
-    SpanData<Anchor, Ctx>: Span,
-    Anchor: Copy,
+    SpanData<Ctx>: Span,
     Ctx: SyntaxContext,
 {
     let buffer = match tt {
@@ -150,21 +156,20 @@ where
 
 /// Convert a string to a `TokenTree`. The spans of the subtree will be anchored to the provided
 /// anchor with the given context.
-pub fn parse_to_token_tree<Anchor, Ctx>(
-    anchor: Anchor,
+pub fn parse_to_token_tree<Ctx>(
+    anchor: SpanAnchor,
     ctx: Ctx,
     text: &str,
-) -> Option<tt::Subtree<SpanData<Anchor, Ctx>>>
+) -> Option<tt::Subtree<SpanData<Ctx>>>
 where
-    SpanData<Anchor, Ctx>: Span,
-    Anchor: Copy,
+    SpanData<Ctx>: Span,
     Ctx: SyntaxContext,
 {
     let lexed = parser::LexedStr::new(text);
     if lexed.errors().next().is_some() {
         return None;
     }
-    let mut conv = RawConverter { lexed, pos: 0, anchor, ctx };
+    let mut conv = RawConverter { lexed, anchor, pos: 0, ctx };
     Some(convert_tokens(&mut conv))
 }
 
@@ -450,10 +455,10 @@ fn convert_doc_comment<S: Copy>(
 }
 
 /// A raw token (straight from lexer) converter
-struct RawConverter<'a, Anchor, Ctx> {
+struct RawConverter<'a, Ctx> {
     lexed: parser::LexedStr<'a>,
     pos: usize,
-    anchor: Anchor,
+    anchor: SpanAnchor,
     ctx: Ctx,
 }
 /// A raw token (straight from lexer) converter that gives every token the same span.
@@ -487,16 +492,16 @@ trait TokenConverter<S>: Sized {
     fn span_for(&self, range: TextRange) -> S;
 }
 
-impl<Anchor, S, Ctx> SrcToken<RawConverter<'_, Anchor, Ctx>, S> for usize {
-    fn kind(&self, ctx: &RawConverter<'_, Anchor, Ctx>) -> SyntaxKind {
+impl<S, Ctx> SrcToken<RawConverter<'_, Ctx>, S> for usize {
+    fn kind(&self, ctx: &RawConverter<'_, Ctx>) -> SyntaxKind {
         ctx.lexed.kind(*self)
     }
 
-    fn to_char(&self, ctx: &RawConverter<'_, Anchor, Ctx>) -> Option<char> {
+    fn to_char(&self, ctx: &RawConverter<'_, Ctx>) -> Option<char> {
         ctx.lexed.text(*self).chars().next()
     }
 
-    fn to_text(&self, ctx: &RawConverter<'_, Anchor, Ctx>) -> SmolStr {
+    fn to_text(&self, ctx: &RawConverter<'_, Ctx>) -> SmolStr {
         ctx.lexed.text(*self).into()
     }
 }
@@ -515,18 +520,17 @@ impl<S: Span> SrcToken<StaticRawConverter<'_, S>, S> for usize {
     }
 }
 
-impl<Anchor: Copy, Ctx: SyntaxContext> TokenConverter<SpanData<Anchor, Ctx>>
-    for RawConverter<'_, Anchor, Ctx>
+impl<Ctx: SyntaxContext> TokenConverter<SpanData<Ctx>> for RawConverter<'_, Ctx>
 where
-    SpanData<Anchor, Ctx>: Span,
+    SpanData<Ctx>: Span,
 {
     type Token = usize;
 
     fn convert_doc_comment(
         &self,
         &token: &usize,
-        span: SpanData<Anchor, Ctx>,
-    ) -> Option<Vec<tt::TokenTree<SpanData<Anchor, Ctx>>>> {
+        span: SpanData<Ctx>,
+    ) -> Option<Vec<tt::TokenTree<SpanData<Ctx>>>> {
         let text = self.lexed.text(token);
         convert_doc_comment(&doc_comment(text), span)
     }
@@ -550,7 +554,7 @@ where
         Some(self.pos)
     }
 
-    fn span_for(&self, range: TextRange) -> SpanData<Anchor, Ctx> {
+    fn span_for(&self, range: TextRange) -> SpanData<Ctx> {
         SpanData { range, anchor: self.anchor, ctx: self.ctx }
     }
 }
@@ -778,22 +782,22 @@ where
     }
 }
 
-struct TtTreeSink<'a, Anchor, Ctx>
+struct TtTreeSink<'a, Ctx>
 where
-    SpanData<Anchor, Ctx>: Span,
+    SpanData<Ctx>: Span,
 {
     buf: String,
-    cursor: Cursor<'a, SpanData<Anchor, Ctx>>,
+    cursor: Cursor<'a, SpanData<Ctx>>,
     text_pos: TextSize,
     inner: SyntaxTreeBuilder,
-    token_map: SpanMap<SpanData<Anchor, Ctx>>,
+    token_map: SpanMap<SpanData<Ctx>>,
 }
 
-impl<'a, Anchor, Ctx> TtTreeSink<'a, Anchor, Ctx>
+impl<'a, Ctx> TtTreeSink<'a, Ctx>
 where
-    SpanData<Anchor, Ctx>: Span,
+    SpanData<Ctx>: Span,
 {
-    fn new(cursor: Cursor<'a, SpanData<Anchor, Ctx>>) -> Self {
+    fn new(cursor: Cursor<'a, SpanData<Ctx>>) -> Self {
         TtTreeSink {
             buf: String::new(),
             cursor,
@@ -803,7 +807,7 @@ where
         }
     }
 
-    fn finish(mut self) -> (Parse<SyntaxNode>, SpanMap<SpanData<Anchor, Ctx>>) {
+    fn finish(mut self) -> (Parse<SyntaxNode>, SpanMap<SpanData<Ctx>>) {
         self.token_map.finish();
         (self.inner.finish(), self.token_map)
     }
@@ -821,9 +825,9 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> {
     Some(&texts[idx..texts.len() - (1 - idx)])
 }
 
-impl<Anchor, Ctx> TtTreeSink<'_, Anchor, Ctx>
+impl<Ctx> TtTreeSink<'_, Ctx>
 where
-    SpanData<Anchor, Ctx>: Span,
+    SpanData<Ctx>: Span,
 {
     /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween.
     /// This occurs when a float literal is used as a field access.
diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml
index 2cbbc9489a2..209cbb945d7 100644
--- a/crates/proc-macro-api/Cargo.toml
+++ b/crates/proc-macro-api/Cargo.toml
@@ -33,6 +33,7 @@ tt.workspace = true
 stdx.workspace = true
 profile.workspace = true
 text-size.workspace = true
+span.workspace = true
 # Ideally this crate would not depend on salsa things, but we need span information here which wraps
 # InternIds for the syntax context
 base-db.workspace = true
diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs
index f697ecd3518..a5d4cfeb275 100644
--- a/crates/proc-macro-api/src/lib.rs
+++ b/crates/proc-macro-api/src/lib.rs
@@ -11,9 +11,9 @@ pub mod msg;
 mod process;
 mod version;
 
-use base_db::span::SpanData;
 use indexmap::IndexSet;
 use paths::AbsPathBuf;
+use span::Span;
 use std::{fmt, io, sync::Mutex};
 use triomphe::Arc;
 
@@ -136,13 +136,13 @@ impl ProcMacro {
 
     pub fn expand(
         &self,
-        subtree: &tt::Subtree<SpanData>,
-        attr: Option<&tt::Subtree<SpanData>>,
+        subtree: &tt::Subtree<Span>,
+        attr: Option<&tt::Subtree<Span>>,
         env: Vec<(String, String)>,
-        def_site: SpanData,
-        call_site: SpanData,
-        mixed_site: SpanData,
-    ) -> Result<Result<tt::Subtree<SpanData>, PanicMessage>, ServerError> {
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
+    ) -> Result<Result<tt::Subtree<Span>, PanicMessage>, ServerError> {
         let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version();
         let current_dir = env
             .iter()
diff --git a/crates/proc-macro-api/src/msg.rs b/crates/proc-macro-api/src/msg.rs
index 1d3e45aff38..18fd9ed7284 100644
--- a/crates/proc-macro-api/src/msg.rs
+++ b/crates/proc-macro-api/src/msg.rs
@@ -136,29 +136,27 @@ fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
 
 #[cfg(test)]
 mod tests {
-    use base_db::{
-        span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId},
-        FileId,
-    };
+    use base_db::FileId;
     use la_arena::RawIdx;
+    use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId};
     use text_size::{TextRange, TextSize};
     use tt::{Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, Subtree, TokenTree};
 
     use super::*;
 
-    fn fixture_token_tree() -> Subtree<SpanData> {
+    fn fixture_token_tree() -> Subtree<Span> {
         let anchor = SpanAnchor {
             file_id: FileId::from_raw(0),
             ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)),
         };
         let mut subtree = Subtree {
             delimiter: Delimiter {
-                open: SpanData {
+                open: Span {
                     range: TextRange::empty(TextSize::new(0)),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
                 },
-                close: SpanData {
+                close: Span {
                     range: TextRange::empty(TextSize::new(13)),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
@@ -170,7 +168,7 @@ mod tests {
         subtree.token_trees.push(TokenTree::Leaf(
             Ident {
                 text: "struct".into(),
-                span: SpanData {
+                span: Span {
                     range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
@@ -181,7 +179,7 @@ mod tests {
         subtree.token_trees.push(TokenTree::Leaf(
             Ident {
                 text: "Foo".into(),
-                span: SpanData {
+                span: Span {
                     range: TextRange::at(TextSize::new(5), TextSize::of("Foo")),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
@@ -192,7 +190,7 @@ mod tests {
         subtree.token_trees.push(TokenTree::Leaf(Leaf::Literal(Literal {
             text: "Foo".into(),
 
-            span: SpanData {
+            span: Span {
                 range: TextRange::at(TextSize::new(8), TextSize::of("Foo")),
                 anchor,
                 ctx: SyntaxContextId::ROOT,
@@ -200,7 +198,7 @@ mod tests {
         })));
         subtree.token_trees.push(TokenTree::Leaf(Leaf::Punct(Punct {
             char: '@',
-            span: SpanData {
+            span: Span {
                 range: TextRange::at(TextSize::new(11), TextSize::of('@')),
                 anchor,
                 ctx: SyntaxContextId::ROOT,
@@ -209,12 +207,12 @@ mod tests {
         })));
         subtree.token_trees.push(TokenTree::Subtree(Subtree {
             delimiter: Delimiter {
-                open: SpanData {
+                open: Span {
                     range: TextRange::at(TextSize::new(12), TextSize::of('{')),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
                 },
-                close: SpanData {
+                close: Span {
                     range: TextRange::at(TextSize::new(13), TextSize::of('}')),
                     anchor,
                     ctx: SyntaxContextId::ROOT,
diff --git a/crates/proc-macro-api/src/msg/flat.rs b/crates/proc-macro-api/src/msg/flat.rs
index 5835718628e..12c0daa7e44 100644
--- a/crates/proc-macro-api/src/msg/flat.rs
+++ b/crates/proc-macro-api/src/msg/flat.rs
@@ -37,13 +37,13 @@
 
 use std::collections::{HashMap, VecDeque};
 
-use base_db::span::SpanData;
 use indexmap::IndexSet;
 use serde::{Deserialize, Serialize};
+use span::Span;
 
 use crate::msg::ENCODE_CLOSE_SPAN_VERSION;
 
-type SpanDataIndexMap = IndexSet<SpanData>;
+type SpanIndexMap = IndexSet<Span>;
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TokenId(pub u32);
@@ -93,9 +93,9 @@ struct IdentRepr {
 
 impl FlatTree {
     pub fn new(
-        subtree: &tt::Subtree<SpanData>,
+        subtree: &tt::Subtree<Span>,
         version: u32,
-        span_data_table: &mut SpanDataIndexMap,
+        span_data_table: &mut SpanIndexMap,
     ) -> FlatTree {
         let mut w = Writer {
             string_table: HashMap::new(),
@@ -157,8 +157,8 @@ impl FlatTree {
     pub fn to_subtree_resolved(
         self,
         version: u32,
-        span_data_table: &SpanDataIndexMap,
-    ) -> tt::Subtree<SpanData> {
+        span_data_table: &SpanIndexMap,
+    ) -> tt::Subtree<Span> {
         Reader {
             subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
                 read_vec(self.subtree, SubtreeRepr::read_with_close_span)
@@ -281,13 +281,13 @@ impl IdentRepr {
     }
 }
 
-trait Span: Copy {
+trait InternableSpan: Copy {
     type Table;
     fn token_id_of(table: &mut Self::Table, s: Self) -> TokenId;
     fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self;
 }
 
-impl Span for TokenId {
+impl InternableSpan for TokenId {
     type Table = ();
     fn token_id_of((): &mut Self::Table, token_id: Self) -> TokenId {
         token_id
@@ -297,8 +297,8 @@ impl Span for TokenId {
         id
     }
 }
-impl Span for SpanData {
-    type Table = IndexSet<SpanData>;
+impl InternableSpan for Span {
+    type Table = IndexSet<Span>;
     fn token_id_of(table: &mut Self::Table, span: Self) -> TokenId {
         TokenId(table.insert_full(span).0 as u32)
     }
@@ -307,7 +307,7 @@ impl Span for SpanData {
     }
 }
 
-struct Writer<'a, 'span, S: Span> {
+struct Writer<'a, 'span, S: InternableSpan> {
     work: VecDeque<(usize, &'a tt::Subtree<S>)>,
     string_table: HashMap<&'a str, u32>,
     span_data_table: &'span mut S::Table,
@@ -320,7 +320,7 @@ struct Writer<'a, 'span, S: Span> {
     text: Vec<String>,
 }
 
-impl<'a, 'span, S: Span> Writer<'a, 'span, S> {
+impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> {
     fn write(&mut self, root: &'a tt::Subtree<S>) {
         self.enqueue(root);
         while let Some((idx, subtree)) = self.work.pop_front() {
@@ -393,7 +393,7 @@ impl<'a, 'span, S: Span> Writer<'a, 'span, S> {
     }
 }
 
-struct Reader<'span, S: Span> {
+struct Reader<'span, S: InternableSpan> {
     subtree: Vec<SubtreeRepr>,
     literal: Vec<LiteralRepr>,
     punct: Vec<PunctRepr>,
@@ -403,7 +403,7 @@ struct Reader<'span, S: Span> {
     span_data_table: &'span S::Table,
 }
 
-impl<'span, S: Span> Reader<'span, S> {
+impl<'span, S: InternableSpan> Reader<'span, S> {
     pub(crate) fn read(self) -> tt::Subtree<S> {
         let mut res: Vec<Option<tt::Subtree<S>>> = vec![None; self.subtree.len()];
         let read_span = |id| S::span_for_token_id(self.span_data_table, id);
diff --git a/crates/span/Cargo.toml b/crates/span/Cargo.toml
new file mode 100644
index 00000000000..8b078f9df13
--- /dev/null
+++ b/crates/span/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "span"
+version = "0.0.0"
+rust-version.workspace = true
+edition.workspace = true
+license.workspace = true
+authors.workspace = true
+
+
+[dependencies]
+la-arena.workspace = true
+rust-analyzer-salsa.workspace = true
+
+
+# local deps
+vfs.workspace = true
+syntax.workspace = true
+stdx.workspace = true
diff --git a/crates/base-db/src/span.rs b/crates/span/src/lib.rs
similarity index 77%
rename from crates/base-db/src/span.rs
rename to crates/span/src/lib.rs
index d8990eb7cae..c8c48c9f1f4 100644
--- a/crates/base-db/src/span.rs
+++ b/crates/span/src/lib.rs
@@ -1,10 +1,28 @@
 //! File and span related types.
-// FIXME: This should probably be moved into its own crate.
+// FIXME: This should be moved into its own crate to get rid of the dependency inversion, base-db
+// has business depending on tt, tt should depend on a span crate only (which unforunately will have
+// to depend on salsa)
 use std::fmt;
 
 use salsa::InternId;
-use tt::SyntaxContext;
-use vfs::FileId;
+
+mod map;
+
+pub use crate::map::SpanMap;
+pub use syntax::{TextRange, TextSize};
+pub use vfs::FileId;
+
+#[derive(Clone, Copy, Debug)]
+pub struct FilePosition {
+    pub file_id: FileId,
+    pub offset: TextSize,
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
+pub struct FileRange {
+    pub file_id: FileId,
+    pub range: TextRange,
+}
 
 pub type ErasedFileAstId = la_arena::Idx<syntax::SyntaxNodePtr>;
 
@@ -12,7 +30,26 @@ pub type ErasedFileAstId = la_arena::Idx<syntax::SyntaxNodePtr>;
 pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
     la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0));
 
-pub type SpanData = tt::SpanData<SpanAnchor, SyntaxContextId>;
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub struct SpanData<Ctx> {
+    /// The text range of this span, relative to the anchor.
+    /// We need the anchor for incrementality, as storing absolute ranges will require
+    /// recomputation on every change in a file at all times.
+    pub range: TextRange,
+    pub anchor: SpanAnchor,
+    /// The syntax context of the span.
+    pub ctx: Ctx,
+}
+impl Span {
+    #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
+    pub const DUMMY: Self = SpanData {
+        range: TextRange::empty(TextSize::new(0)),
+        anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
+        ctx: SyntaxContextId::ROOT,
+    };
+}
+
+pub type Span = SpanData<SyntaxContextId>;
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SyntaxContextId(InternId);
@@ -33,7 +70,15 @@ impl fmt::Debug for SyntaxContextId {
         }
     }
 }
-crate::impl_intern_key!(SyntaxContextId);
+
+impl salsa::InternKey for SyntaxContextId {
+    fn from_intern_id(v: salsa::InternId) -> Self {
+        SyntaxContextId(v)
+    }
+    fn as_intern_id(&self) -> salsa::InternId {
+        self.0
+    }
+}
 
 impl fmt::Display for SyntaxContextId {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -41,9 +86,6 @@ impl fmt::Display for SyntaxContextId {
     }
 }
 
-impl SyntaxContext for SyntaxContextId {
-    const DUMMY: Self = Self::ROOT;
-}
 // inherent trait impls please tyvm
 impl SyntaxContextId {
     pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) });
@@ -69,10 +111,6 @@ impl fmt::Debug for SpanAnchor {
     }
 }
 
-impl tt::SpanAnchor for SpanAnchor {
-    const DUMMY: Self = SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID };
-}
-
 /// Input to the analyzer is a set of files, where each file is identified by
 /// `FileId` and contains source code. However, another source of source code in
 /// Rust are macros: each macro can be thought of as producing a "temporary
@@ -90,6 +128,7 @@ impl tt::SpanAnchor for SpanAnchor {
 /// The two variants are encoded in a single u32 which are differentiated by the MSB.
 /// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
 /// `MacroCallId`.
+// FIXME: Give this a better fitting name
 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct HirFileId(u32);
 
@@ -120,7 +159,15 @@ pub struct MacroFileId {
 /// `println!("Hello, {}", world)`.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct MacroCallId(salsa::InternId);
-crate::impl_intern_key!(MacroCallId);
+
+impl salsa::InternKey for MacroCallId {
+    fn from_intern_id(v: salsa::InternId) -> Self {
+        MacroCallId(v)
+    }
+    fn as_intern_id(&self) -> salsa::InternId {
+        self.0
+    }
+}
 
 impl MacroCallId {
     pub fn as_file(self) -> HirFileId {
diff --git a/crates/mbe/src/token_map.rs b/crates/span/src/map.rs
similarity index 89%
rename from crates/mbe/src/token_map.rs
rename to crates/span/src/map.rs
index 7d15812f8cb..e69d003c078 100644
--- a/crates/mbe/src/token_map.rs
+++ b/crates/span/src/map.rs
@@ -1,18 +1,20 @@
-//! Mapping between `TokenId`s and the token's position in macro definitions or inputs.
+//! A map that maps a span to every position in a file. Usually maps a span to some range of positions.
+//! Allows bidirectional lookup.
 
 use std::hash::Hash;
 
 use stdx::{always, itertools::Itertools};
 use syntax::{TextRange, TextSize};
-use tt::Span;
 
 /// Maps absolute text ranges for the corresponding file to the relevant span data.
 #[derive(Debug, PartialEq, Eq, Clone, Hash)]
-pub struct SpanMap<S: Span> {
+pub struct SpanMap<S> {
     spans: Vec<(TextSize, S)>,
+    // FIXME: Should be
+    // spans: Vec<(TextSize, crate::SyntaxContextId)>,
 }
 
-impl<S: Span> SpanMap<S> {
+impl<S: Copy> SpanMap<S> {
     /// Creates a new empty [`SpanMap`].
     pub fn empty() -> Self {
         Self { spans: Vec::new() }
@@ -44,7 +46,10 @@ impl<S: Span> SpanMap<S> {
     /// Returns all [`TextRange`]s that correspond to the given span.
     ///
     /// Note this does a linear search through the entire backing vector.
-    pub fn ranges_with_span(&self, span: S) -> impl Iterator<Item = TextRange> + '_ {
+    pub fn ranges_with_span(&self, span: S) -> impl Iterator<Item = TextRange> + '_
+    where
+        S: Eq,
+    {
         // FIXME: This should ignore the syntax context!
         self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
             if s != span {
diff --git a/crates/tt/Cargo.toml b/crates/tt/Cargo.toml
index 57222449790..e2c0f7d9c79 100644
--- a/crates/tt/Cargo.toml
+++ b/crates/tt/Cargo.toml
@@ -16,3 +16,6 @@ smol_str.workspace = true
 text-size.workspace = true
 
 stdx.workspace = true
+
+# FIXME: Remove this dependency once the `Span` trait is gone (that is once Span::DUMMY has been removed)
+span.workspace = true
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index 481d575403a..70843f0032c 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -11,47 +11,35 @@ use stdx::impl_from;
 pub use smol_str::SmolStr;
 pub use text_size::{TextRange, TextSize};
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
-pub struct SpanData<Anchor, Ctx> {
-    /// The text range of this span, relative to the anchor.
-    /// We need the anchor for incrementality, as storing absolute ranges will require
-    /// recomputation on every change in a file at all times.
-    pub range: TextRange,
-    pub anchor: Anchor,
-    /// The syntax context of the span.
-    pub ctx: Ctx,
-}
-
-impl<Anchor: SpanAnchor, Ctx: SyntaxContext> Span for SpanData<Anchor, Ctx> {
-    #[allow(deprecated)]
-    const DUMMY: Self = SpanData {
-        range: TextRange::empty(TextSize::new(0)),
-        anchor: Anchor::DUMMY,
-        ctx: Ctx::DUMMY,
-    };
-}
-
 pub trait Span: std::fmt::Debug + Copy + Sized + Eq {
     // FIXME: Should not exist. Dummy spans will always be wrong if they leak somewhere. Instead,
     // the call site or def site spans should be used in relevant places, its just that we don't
     // expose those everywhere in the yet.
+    #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
     const DUMMY: Self;
 }
 
-// FIXME: Should not exist
-pub trait SpanAnchor:
-    std::fmt::Debug + Copy + Sized + Eq + Copy + fmt::Debug + std::hash::Hash
-{
-    #[deprecated(note = "this should not exist")]
-    const DUMMY: Self;
-}
-
-// FIXME: Should not exist
 pub trait SyntaxContext: std::fmt::Debug + Copy + Sized + Eq {
-    #[deprecated(note = "this should not exist")]
+    #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
     const DUMMY: Self;
 }
 
+impl<Ctx: SyntaxContext> Span for span::SpanData<Ctx> {
+    #[allow(deprecated)]
+    const DUMMY: Self = span::SpanData {
+        range: TextRange::empty(TextSize::new(0)),
+        anchor: span::SpanAnchor {
+            file_id: span::FileId::BOGUS,
+            ast_id: span::ROOT_ERASED_FILE_AST_ID,
+        },
+        ctx: Ctx::DUMMY,
+    };
+}
+
+impl SyntaxContext for span::SyntaxContextId {
+    const DUMMY: Self = Self::ROOT;
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum TokenTree<S> {
     Leaf(Leaf<S>),
@@ -136,6 +124,7 @@ pub struct DelimSpan<S> {
 
 impl<S: Span> DelimSpan<S> {
     // FIXME should not exist
+    #[allow(deprecated)]
     pub const DUMMY: Self = Self { open: S::DUMMY, close: S::DUMMY };
 }
 
@@ -148,6 +137,7 @@ pub struct Delimiter<S> {
 
 impl<S: Span> Delimiter<S> {
     // FIXME should not exist
+    #[allow(deprecated)]
     pub const DUMMY_INVISIBLE: Self =
         Self { open: S::DUMMY, close: S::DUMMY, kind: DelimiterKind::Invisible };