Repository: rust-analyzer/rust-analyzer
Pull request #5100 (2020-06-27 12:31:54)

Add support for include_str
r? @edwin0cheng 

edwin0cheng: Maybe we could reuse https://github.com/rust-analyzer/rust-analyzer/blob/6a067ce947980d887a254d71d7183c3d306d8978/crates/rust-analyzer/tests/heavy_tests/main.rs#L468 and add a test ? 
lnicola: I'm not sure what to test for? For some reason, "expand macros recursively" doesn't seem to work.
edwin0cheng: Adding something like this inside the `out_dir_checks` should works: 
```
let should_<|>be_str = include_str!(“text.rs”);
```




lnicola: For some reason, we don't seem to find the file -- `relative_file(db, arg_id.into(), &path)` is `None` even though `path` looks correct. This happens even with `include!("main.rs")`, but we might not find the file in the database anyway. Should we expand to `""` for now?
edwin0cheng: iirc, we added a constraint for limiting self including in `relative_path` in `include!` case. Maybe we should add an enum to turn off it if needed ? 
lnicola: Oh, right, it works with other files. But what about non-`.rs` files? I assume they're not going to be found in the database.
edwin0cheng: I remember we have some discussion with @matklad that all non-rs files should be included by users. So I think it is okay for now
lnicola: Added a small hack.
edwin0cheng: Just small nits, otherwise LGTM

