-use hir::db::HirDatabase;\r
-use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit};\r
-\r
-use crate::{Assist, AssistCtx, AssistId};\r
-\r
-pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {\r
- let literal = ctx.node_at_offset::<Literal>()?;\r
- if literal.token().kind() != ra_syntax::SyntaxKind::STRING {\r
- return None;\r
- }\r
- ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| {\r
- edit.target(literal.syntax().text_range());\r
- edit.insert(literal.syntax().text_range().start(), "r");\r
- });\r
- ctx.build()\r
-}\r
-\r
-fn find_usual_string_range(s: &str) -> Option<TextRange> {\r
- Some(TextRange::from_to(\r
- TextUnit::from(s.find('"')? as u32),\r
- TextUnit::from(s.rfind('"')? as u32),\r
- ))\r
-}\r
-\r
-pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {\r
- let literal = ctx.node_at_offset::<Literal>()?;\r
- if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {\r
- return None;\r
- }\r
- let token = literal.token();\r
- let text = token.text().as_str();\r
- let usual_string_range = find_usual_string_range(text)?;\r
- ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| {\r
- edit.target(literal.syntax().text_range());\r
- // parse inside string to escape `"`\r
- let start_of_inside = usual_string_range.start().to_usize() + 1;\r
- let end_of_inside = usual_string_range.end().to_usize();\r
- let inside_str = &text[start_of_inside..end_of_inside];\r
- let escaped = inside_str.escape_default().to_string();\r
- edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped));\r
- });\r
- ctx.build()\r
-}\r
-\r
-pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {\r
- let literal = ctx.node_at_offset::<Literal>()?;\r
- if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {\r
- return None;\r
- }\r
- ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| {\r
- edit.target(literal.syntax().text_range());\r
- edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#");\r
- edit.insert(literal.syntax().text_range().end(), "#");\r
- });\r
- ctx.build()\r
-}\r
-\r
-pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {\r
- let literal = ctx.node_at_offset::<Literal>()?;\r
- if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {\r
- return None;\r
- }\r
- let token = literal.token();\r
- let text = token.text().as_str();\r
- if text.starts_with("r\"") {\r
- // no hash to remove\r
- return None;\r
- }\r
- ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| {\r
- edit.target(literal.syntax().text_range());\r
- let result = &text[2..text.len() - 1];\r
- let result = if result.starts_with("\"") {\r
- // no more hash, escape\r
- let internal_str = &result[1..result.len() - 1];\r
- format!("\"{}\"", internal_str.escape_default().to_string())\r
- } else {\r
- result.to_owned()\r
- };\r
- edit.replace(literal.syntax().text_range(), format!("r{}", result));\r
- });\r
- ctx.build()\r
-}\r
-\r
-#[cfg(test)]\r
-mod test {\r
- use super::*;\r
- use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};\r
-\r
- #[test]\r
- fn make_raw_string_target() {\r
- check_assist_target(\r
- make_raw_string,\r
- r#"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "#,\r
- r#""random string""#,\r
- );\r
- }\r
-\r
- #[test]\r
- fn make_raw_string_works() {\r
- check_assist(\r
- make_raw_string,\r
- r#"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "#,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random string";\r
- }\r
- "#,\r
- )\r
- }\r
-\r
- #[test]\r
- fn make_raw_string_with_escaped_works() {\r
- check_assist(\r
- make_raw_string,\r
- r#"\r
- fn f() {\r
- let s = <|>"random\nstring";\r
- }\r
- "#,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random\nstring";\r
- }\r
- "#,\r
- )\r
- }\r
-\r
- #[test]\r
- fn make_raw_string_not_works() {\r
- check_assist_not_applicable(\r
- make_raw_string,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random string";\r
- }\r
- "#,\r
- );\r
- }\r
-\r
- #[test]\r
- fn add_hash_target() {\r
- check_assist_target(\r
- add_hash,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random string";\r
- }\r
- "#,\r
- r#"r"random string""#,\r
- );\r
- }\r
-\r
- #[test]\r
- fn add_hash_works() {\r
- check_assist(\r
- add_hash,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random string";\r
- }\r
- "#,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random string"#;\r
- }\r
- "##,\r
- )\r
- }\r
-\r
- #[test]\r
- fn add_more_hash_works() {\r
- check_assist(\r
- add_hash,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random"string"#;\r
- }\r
- "##,\r
- r###"\r
- fn f() {\r
- let s = <|>r##"random"string"##;\r
- }\r
- "###,\r
- )\r
- }\r
-\r
- #[test]\r
- fn add_hash_not_works() {\r
- check_assist_not_applicable(\r
- add_hash,\r
- r#"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "#,\r
- );\r
- }\r
-\r
- #[test]\r
- fn remove_hash_target() {\r
- check_assist_target(\r
- remove_hash,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random string"#;\r
- }\r
- "##,\r
- r##"r#"random string"#"##,\r
- );\r
- }\r
-\r
- #[test]\r
- fn remove_hash_works() {\r
- check_assist(\r
- remove_hash,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random string"#;\r
- }\r
- "##,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random string";\r
- }\r
- "#,\r
- )\r
- }\r
-\r
- #[test]\r
- fn remove_hash_with_quote_works() {\r
- check_assist(\r
- remove_hash,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random"str"ing"#;\r
- }\r
- "##,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random\"str\"ing";\r
- }\r
- "#,\r
- )\r
- }\r
-\r
- #[test]\r
- fn remove_more_hash_works() {\r
- check_assist(\r
- remove_hash,\r
- r###"\r
- fn f() {\r
- let s = <|>r##"random string"##;\r
- }\r
- "###,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random string"#;\r
- }\r
- "##,\r
- )\r
- }\r
-\r
- #[test]\r
- fn remove_hash_not_works() {\r
- check_assist_not_applicable(\r
- remove_hash,\r
- r#"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "#,\r
- );\r
- }\r
-\r
- #[test]\r
- fn remove_hash_no_hash_not_works() {\r
- check_assist_not_applicable(\r
- remove_hash,\r
- r#"\r
- fn f() {\r
- let s = <|>r"random string";\r
- }\r
- "#,\r
- );\r
- }\r
-\r
- #[test]\r
- fn make_usual_string_target() {\r
- check_assist_target(\r
- make_usual_string,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random string"#;\r
- }\r
- "##,\r
- r##"r#"random string"#"##,\r
- );\r
- }\r
-\r
- #[test]\r
- fn make_usual_string_works() {\r
- check_assist(\r
- make_usual_string,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random string"#;\r
- }\r
- "##,\r
- r#"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "#,\r
- )\r
- }\r
-\r
- #[test]\r
- fn make_usual_string_with_quote_works() {\r
- check_assist(\r
- make_usual_string,\r
- r##"\r
- fn f() {\r
- let s = <|>r#"random"str"ing"#;\r
- }\r
- "##,\r
- r#"\r
- fn f() {\r
- let s = <|>"random\"str\"ing";\r
- }\r
- "#,\r
- )\r
- }\r
-\r
- #[test]\r
- fn make_usual_string_more_hash_works() {\r
- check_assist(\r
- make_usual_string,\r
- r###"\r
- fn f() {\r
- let s = <|>r##"random string"##;\r
- }\r
- "###,\r
- r##"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "##,\r
- )\r
- }\r
-\r
- #[test]\r
- fn make_usual_string_not_works() {\r
- check_assist_not_applicable(\r
- make_usual_string,\r
- r#"\r
- fn f() {\r
- let s = <|>"random string";\r
- }\r
- "#,\r
- );\r
- }\r
-}\r
+use hir::db::HirDatabase;
+use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit};
+
+use crate::{Assist, AssistCtx, AssistId};
+
+pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
+ let literal = ctx.node_at_offset::<Literal>()?;
+ if literal.token().kind() != ra_syntax::SyntaxKind::STRING {
+ return None;
+ }
+ ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| {
+ edit.target(literal.syntax().text_range());
+ edit.insert(literal.syntax().text_range().start(), "r");
+ });
+ ctx.build()
+}
+
+fn find_usual_string_range(s: &str) -> Option<TextRange> {
+ Some(TextRange::from_to(
+ TextUnit::from(s.find('"')? as u32),
+ TextUnit::from(s.rfind('"')? as u32),
+ ))
+}
+
+pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
+ let literal = ctx.node_at_offset::<Literal>()?;
+ if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
+ return None;
+ }
+ let token = literal.token();
+ let text = token.text().as_str();
+ let usual_string_range = find_usual_string_range(text)?;
+ ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| {
+ edit.target(literal.syntax().text_range());
+ // parse inside string to escape `"`
+ let start_of_inside = usual_string_range.start().to_usize() + 1;
+ let end_of_inside = usual_string_range.end().to_usize();
+ let inside_str = &text[start_of_inside..end_of_inside];
+ let escaped = inside_str.escape_default().to_string();
+ edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped));
+ });
+ ctx.build()
+}
+
+pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
+ let literal = ctx.node_at_offset::<Literal>()?;
+ if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
+ return None;
+ }
+ ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| {
+ edit.target(literal.syntax().text_range());
+ edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#");
+ edit.insert(literal.syntax().text_range().end(), "#");
+ });
+ ctx.build()
+}
+
+pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
+ let literal = ctx.node_at_offset::<Literal>()?;
+ if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING {
+ return None;
+ }
+ let token = literal.token();
+ let text = token.text().as_str();
+ if text.starts_with("r\"") {
+ // no hash to remove
+ return None;
+ }
+ ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| {
+ edit.target(literal.syntax().text_range());
+ let result = &text[2..text.len() - 1];
+ let result = if result.starts_with("\"") {
+ // no more hash, escape
+ let internal_str = &result[1..result.len() - 1];
+ format!("\"{}\"", internal_str.escape_default().to_string())
+ } else {
+ result.to_owned()
+ };
+ edit.replace(literal.syntax().text_range(), format!("r{}", result));
+ });
+ ctx.build()
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
+
+ #[test]
+ fn make_raw_string_target() {
+ check_assist_target(
+ make_raw_string,
+ r#"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "#,
+ r#""random string""#,
+ );
+ }
+
+ #[test]
+ fn make_raw_string_works() {
+ check_assist(
+ make_raw_string,
+ r#"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "#,
+ r#"
+ fn f() {
+ let s = <|>r"random string";
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn make_raw_string_with_escaped_works() {
+ check_assist(
+ make_raw_string,
+ r#"
+ fn f() {
+ let s = <|>"random\nstring";
+ }
+ "#,
+ r#"
+ fn f() {
+ let s = <|>r"random\nstring";
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn make_raw_string_not_works() {
+ check_assist_not_applicable(
+ make_raw_string,
+ r#"
+ fn f() {
+ let s = <|>r"random string";
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn add_hash_target() {
+ check_assist_target(
+ add_hash,
+ r#"
+ fn f() {
+ let s = <|>r"random string";
+ }
+ "#,
+ r#"r"random string""#,
+ );
+ }
+
+ #[test]
+ fn add_hash_works() {
+ check_assist(
+ add_hash,
+ r#"
+ fn f() {
+ let s = <|>r"random string";
+ }
+ "#,
+ r##"
+ fn f() {
+ let s = <|>r#"random string"#;
+ }
+ "##,
+ )
+ }
+
+ #[test]
+ fn add_more_hash_works() {
+ check_assist(
+ add_hash,
+ r##"
+ fn f() {
+ let s = <|>r#"random"string"#;
+ }
+ "##,
+ r###"
+ fn f() {
+ let s = <|>r##"random"string"##;
+ }
+ "###,
+ )
+ }
+
+ #[test]
+ fn add_hash_not_works() {
+ check_assist_not_applicable(
+ add_hash,
+ r#"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn remove_hash_target() {
+ check_assist_target(
+ remove_hash,
+ r##"
+ fn f() {
+ let s = <|>r#"random string"#;
+ }
+ "##,
+ r##"r#"random string"#"##,
+ );
+ }
+
+ #[test]
+ fn remove_hash_works() {
+ check_assist(
+ remove_hash,
+ r##"
+ fn f() {
+ let s = <|>r#"random string"#;
+ }
+ "##,
+ r#"
+ fn f() {
+ let s = <|>r"random string";
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn remove_hash_with_quote_works() {
+ check_assist(
+ remove_hash,
+ r##"
+ fn f() {
+ let s = <|>r#"random"str"ing"#;
+ }
+ "##,
+ r#"
+ fn f() {
+ let s = <|>r"random\"str\"ing";
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn remove_more_hash_works() {
+ check_assist(
+ remove_hash,
+ r###"
+ fn f() {
+ let s = <|>r##"random string"##;
+ }
+ "###,
+ r##"
+ fn f() {
+ let s = <|>r#"random string"#;
+ }
+ "##,
+ )
+ }
+
+ #[test]
+ fn remove_hash_not_works() {
+ check_assist_not_applicable(
+ remove_hash,
+ r#"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn remove_hash_no_hash_not_works() {
+ check_assist_not_applicable(
+ remove_hash,
+ r#"
+ fn f() {
+ let s = <|>r"random string";
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn make_usual_string_target() {
+ check_assist_target(
+ make_usual_string,
+ r##"
+ fn f() {
+ let s = <|>r#"random string"#;
+ }
+ "##,
+ r##"r#"random string"#"##,
+ );
+ }
+
+ #[test]
+ fn make_usual_string_works() {
+ check_assist(
+ make_usual_string,
+ r##"
+ fn f() {
+ let s = <|>r#"random string"#;
+ }
+ "##,
+ r#"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn make_usual_string_with_quote_works() {
+ check_assist(
+ make_usual_string,
+ r##"
+ fn f() {
+ let s = <|>r#"random"str"ing"#;
+ }
+ "##,
+ r#"
+ fn f() {
+ let s = <|>"random\"str\"ing";
+ }
+ "#,
+ )
+ }
+
+ #[test]
+ fn make_usual_string_more_hash_works() {
+ check_assist(
+ make_usual_string,
+ r###"
+ fn f() {
+ let s = <|>r##"random string"##;
+ }
+ "###,
+ r##"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "##,
+ )
+ }
+
+ #[test]
+ fn make_usual_string_not_works() {
+ check_assist_not_applicable(
+ make_usual_string,
+ r#"
+ fn f() {
+ let s = <|>"random string";
+ }
+ "#,
+ );
+ }
+}
}
}
+ /// This is only for `generic_predicates_for_param`, where we can't just
+ /// lower the self types of the predicates since that could lead to cycles.
+ /// So we just check here if the `type_ref` resolves to a generic param, and which.
+ fn from_hir_only_param(
+ db: &impl HirDatabase,
+ resolver: &Resolver,
+ type_ref: &TypeRef,
+ ) -> Option<u32> {
+ let path = match type_ref {
+ TypeRef::Path(path) => path,
+ _ => return None,
+ };
+ if let crate::PathKind::Type(_) = &path.kind {
+ return None;
+ }
+ if path.segments.len() > 1 {
+ return None;
+ }
+ let resolution = match resolver.resolve_path_in_type_ns(db, path) {
+ Some((it, None)) => it,
+ _ => return None,
+ };
+ if let TypeNs::GenericParam(idx) = resolution {
+ Some(idx)
+ } else {
+ None
+ }
+ }
+
pub(crate) fn from_type_relative_path(
db: &impl HirDatabase,
resolver: &Resolver,
}
fn select_associated_type(
- _db: &impl HirDatabase,
- _resolver: &Resolver,
- _self_ty: Ty,
- _segment: &PathSegment,
+ db: &impl HirDatabase,
+ resolver: &Resolver,
+ self_ty: Ty,
+ segment: &PathSegment,
) -> Ty {
+ let param_idx = match self_ty {
+ Ty::Param { idx, .. } => idx,
+ _ => return Ty::Unknown, // Error: Ambiguous associated type
+ };
+ let def = match resolver.generic_def() {
+ Some(def) => def,
+ None => return Ty::Unknown, // this can't actually happen
+ };
+ let predicates = db.generic_predicates_for_param(def, param_idx);
+ let traits_from_env = predicates.iter().filter_map(|pred| match pred {
+ GenericPredicate::Implemented(tr) if tr.self_ty() == &self_ty => Some(tr.trait_),
+ _ => None,
+ });
+ let traits = traits_from_env.flat_map(|t| t.all_super_traits(db));
+ for t in traits {
+ if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) {
+ let generics = t.generic_params(db);
+ let mut substs = Vec::new();
+ substs.push(self_ty.clone());
+ substs.extend(
+ iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1),
+ );
+ // FIXME handle type parameters on the segment
+ return Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() });
+ }
+ }
Ty::Unknown
}
add_self_param: bool,
) -> Substs {
let mut substs = Vec::new();
- let def_generics = def_generic.map(|def| def.generic_params(db)).unwrap_or_default();
+ let def_generics = def_generic.map(|def| def.generic_params(db));
- let parent_param_count = def_generics.count_parent_params();
+ let (parent_param_count, param_count) =
+ def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len()));
substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count));
if add_self_param {
// FIXME this add_self_param argument is kind of a hack: Traits have the
if let Some(generic_args) = &segment.args_and_bindings {
// if args are provided, it should be all of them, but we can't rely on that
let self_param_correction = if add_self_param { 1 } else { 0 };
- let param_count = def_generics.params.len() - self_param_correction;
+ let param_count = param_count - self_param_correction;
for arg in generic_args.args.iter().take(param_count) {
match arg {
GenericArg::Type(type_ref) => {
}
// add placeholders for args that were not provided
let supplied_params = substs.len();
- for _ in supplied_params..def_generics.count_params_including_parent() {
+ for _ in supplied_params..parent_param_count + param_count {
substs.push(Ty::Unknown);
}
- assert_eq!(substs.len(), def_generics.count_params_including_parent());
+ assert_eq!(substs.len(), parent_param_count + param_count);
// handle defaults
if let Some(def_generic) = def_generic {
Ty::from_hir(db, &resolver, type_ref)
}
+/// This query exists only to be used when resolving short-hand associated types
+/// like `T::Item`.
+///
+/// See the analogous query in rustc and its comment:
+/// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46
+/// This is a query mostly to handle cycles somewhat gracefully; e.g. the
+/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but
+/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
+pub(crate) fn generic_predicates_for_param_query(
+ db: &impl HirDatabase,
+ def: GenericDef,
+ param_idx: u32,
+) -> Arc<[GenericPredicate]> {
+ let resolver = def.resolver(db);
+ let predicates = resolver
+ .where_predicates_in_scope()
+ // we have to filter out all other predicates *first*, before attempting to lower them
+ .filter(|pred| Ty::from_hir_only_param(db, &resolver, &pred.type_ref) == Some(param_idx))
+ .flat_map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred))
+ .collect::<Vec<_>>();
+ predicates.into()
+}
+
pub(crate) fn trait_env(
db: &impl HirDatabase,
resolver: &Resolver,