mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-31 09:04:18 +00:00
Basic injections
This commit is contained in:
parent
8ed7e751b6
commit
c6247f74c7
@ -51,8 +51,8 @@ fn type_at(content: &str) -> String {
|
||||
type_at_pos(&db, file_pos)
|
||||
}
|
||||
|
||||
fn infer(content: &str) -> String {
|
||||
infer_with_mismatches(content, false)
|
||||
fn infer(ra_fixture: &str) -> String {
|
||||
infer_with_mismatches(ra_fixture, false)
|
||||
}
|
||||
|
||||
fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
|
||||
|
@ -3,7 +3,7 @@ use hir::Semantics;
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
ast::{self, ArgListOwner},
|
||||
match_ast, AstNode, SyntaxNode,
|
||||
match_ast, AstNode, SyntaxNode, SyntaxToken,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
|
||||
@ -16,7 +16,13 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
let file = file.syntax();
|
||||
let token = file.token_at_offset(position.offset).next()?;
|
||||
let token = sema.descend_into_macros(token);
|
||||
call_info_for_token(&sema, token)
|
||||
}
|
||||
|
||||
pub(crate) fn call_info_for_token(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
token: SyntaxToken,
|
||||
) -> Option<CallInfo> {
|
||||
// Find the calling expression and it's NameRef
|
||||
let calling_node = FnCallNode::with_node(&token.parent())?;
|
||||
|
||||
@ -27,21 +33,23 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
match callable_def {
|
||||
hir::CallableDef::FunctionId(it) => {
|
||||
let fn_def = it.into();
|
||||
(CallInfo::with_fn(db, fn_def), fn_def.has_self_param(db))
|
||||
(CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db))
|
||||
}
|
||||
hir::CallableDef::StructId(it) => {
|
||||
(CallInfo::with_struct(sema.db, it.into())?, false)
|
||||
}
|
||||
hir::CallableDef::StructId(it) => (CallInfo::with_struct(db, it.into())?, false),
|
||||
hir::CallableDef::EnumVariantId(it) => {
|
||||
(CallInfo::with_enum_variant(db, it.into())?, false)
|
||||
(CallInfo::with_enum_variant(sema.db, it.into())?, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
FnCallNode::MethodCallExpr(method_call) => {
|
||||
let function = sema.resolve_method_call(&method_call)?;
|
||||
(CallInfo::with_fn(db, function), function.has_self_param(db))
|
||||
(CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
|
||||
}
|
||||
FnCallNode::MacroCallExpr(macro_call) => {
|
||||
let macro_def = sema.resolve_macro_call(¯o_call)?;
|
||||
(CallInfo::with_macro(db, macro_def)?, false)
|
||||
(CallInfo::with_macro(sema.db, macro_def)?, false)
|
||||
}
|
||||
};
|
||||
|
||||
@ -61,7 +69,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
let num_args_at_callsite = arg_list.args().count();
|
||||
|
||||
let arg_list_range = arg_list.syntax().text_range();
|
||||
if !arg_list_range.contains_inclusive(position.offset) {
|
||||
if !arg_list_range.contains_inclusive(token.text_range().start()) {
|
||||
tested_by!(call_info_bad_offset);
|
||||
return None;
|
||||
}
|
||||
@ -70,7 +78,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
num_args_at_callsite,
|
||||
arg_list
|
||||
.args()
|
||||
.take_while(|arg| arg.syntax().text_range().end() < position.offset)
|
||||
.take_while(|arg| {
|
||||
arg.syntax().text_range().end() < token.text_range().start()
|
||||
})
|
||||
.count(),
|
||||
);
|
||||
|
||||
@ -100,7 +110,13 @@ impl FnCallNode {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
|
||||
ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
|
||||
ast::MethodCallExpr(it) => {
|
||||
let arg_list = it.arg_list()?;
|
||||
if !syntax.text_range().is_subrange(&arg_list.syntax().text_range()) {
|
||||
return None;
|
||||
}
|
||||
Some(FnCallNode::MethodCallExpr(it))
|
||||
},
|
||||
ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
|
||||
_ => { None },
|
||||
}
|
||||
|
@ -124,28 +124,28 @@ impl MockAnalysis {
|
||||
}
|
||||
|
||||
/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
|
||||
pub fn analysis_and_position(fixture: &str) -> (Analysis, FilePosition) {
|
||||
let (mock, position) = MockAnalysis::with_files_and_position(fixture);
|
||||
pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) {
|
||||
let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
|
||||
(mock.analysis(), position)
|
||||
}
|
||||
|
||||
/// Creates analysis for a single file.
|
||||
pub fn single_file(code: &str) -> (Analysis, FileId) {
|
||||
pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) {
|
||||
let mut mock = MockAnalysis::new();
|
||||
let file_id = mock.add_file("/main.rs", code);
|
||||
let file_id = mock.add_file("/main.rs", ra_fixture);
|
||||
(mock.analysis(), file_id)
|
||||
}
|
||||
|
||||
/// Creates analysis for a single file, returns position marked with <|>.
|
||||
pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) {
|
||||
pub fn single_file_with_position(ra_fixture: &str) -> (Analysis, FilePosition) {
|
||||
let mut mock = MockAnalysis::new();
|
||||
let pos = mock.add_file_with_position("/main.rs", code);
|
||||
let pos = mock.add_file_with_position("/main.rs", ra_fixture);
|
||||
(mock.analysis(), pos)
|
||||
}
|
||||
|
||||
/// Creates analysis for a single file, returns range marked with a pair of <|>.
|
||||
pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) {
|
||||
pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) {
|
||||
let mut mock = MockAnalysis::new();
|
||||
let pos = mock.add_file_with_range("/main.rs", code);
|
||||
let pos = mock.add_file_with_range("/main.rs", ra_fixture);
|
||||
(mock.analysis(), pos)
|
||||
}
|
||||
|
@ -12,11 +12,12 @@ use ra_ide_db::{
|
||||
};
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, TextRange, WalkEvent, T,
|
||||
ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, SyntaxToken,
|
||||
TextRange, WalkEvent, T,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::{references::classify_name_ref, FileId};
|
||||
use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId};
|
||||
|
||||
pub(crate) use html::highlight_as_html;
|
||||
pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
|
||||
@ -94,11 +95,12 @@ pub(crate) fn highlight(
|
||||
WalkEvent::Enter(it) => it,
|
||||
WalkEvent::Leave(_) => continue,
|
||||
};
|
||||
|
||||
let range = element.text_range();
|
||||
|
||||
let element_to_highlight = if current_macro_call.is_some() {
|
||||
// Inside a macro -- expand it first
|
||||
let token = match element.into_token() {
|
||||
let token = match element.clone().into_token() {
|
||||
Some(it) if it.parent().kind() == TOKEN_TREE => it,
|
||||
_ => continue,
|
||||
};
|
||||
@ -110,9 +112,17 @@ pub(crate) fn highlight(
|
||||
_ => token.into(),
|
||||
}
|
||||
} else {
|
||||
element
|
||||
element.clone()
|
||||
};
|
||||
|
||||
if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
|
||||
let expanded = element_to_highlight.as_token().unwrap().clone();
|
||||
if highlight_injection(&mut res, &sema, token, expanded).is_some() {
|
||||
eprintln!("res = {:?}", res);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((highlight, binding_hash)) =
|
||||
highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight)
|
||||
{
|
||||
@ -281,3 +291,44 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
|
||||
_ => default,
|
||||
}
|
||||
}
|
||||
|
||||
fn highlight_injection(
|
||||
acc: &mut Vec<HighlightedRange>,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
literal: ast::RawString,
|
||||
expanded: SyntaxToken,
|
||||
) -> Option<()> {
|
||||
let call_info = call_info_for_token(&sema, expanded)?;
|
||||
let idx = call_info.active_parameter?;
|
||||
let name = call_info.signature.parameter_names.get(idx)?;
|
||||
if name != "ra_fixture" {
|
||||
return None;
|
||||
}
|
||||
let value = literal.value()?;
|
||||
let (analysis, tmp_file_id) = Analysis::from_single_file(value);
|
||||
|
||||
if let Some(range) = literal.open_quote_text_range() {
|
||||
acc.push(HighlightedRange {
|
||||
range,
|
||||
highlight: HighlightTag::LiteralString.into(),
|
||||
binding_hash: None,
|
||||
})
|
||||
}
|
||||
|
||||
for mut h in analysis.highlight(tmp_file_id).unwrap() {
|
||||
if let Some(r) = literal.map_range_up(h.range) {
|
||||
h.range = r;
|
||||
acc.push(h)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(range) = literal.close_quote_text_range() {
|
||||
acc.push(HighlightedRange {
|
||||
range,
|
||||
highlight: HighlightTag::LiteralString.into(),
|
||||
binding_hash: None,
|
||||
})
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
@ -171,6 +171,36 @@ impl RawString {
|
||||
let inside_str = &text[start_of_inside..end_of_inside];
|
||||
Some(inside_str.to_string())
|
||||
}
|
||||
|
||||
pub fn open_quote_text_range(&self) -> Option<TextRange> {
|
||||
let text = self.text().as_str();
|
||||
let usual_string_range = find_usual_string_range(text)?;
|
||||
|
||||
let start = self.syntax().text_range().start();
|
||||
let len = usual_string_range.start() + TextUnit::of_char('"');
|
||||
Some(TextRange::offset_len(start, len))
|
||||
}
|
||||
|
||||
pub fn close_quote_text_range(&self) -> Option<TextRange> {
|
||||
let text = self.text().as_str();
|
||||
let usual_string_range = find_usual_string_range(text)?;
|
||||
|
||||
let end = self.syntax().text_range().end();
|
||||
let len = TextUnit::of_str(text) - usual_string_range.end();
|
||||
Some(TextRange::from_to(end - len, end))
|
||||
}
|
||||
|
||||
pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
|
||||
// FIXME: handle escapes here properly
|
||||
let text = self.text().as_str();
|
||||
let usual_string_range = find_usual_string_range(text)?;
|
||||
Some(
|
||||
range
|
||||
+ self.syntax().text_range().start()
|
||||
+ TextUnit::of_char('"')
|
||||
+ usual_string_range.start(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_usual_string_range(s: &str) -> Option<TextRange> {
|
||||
|
Loading…
Reference in New Issue
Block a user