bors d+
bors[bot]: :v: lnicola can now approve this pull request. To approve and merge a pull request, simply reply with `bors r+`. More detailed instructions are available [here](https://bors.tech/documentation/getting-started/#reviewing-pull-requests).
lnicola: > Just small nits, otherwise LGTM

Which nits? :smile:

Patch:
diff --git a/.gitignore b/.gitignore
index 472fe1a137..aef0fac339 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,5 +7,6 @@ crates/*/target
 *.log
 *.iml
 .vscode/settings.json
+*.html
 generated_assists.adoc
 generated_features.adoc
diff --git a/Cargo.lock b/Cargo.lock
index dc758a1f09..a70c22a949 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -30,7 +30,7 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 dependencies = [
- "winapi 0.3.9",
+ "winapi 0.3.8",
 ]
 
 [[package]]
@@ -1791,7 +1791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
 dependencies = [
  "libc",
- "winapi 0.3.9",
+ "winapi 0.3.8",
 ]
 
 [[package]]
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 27e94b7fe7..a379b9f49b 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -26,10 +26,8 @@ use hir_ty::{
     autoderef,
     display::{HirDisplayError, HirFormatter},
     expr::ExprValidator,
-    method_resolution,
-    unsafe_validation::UnsafeValidator,
-    ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty,
-    TyDefId, TypeCtor,
+    method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
+    TraitEnvironment, Ty, TyDefId, TypeCtor,
 };
 use ra_db::{CrateId, CrateName, Edition, FileId};
 use ra_prof::profile;
@@ -679,9 +677,7 @@ impl Function {
         let _p = profile("Function::diagnostics");
         let infer = db.infer(self.id.into());
         infer.add_diagnostics(db, self.id, sink);
-        let mut validator = ExprValidator::new(self.id, infer.clone(), sink);
-        validator.validate_body(db);
-        let mut validator = UnsafeValidator::new(self.id, infer, sink);
+        let mut validator = ExprValidator::new(self.id, infer, sink);
         validator.validate_body(db);
     }
 }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index c6bc85e2f1..a7e2e09822 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -176,7 +176,6 @@ impl ExprCollector<'_> {
         if !self.expander.is_cfg_enabled(&expr) {
             return self.missing_expr();
         }
-
         match expr {
             ast::Expr::IfExpr(e) => {
                 let then_branch = self.collect_block_opt(e.then_branch());
@@ -219,12 +218,8 @@ impl ExprCollector<'_> {
                     let body = self.collect_block_opt(e.block_expr());
                     self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
                 }
-                ast::Effect::Unsafe(_) => {
-                    let body = self.collect_block_opt(e.block_expr());
-                    self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
-                }
                 // FIXME: we need to record these effects somewhere...
-                ast::Effect::Async(_) | ast::Effect::Label(_) => {
+                ast::Effect::Async(_) | ast::Effect::Label(_) | ast::Effect::Unsafe(_) => {
                     self.collect_block_opt(e.block_expr())
                 }
             },
@@ -450,6 +445,7 @@ impl ExprCollector<'_> {
                     Mutability::from_mutable(e.mut_token().is_some())
                 };
                 let rawness = Rawness::from_raw(raw_tok);
+
                 self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr)
             }
             ast::Expr::PrefixExpr(e) => {
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index e41cfc16b9..ca49b26d15 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -150,9 +150,6 @@ pub enum Expr {
     Tuple {
         exprs: Vec<ExprId>,
     },
-    Unsafe {
-        body: ExprId,
-    },
     Array(Array),
     Literal(Literal),
 }
@@ -250,7 +247,7 @@ impl Expr {
                     f(*expr);
                 }
             }
-            Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body),
+            Expr::TryBlock { body } => f(*body),
             Expr::Loop { body, .. } => f(*body),
             Expr::While { condition, body, .. } => {
                 f(*condition);
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index b50eb347c8..631358f41a 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -99,6 +99,7 @@ register_builtin! {
     EAGER:
     (concat, Concat) => concat_expand,
     (include, Include) => include_expand,
+    (include_str, IncludeStr) => include_str_expand,
     (env, Env) => env_expand,
     (option_env, OptionEnv) => option_env_expand
 }
@@ -292,11 +293,16 @@ fn concat_expand(
     Ok((quote!(#text), FragmentKind::Expr))
 }
 
-fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
+fn relative_file(
+    db: &dyn AstDatabase,
+    call_id: MacroCallId,
+    path: &str,
+    allow_recursion: bool,
+) -> Option<FileId> {
     let call_site = call_id.as_file().original_file(db);
     let res = db.resolve_path(call_site, path)?;
     // Prevent include itself
-    if res == call_site {
+    if res == call_site && !allow_recursion {
         None
     } else {
         Some(res)
@@ -319,8 +325,8 @@ fn include_expand(
     tt: &tt::Subtree,
 ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
     let path = parse_string(tt)?;
-    let file_id =
-        relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?;
+    let file_id = relative_file(db, arg_id.into(), &path, false)
+        .ok_or_else(|| mbe::ExpandError::ConversionError)?;
 
     // FIXME:
     // Handle include as expression
@@ -331,6 +337,30 @@ fn include_expand(
     Ok((res, FragmentKind::Items))
 }
 
+fn include_str_expand(
+    db: &dyn AstDatabase,
+    arg_id: EagerMacroId,
+    tt: &tt::Subtree,
+) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
+    let path = parse_string(tt)?;
+
+    // FIXME: we're not able to read excluded files (which is most of them because
+    // it's unusual to `include_str!` a Rust file), but we can return an empty string.
+    // Ideally, we'd be able to offer a precise expansion if the user asks for macro
+    // expansion.
+    let file_id = match relative_file(db, arg_id.into(), &path, true) {
+        Some(file_id) => file_id,
+        None => {
+            return Ok((quote!(""), FragmentKind::Expr));
+        }
+    };
+
+    let text = db.file_text(file_id);
+    let text = &*text;
+
+    Ok((quote!(#text), FragmentKind::Expr))
+}
+
 fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
     let krate = db.lookup_intern_eager_expansion(arg_id).krate;
     db.crate_graph()[krate].env.get(key)
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 660bdfe336..b475c8cc72 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -191,6 +191,7 @@ pub mod known {
         stringify,
         concat,
         include,
+        include_str,
         format_args,
         format_args_nl,
         env,
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index a59efb3476..ebd9cb08f9 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -169,31 +169,3 @@ impl AstDiagnostic for BreakOutsideOfLoop {
         ast::Expr::cast(node).unwrap()
     }
 }
-
-#[derive(Debug)]
-pub struct MissingUnsafe {
-    pub file: HirFileId,
-    pub expr: AstPtr<ast::Expr>,
-}
-
-impl Diagnostic for MissingUnsafe {
-    fn message(&self) -> String {
-        format!("This operation is unsafe and requires an unsafe function or block")
-    }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
-        InFile { file_id: self.file, value: self.expr.clone().into() }
-    }
-    fn as_any(&self) -> &(dyn Any + Send + 'static) {
-        self
-    }
-}
-
-impl AstDiagnostic for MissingUnsafe {
-    type AST = ast::Expr;
-
-    fn ast(&self, db: &impl AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.source().file_id).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::Expr::cast(node).unwrap()
-    }
-}
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 61af5f0645..a9565a58d1 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -142,7 +142,6 @@ impl<'a> InferenceContext<'a> {
                 // FIXME: Breakable block inference
                 self.infer_block(statements, *tail, expected)
             }
-            Expr::Unsafe { body } => self.infer_expr(*body, expected),
             Expr::TryBlock { body } => {
                 let _inner = self.infer_expr(*body, expected);
                 // FIXME should be std::result::Result<{inner}, _>
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 4141581393..f222323249 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -37,7 +37,6 @@ pub(crate) mod utils;
 pub mod db;
 pub mod diagnostics;
 pub mod expr;
-pub mod unsafe_validation;
 
 #[cfg(test)]
 mod tests;
diff --git a/crates/ra_hir_ty/src/unsafe_validation.rs b/crates/ra_hir_ty/src/unsafe_validation.rs
deleted file mode 100644
index c512c4f8e9..0000000000
--- a/crates/ra_hir_ty/src/unsafe_validation.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
-//! unsafe blocks.
-
-use std::sync::Arc;
-
-use hir_def::{
-    body::Body,
-    expr::{Expr, ExprId, UnaryOp},
-    DefWithBodyId, FunctionId,
-};
-use hir_expand::diagnostics::DiagnosticSink;
-
-use crate::{
-    db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDef, ApplicationTy,
-    InferenceResult, Ty, TypeCtor,
-};
-
-pub struct UnsafeValidator<'a, 'b: 'a> {
-    func: FunctionId,
-    infer: Arc<InferenceResult>,
-    sink: &'a mut DiagnosticSink<'b>,
-}
-
-impl<'a, 'b> UnsafeValidator<'a, 'b> {
-    pub fn new(
-        func: FunctionId,
-        infer: Arc<InferenceResult>,
-        sink: &'a mut DiagnosticSink<'b>,
-    ) -> UnsafeValidator<'a, 'b> {
-        UnsafeValidator { func, infer, sink }
-    }
-
-    pub fn validate_body(&mut self, db: &dyn HirDatabase) {
-        let def = self.func.into();
-        let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
-        let func_data = db.function_data(self.func);
-        if func_data.is_unsafe
-            || unsafe_expressions
-                .iter()
-                .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
-                .count()
-                == 0
-        {
-            return;
-        }
-
-        let (_, body_source) = db.body_with_source_map(def);
-        for unsafe_expr in unsafe_expressions {
-            if !unsafe_expr.inside_unsafe_block {
-                if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) {
-                    self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
-                }
-            }
-        }
-    }
-}
-
-pub struct UnsafeExpr {
-    pub expr: ExprId,
-    pub inside_unsafe_block: bool,
-}
-
-pub fn unsafe_expressions(
-    db: &dyn HirDatabase,
-    infer: &InferenceResult,
-    def: DefWithBodyId,
-) -> Vec<UnsafeExpr> {
-    let mut unsafe_exprs = vec![];
-    let body = db.body(def);
-    walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false);
-
-    unsafe_exprs
-}
-
-fn walk_unsafe(
-    unsafe_exprs: &mut Vec<UnsafeExpr>,
-    db: &dyn HirDatabase,
-    infer: &InferenceResult,
-    body: &Body,
-    current: ExprId,
-    inside_unsafe_block: bool,
-) {
-    let expr = &body.exprs[current];
-    match expr {
-        Expr::Call { callee, .. } => {
-            let ty = &infer[*callee];
-            if let &Ty::Apply(ApplicationTy {
-                ctor: TypeCtor::FnDef(CallableDef::FunctionId(func)),
-                ..
-            }) = ty
-            {
-                if db.function_data(func).is_unsafe {
-                    unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
-                }
-            }
-        }
-        Expr::MethodCall { .. } => {
-            if infer
-                .method_resolution(current)
-                .map(|func| db.function_data(func).is_unsafe)
-                .unwrap_or(false)
-            {
-                unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
-            }
-        }
-        Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
-            if let Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }) = &infer[*expr] {
-                unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
-            }
-        }
-        Expr::Unsafe { body: child } => {
-            return walk_unsafe(unsafe_exprs, db, infer, body, *child, true);
-        }
-        _ => {}
-    }
-
-    expr.walk_child_exprs(|child| {
-        walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block);
-    });
-}
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 13d9dd195a..e8e251cfc0 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -25,6 +25,7 @@ pub enum HighlightTag {
     EnumVariant,
     EscapeSequence,
     Field,
+    FormatSpecifier,
     Function,
     Generic,
     Keyword,
@@ -32,6 +33,7 @@ pub enum HighlightTag {
     Macro,
     Module,
     NumericLiteral,
+    Operator,
     SelfKeyword,
     SelfType,
     Static,
@@ -43,8 +45,6 @@ pub enum HighlightTag {
     Union,
     Local,
     UnresolvedReference,
-    FormatSpecifier,
-    Operator,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index b763958fee..0dc1dc2ae2 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -57,7 +57,7 @@ To disable this notification put the following to `settings.json`
 The server binary is stored in:
 
 * Linux: `~/.config/Code/User/globalStorage/matklad.rust-analyzer`
-* macOS: `~/Library/Application\ Support/Code/User/globalStorage/matklad.rust-analyzer`
+* macOS: `~/Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer`
 * Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer`
 
 Note that we only support two most recent versions of VS Code.


Test patch:
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 9c2c6959dd..ad04e3e0f9 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -11,10 +11,7 @@ use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDataba
 use rustc_hash::FxHashSet;
 use stdx::format_to;
 
-use crate::{
-    db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator,
-    unsafe_validation::UnsafeValidator,
-};
+use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
 
 #[salsa::database(
     ra_db::SourceDatabaseExtStorage,
@@ -122,9 +119,7 @@ impl TestDB {
                 let infer = self.infer(f.into());
                 let mut sink = DiagnosticSink::new(&mut cb);
                 infer.add_diagnostics(self, f, &mut sink);
-                let mut validator = ExprValidator::new(f, infer.clone(), &mut sink);
-                validator.validate_body(self);
-                let mut validator = UnsafeValidator::new(f, infer, &mut sink);
+                let mut validator = ExprValidator::new(f, infer, &mut sink);
                 validator.validate_body(self);
             }
         }
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 2a85ce85d6..85ff26a368 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -538,155 +538,6 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
     assert_snapshot!(diagnostics, @"");
 }
 
-#[test]
-fn missing_unsafe_diagnostic_with_raw_ptr() {
-    let diagnostics = TestDB::with_files(
-        r"
-//- /lib.rs
-fn missing_unsafe() {
-    let x = &5 as *const usize;
-    let y = *x;
-}
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#);
-}
-
-#[test]
-fn missing_unsafe_diagnostic_with_unsafe_call() {
-    let diagnostics = TestDB::with_files(
-        r"
-//- /lib.rs
-unsafe fn unsafe_fn() {
-    let x = &5 as *const usize;
-    let y = *x;
-}
-
-fn missing_unsafe() {
-    unsafe_fn();
-}
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @r#""unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#);
-}
-
-#[test]
-fn missing_unsafe_diagnostic_with_unsafe_method_call() {
-    let diagnostics = TestDB::with_files(
-        r"
-struct HasUnsafe;
-
-impl HasUnsafe {
-    unsafe fn unsafe_fn(&self) {
-        let x = &5 as *const usize;
-        let y = *x;
-    }
-}
-
-fn missing_unsafe() {
-    HasUnsafe.unsafe_fn();
-}
-
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @r#""HasUnsafe.unsafe_fn()": This operation is unsafe and requires an unsafe function or block"#);
-}
-
-#[test]
-fn no_missing_unsafe_diagnostic_with_raw_ptr_in_unsafe_block() {
-    let diagnostics = TestDB::with_files(
-        r"
-fn nothing_to_see_move_along() {
-    let x = &5 as *const usize;
-    unsafe {
-        let y = *x;
-    }
-}
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @"");
-}
-
-#[test]
-fn missing_unsafe_diagnostic_with_raw_ptr_outside_unsafe_block() {
-    let diagnostics = TestDB::with_files(
-        r"
-fn nothing_to_see_move_along() {
-    let x = &5 as *const usize;
-    unsafe {
-        let y = *x;
-    }
-    let z = *x;
-}
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @r#""*x": This operation is unsafe and requires an unsafe function or block"#);
-}
-
-#[test]
-fn no_missing_unsafe_diagnostic_with_unsafe_call_in_unsafe_block() {
-    let diagnostics = TestDB::with_files(
-        r"
-unsafe fn unsafe_fn() {
-    let x = &5 as *const usize;
-    let y = *x;
-}
-
-fn nothing_to_see_move_along() {
-    unsafe {
-        unsafe_fn();
-    }
-}
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @"");
-}
-
-#[test]
-fn no_missing_unsafe_diagnostic_with_unsafe_method_call_in_unsafe_block() {
-    let diagnostics = TestDB::with_files(
-        r"
-struct HasUnsafe;
-
-impl HasUnsafe {
-    unsafe fn unsafe_fn() {
-        let x = &5 as *const usize;
-        let y = *x;
-    }
-}
-
-fn nothing_to_see_move_along() {
-    unsafe {
-        HasUnsafe.unsafe_fn();
-    }
-}
-
-",
-    )
-    .diagnostics()
-    .0;
-
-    assert_snapshot!(diagnostics, @"");
-}
-
 #[test]
 fn break_outside_of_loop() {
     let diagnostics = TestDB::with_files(
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index cd919466f4..7d8197f8b0 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1880,7 +1880,6 @@ fn main() {
         @r###"
     10..130 '{     ...2 }; }': ()
     20..21 'x': i32
-    24..37 'unsafe { 92 }': i32
     31..37 '{ 92 }': i32
     33..35 '92': i32
     47..48 'y': {unknown}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 58839b14aa..cc079790ee 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -494,6 +494,7 @@ fn main() {
 }
 //- /src/main.rs
 #[rustc_builtin_macro] macro_rules! include {}
+#[rustc_builtin_macro] macro_rules! include_str {}
 #[rustc_builtin_macro] macro_rules! concat {}
 #[rustc_builtin_macro] macro_rules! env {}
 
@@ -512,6 +513,7 @@ fn main() {
     let va = A;
     let vb = B;
     let should_be_str = message();
+    let another_str = include_str!("main.rs");
 }
 "###,
     )
@@ -523,7 +525,15 @@ fn main() {
     let res = server.send_request::<HoverRequest>(HoverParams {
         text_document_position_params: TextDocumentPositionParams::new(
             server.doc_id("src/main.rs"),
-            Position::new(18, 10),
+            Position::new(19, 10),
+        ),
+        work_done_progress_params: Default::default(),
+    });
+    assert!(res.to_string().contains("&str"));
+    let res = server.send_request::<HoverRequest>(HoverParams {
+        text_document_position_params: TextDocumentPositionParams::new(
+            server.doc_id("src/main.rs"),
+            Position::new(20, 10),
         ),
         work_done_progress_params: Default::default(),
     });
@@ -532,23 +542,23 @@ fn main() {
         GotoDefinitionParams {
             text_document_position_params: TextDocumentPositionParams::new(
                 server.doc_id("src/main.rs"),
-                Position::new(16, 9),
+                Position::new(17, 9),
             ),
             work_done_progress_params: Default::default(),
             partial_result_params: Default::default(),
         },
         json!([{
             "originSelectionRange": {
-                "end": { "character": 10, "line": 16 },
-                "start": { "character": 8, "line": 16 }
+                "end": { "character": 10, "line": 17 },
+                "start": { "character": 8, "line": 17 }
             },
             "targetRange": {
-                "end": { "character": 9, "line": 7 },
-                "start": { "character": 0, "line": 6 }
+                "end": { "character": 9, "line": 8 },
+                "start": { "character": 0, "line": 7 }
             },
             "targetSelectionRange": {
-                "end": { "character": 8, "line": 7 },
-                "start": { "character": 7, "line": 7 }
+                "end": { "character": 8, "line": 8 },
+                "start": { "character": 7, "line": 8 }
             },
             "targetUri": "file:///[..]src/main.rs"
         }]),
@@ -557,23 +567,23 @@ fn main() {
         GotoDefinitionParams {
             text_document_position_params: TextDocumentPositionParams::new(
                 server.doc_id("src/main.rs"),
-                Position::new(17, 9),
+                Position::new(18, 9),
             ),
             work_done_progress_params: Default::default(),
             partial_result_params: Default::default(),
         },
         json!([{
             "originSelectionRange": {
-                "end": { "character": 10, "line": 17 },
-                "start": { "character": 8, "line": 17 }
+                "end": { "character": 10, "line": 18 },
+                "start": { "character": 8, "line": 18 }
             },
             "targetRange": {
-                "end": { "character": 9, "line": 11 },
-                "start": { "character": 0, "line":10 }
+                "end": { "character": 9, "line": 12 },
+                "start": { "character": 0, "line":11 }
             },
             "targetSelectionRange": {
-                "end": { "character": 8, "line": 11 },
-                "start": { "character": 7, "line": 11 }
+                "end": { "character": 8, "line": 12 },
+                "start": { "character": 7, "line": 12 }
             },
             "targetUri": "file:///[..]src/main.rs"
         }]),