diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 74c12a0a2d5..b4bdd81f64c 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -4857,3 +4857,22 @@ fn main() {
     "###
     );
 }
+
+#[test]
+fn infer_builtin_macros_file() {
+    assert_snapshot!(
+        infer(r#"
+#[rustc_builtin_macro]
+macro_rules! file {() => {}}
+
+fn main() {
+    let x = file!();
+}
+"#),
+        @r###"
+    ![0; 2) '""': &str
+    [64; 88) '{     ...!(); }': ()
+    [74; 75) 'x': &str
+    "###
+    );
+}
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 9628666d463..d7057e00542 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -10,6 +10,7 @@ use crate::quote;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum BuiltinExpander {
+    File,
     Line,
     Stringify,
 }
@@ -22,6 +23,7 @@ impl BuiltinExpander {
         tt: &tt::Subtree,
     ) -> Result<tt::Subtree, mbe::ExpandError> {
         match self {
+            BuiltinExpander::File => file_expand(db, id, tt),
             BuiltinExpander::Line => line_expand(db, id, tt),
             BuiltinExpander::Stringify => stringify_expand(db, id, tt),
         }
@@ -34,7 +36,9 @@ pub fn find_builtin_macro(
     ast_id: AstId<ast::MacroCall>,
 ) -> Option<MacroDefId> {
     // FIXME: Better registering method
-    if ident == &name::LINE_MACRO {
+    if ident == &name::FILE_MACRO {
+        Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::File) })
+    } else if ident == &name::LINE_MACRO {
         Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) })
     } else if ident == &name::STRINGIFY_MACRO {
         Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Stringify) })
@@ -105,3 +109,23 @@ fn stringify_expand(
 
     Ok(expanded)
 }
+
+fn file_expand(
+    db: &dyn AstDatabase,
+    id: MacroCallId,
+    _tt: &tt::Subtree,
+) -> Result<tt::Subtree, mbe::ExpandError> {
+    let loc = db.lookup_intern_macro(id);
+    let macro_call = loc.ast_id.to_node(db);
+    let _ = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
+
+    // FIXME: RA purposefully lacks knowledge of absolute file names
+    // so just return "".
+    let file_name = "";
+
+    let expanded = quote! {
+        #file_name
+    };
+
+    Ok(expanded)
+}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index c3f7e77a5ae..05fe6afd9b8 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -142,5 +142,6 @@ pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
 pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
 
 // Builtin Macros
+pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file");
 pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");
 pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify");