]> git.lizzy.rs Git - rust.git/commitdiff
Prefix/suffix parameter inlay hint hiding heuristic is more strict
authorLukas Wirth <lukastw97@gmail.com>
Thu, 3 Jun 2021 14:22:24 +0000 (16:22 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Thu, 3 Jun 2021 14:22:24 +0000 (16:22 +0200)
crates/ide/src/inlay_hints.rs

index 85f8877371473f06daf7742ebecfdf38a2fac392..d6dfa0183989bfc4889e93892a5afad1eeb12ce8 100644 (file)
@@ -150,17 +150,11 @@ fn get_param_name_hints(
         return None;
     }
 
-    let args = match &expr {
-        ast::Expr::CallExpr(expr) => expr.arg_list()?.args(),
-        ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
-        _ => return None,
-    };
-
-    let callable = get_callable(sema, &expr)?;
+    let (callable, arg_list) = get_callable(sema, &expr)?;
     let hints = callable
         .params(sema.db)
         .into_iter()
-        .zip(args)
+        .zip(arg_list.args())
         .filter_map(|((param, _ty), arg)| {
             let param_name = match param? {
                 Either::Left(_) => "self".to_string(),
@@ -171,7 +165,7 @@ fn get_param_name_hints(
             };
             Some((param_name, arg))
         })
-        .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg))
+        .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, &arg))
         .map(|(param_name, arg)| InlayHint {
             range: arg.syntax().text_range(),
             kind: InlayKind::ParameterHint,
@@ -289,15 +283,9 @@ fn should_not_display_type_hint(
     for node in bind_pat.syntax().ancestors() {
         match_ast! {
             match node {
-                ast::LetStmt(it) => {
-                    return it.ty().is_some()
-                },
-                ast::Param(it) => {
-                    return it.ty().is_some()
-                },
-                ast::MatchArm(_it) => {
-                    return pat_is_enum_variant(db, bind_pat, pat_ty);
-                },
+                ast::LetStmt(it) => return it.ty().is_some(),
+                ast::Param(it) => return it.ty().is_some(),
+                ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
                 ast::IfExpr(it) => {
                     return it.condition().and_then(|condition| condition.pat()).is_some()
                         && pat_is_enum_variant(db, bind_pat, pat_ty);
@@ -322,76 +310,66 @@ fn should_not_display_type_hint(
     false
 }
 
-fn should_show_param_name_hint(
+fn should_hide_param_name_hint(
     sema: &Semantics<RootDatabase>,
     callable: &hir::Callable,
     param_name: &str,
     argument: &ast::Expr,
 ) -> bool {
+    // hide when:
+    // - the parameter name is a suffix of the function's name
+    // - the argument is an enum whose name is equal to the parameter
+    // - exact argument<->parameter match(ignoring leading underscore) or argument is a prefix/suffix
+    //   of parameter with _ splitting it off
+    // - param starts with `ra_fixture`
+    // - param is a well known name in an unary function
+
     let param_name = param_name.trim_start_matches('_');
+    if param_name.is_empty() {
+        return true;
+    }
+
     let fn_name = match callable.kind() {
         hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
-        hir::CallableKind::TupleStruct(_)
-        | hir::CallableKind::TupleEnumVariant(_)
-        | hir::CallableKind::Closure => None,
+        _ => None,
     };
-
-    if param_name.is_empty()
-        || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
-        || is_argument_similar_to_param_name(sema, argument, param_name)
-        || is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
+    let fn_name = fn_name.as_deref();
+    is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
+        || is_enum_name_similar_to_param_name(sema, argument, param_name)
+        || is_argument_similar_to_param_name(argument, param_name)
         || param_name.starts_with("ra_fixture")
-    {
-        return false;
-    }
-
-    // avoid displaying hints for common functions like map, filter, etc.
-    // or other obvious words used in std
-    !(callable.n_params() == 1 && is_obvious_param(param_name))
+        || (callable.n_params() == 1 && is_obvious_param(param_name))
 }
 
-fn is_argument_similar_to_param_name(
-    sema: &Semantics<RootDatabase>,
-    argument: &ast::Expr,
-    param_name: &str,
-) -> bool {
-    if is_enum_name_similar_to_param_name(sema, argument, param_name) {
-        return true;
-    }
+fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
     match get_string_representation(argument) {
         None => false,
-        Some(argument_string) => {
-            let num_leading_underscores =
-                argument_string.bytes().take_while(|&c| c == b'_').count();
-
-            // Does the argument name begin with the parameter name? Ignore leading underscores.
-            let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
-            let starts_with_pattern = param_name.bytes().all(
-                |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
-            );
-
-            if starts_with_pattern {
-                return true;
+        Some(argument) => {
+            let mut res = false;
+            if let Some(first) = argument.bytes().skip_while(|&c| c == b'_').position(|c| c == b'_')
+            {
+                res |= param_name == argument[..first].trim_start_matches('_');
             }
-
-            // Does the argument name end with the parameter name?
-            let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
-            param_name.bytes().rev().all(
-                |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
-            )
+            if let Some(last) =
+                argument.bytes().rev().skip_while(|&c| c == b'_').position(|c| c == b'_')
+            {
+                res |= param_name == argument[last..].trim_end_matches('_');
+            }
+            res |= argument == param_name;
+            res
         }
     }
 }
 
-fn is_param_name_similar_to_fn_name(
+/// Hide the parameter name of an unary function if it is a `_` - prefixed suffix of the function's name, or equal.
+///
+/// `fn strip_suffix(suffix)` will be hidden.
+/// `fn stripsuffix(suffix)` will not be hidden.
+fn is_param_name_suffix_of_fn_name(
     param_name: &str,
     callable: &Callable,
-    fn_name: Option<&String>,
+    fn_name: Option<&str>,
 ) -> bool {
-    // if it's the only parameter, don't show it if:
-    // - is the same as the function name, or
-    // - the function ends with '_' + param_name
-
     match (callable.n_params(), fn_name) {
         (1, Some(function)) => {
             function == param_name
@@ -424,7 +402,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
             }
         }
         ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
-        ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()),
+        ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
         ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
         ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
         _ => None,
@@ -432,15 +410,24 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
 }
 
 fn is_obvious_param(param_name: &str) -> bool {
+    // avoid displaying hints for common functions like map, filter, etc.
+    // or other obvious words used in std
     let is_obvious_param_name =
         matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
     param_name.len() == 1 || is_obvious_param_name
 }
 
-fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> {
+fn get_callable(
+    sema: &Semantics<RootDatabase>,
+    expr: &ast::Expr,
+) -> Option<(hir::Callable, ast::ArgList)> {
     match expr {
-        ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
-        ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
+        ast::Expr::CallExpr(expr) => {
+            sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list())
+        }
+        ast::Expr::MethodCallExpr(expr) => {
+            sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
+        }
         _ => None,
     }
 }