mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Add checks for function parameters
This commit is contained in:
parent
4039176ec6
commit
f5cea35986
2 changed files with 94 additions and 7 deletions
|
@ -269,6 +269,7 @@ pub struct IncorrectCase {
|
||||||
pub file: HirFileId,
|
pub file: HirFileId,
|
||||||
pub ident: SyntaxNodePtr,
|
pub ident: SyntaxNodePtr,
|
||||||
pub expected_case: CaseType,
|
pub expected_case: CaseType,
|
||||||
|
pub ident_type: String,
|
||||||
pub ident_text: String,
|
pub ident_text: String,
|
||||||
pub suggested_text: String,
|
pub suggested_text: String,
|
||||||
}
|
}
|
||||||
|
@ -280,7 +281,8 @@ impl Diagnostic for IncorrectCase {
|
||||||
|
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Argument `{}` should have a {} name, e.g. `{}`",
|
"{} `{}` should have a {} name, e.g. `{}`",
|
||||||
|
self.ident_type,
|
||||||
self.ident_text,
|
self.ident_text,
|
||||||
self.expected_case.to_string(),
|
self.expected_case.to_string(),
|
||||||
self.suggested_text
|
self.suggested_text
|
||||||
|
|
|
@ -21,7 +21,10 @@ use hir_def::{
|
||||||
AdtId, FunctionId, Lookup, ModuleDefId,
|
AdtId, FunctionId, Lookup, ModuleDefId,
|
||||||
};
|
};
|
||||||
use hir_expand::{diagnostics::DiagnosticSink, name::Name};
|
use hir_expand::{diagnostics::DiagnosticSink, name::Name};
|
||||||
use syntax::{ast::NameOwner, AstPtr};
|
use syntax::{
|
||||||
|
ast::{self, NameOwner},
|
||||||
|
AstPtr,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
|
@ -122,7 +125,8 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
} else {
|
} else {
|
||||||
// We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic.
|
// We don't want rust-analyzer to panic over this, but it is definitely some kind of error in the logic.
|
||||||
log::error!(
|
log::error!(
|
||||||
"Replacement was generated for a function without a name: {:?}",
|
"Replacement ({:?}) was generated for a function without a name: {:?}",
|
||||||
|
replacement,
|
||||||
fn_src
|
fn_src
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -130,6 +134,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
|
|
||||||
let diagnostic = IncorrectCase {
|
let diagnostic = IncorrectCase {
|
||||||
file: fn_src.file_id,
|
file: fn_src.file_id,
|
||||||
|
ident_type: "Function".to_string(),
|
||||||
ident: AstPtr::new(&ast_ptr).into(),
|
ident: AstPtr::new(&ast_ptr).into(),
|
||||||
expected_case: replacement.expected_case,
|
expected_case: replacement.expected_case,
|
||||||
ident_text: replacement.current_name.to_string(),
|
ident_text: replacement.current_name.to_string(),
|
||||||
|
@ -139,15 +144,71 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
self.sink.push(diagnostic);
|
self.sink.push(diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
// let item_tree = db.item_tree(loc.id.file_id);
|
let fn_params_list = match fn_src.value.param_list() {
|
||||||
// let fn_def = &item_tree[fn_loc.id.value];
|
Some(params) => params,
|
||||||
// let (_, source_map) = db.body_with_source_map(func.into());
|
None => {
|
||||||
|
if !fn_param_replacements.is_empty() {
|
||||||
|
log::error!(
|
||||||
|
"Replacements ({:?}) were generated for a function parameters which had no parameters list: {:?}",
|
||||||
|
fn_param_replacements, fn_src
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut fn_params_iter = fn_params_list.params();
|
||||||
|
for param_to_rename in fn_param_replacements {
|
||||||
|
// We assume that parameters in replacement are in the same order as in the
|
||||||
|
// actual params list, but just some of them (ones that named correctly) are skipped.
|
||||||
|
let ast_ptr = loop {
|
||||||
|
match fn_params_iter.next() {
|
||||||
|
Some(element)
|
||||||
|
if pat_equals_to_name(element.pat(), ¶m_to_rename.current_name) =>
|
||||||
|
{
|
||||||
|
break element.pat().unwrap()
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
log::error!(
|
||||||
|
"Replacement ({:?}) was generated for a function parameter which was not found: {:?}",
|
||||||
|
param_to_rename, fn_src
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let diagnostic = IncorrectCase {
|
||||||
|
file: fn_src.file_id,
|
||||||
|
ident_type: "Argument".to_string(),
|
||||||
|
ident: AstPtr::new(&ast_ptr).into(),
|
||||||
|
expected_case: param_to_rename.expected_case,
|
||||||
|
ident_text: param_to_rename.current_name.to_string(),
|
||||||
|
suggested_text: param_to_rename.suggested_text,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.sink.push(diagnostic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_adt(&mut self, db: &dyn HirDatabase, adt: AdtId) {}
|
fn validate_adt(&mut self, db: &dyn HirDatabase, adt: AdtId) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pat_equals_to_name(pat: Option<ast::Pat>, name: &Name) -> bool {
|
||||||
|
if let Some(ast::Pat::IdentPat(ident)) = pat {
|
||||||
|
ident.to_string() == name.to_string()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_lower_snake_case(ident: &str) -> Option<String> {
|
fn to_lower_snake_case(ident: &str) -> Option<String> {
|
||||||
|
// First, assume that it's UPPER_SNAKE_CASE.
|
||||||
|
if let Some(normalized) = to_lower_snake_case_from_upper_snake_case(ident) {
|
||||||
|
return Some(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, assume that it's CamelCase.
|
||||||
let lower_snake_case = stdx::to_lower_snake_case(ident);
|
let lower_snake_case = stdx::to_lower_snake_case(ident);
|
||||||
|
|
||||||
if lower_snake_case == ident {
|
if lower_snake_case == ident {
|
||||||
|
@ -157,6 +218,17 @@ fn to_lower_snake_case(ident: &str) -> Option<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_lower_snake_case_from_upper_snake_case(ident: &str) -> Option<String> {
|
||||||
|
let is_upper_snake_case = ident.chars().all(|c| c.is_ascii_uppercase() || c == '_');
|
||||||
|
|
||||||
|
if is_upper_snake_case {
|
||||||
|
let string = ident.chars().map(|c| c.to_ascii_lowercase()).collect();
|
||||||
|
Some(string)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::diagnostics::tests::check_diagnostics;
|
use crate::diagnostics::tests::check_diagnostics;
|
||||||
|
@ -166,7 +238,20 @@ mod tests {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
fn NonSnakeCaseName() {}
|
fn NonSnakeCaseName() {}
|
||||||
// ^^^^^^^^^^^^^^^^ Argument `NonSnakeCaseName` should have a snake_case name, e.g. `non_snake_case_name`
|
// ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have a snake_case name, e.g. `non_snake_case_name`
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incorrect_function_params() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn foo(SomeParam: u8) {}
|
||||||
|
// ^^^^^^^^^ Argument `SomeParam` should have a snake_case name, e.g. `some_param`
|
||||||
|
|
||||||
|
fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
|
||||||
|
// ^^^^^^^^^^ Argument `CAPS_PARAM` should have a snake_case name, e.g. `caps_param`
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue