Skip to content

Commit

Permalink
Add pub mod option for UnlinkedFile
Browse files Browse the repository at this point in the history
  • Loading branch information
yue4u committed May 17, 2021
1 parent c04eaa1 commit 6112376
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 94 deletions.
63 changes: 42 additions & 21 deletions crates/ide/src/diagnostics.rs
Expand Up @@ -28,37 +28,37 @@ use unlinked_file::UnlinkedFile;

use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};

use self::fixes::DiagnosticWithFix;
use self::fixes::DiagnosticWithFixes;

#[derive(Debug)]
pub struct Diagnostic {
// pub name: Option<String>,
pub message: String,
pub range: TextRange,
pub severity: Severity,
pub fix: Option<Assist>,
pub fixes: Option<Vec<Assist>>,
pub unused: bool,
pub code: Option<DiagnosticCode>,
}

impl Diagnostic {
fn error(range: TextRange, message: String) -> Self {
Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None }
Self { message, range, severity: Severity::Error, fixes: None, unused: false, code: None }
}

fn hint(range: TextRange, message: String) -> Self {
Self {
message,
range,
severity: Severity::WeakWarning,
fix: None,
fixes: None,
unused: false,
code: None,
}
}

fn with_fix(self, fix: Option<Assist>) -> Self {
Self { fix, ..self }
fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self {
Self { fixes, ..self }
}

fn with_unused(self, unused: bool) -> Self {
Expand Down Expand Up @@ -154,7 +154,7 @@ pub(crate) fn diagnostics(
// Override severity and mark as unused.
res.borrow_mut().push(
Diagnostic::hint(range, d.message())
.with_fix(d.fix(&sema, resolve))
.with_fixes(d.fixes(&sema, resolve))
.with_code(Some(d.code())),
);
})
Expand Down Expand Up @@ -210,23 +210,23 @@ pub(crate) fn diagnostics(
res.into_inner()
}

fn diagnostic_with_fix<D: DiagnosticWithFix>(
fn diagnostic_with_fix<D: DiagnosticWithFixes>(
d: &D,
sema: &Semantics<RootDatabase>,
resolve: &AssistResolveStrategy,
) -> Diagnostic {
Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
.with_fix(d.fix(&sema, resolve))
.with_fixes(d.fixes(&sema, resolve))
.with_code(Some(d.code()))
}

fn warning_with_fix<D: DiagnosticWithFix>(
fn warning_with_fix<D: DiagnosticWithFixes>(
d: &D,
sema: &Semantics<RootDatabase>,
resolve: &AssistResolveStrategy,
) -> Diagnostic {
Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
.with_fix(d.fix(&sema, resolve))
.with_fixes(d.fixes(&sema, resolve))
.with_code(Some(d.code()))
}

Expand Down Expand Up @@ -256,12 +256,12 @@ fn check_unnecessary_braces_in_use_statement(

acc.push(
Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
.with_fix(Some(fix(
.with_fixes(Some(vec![fix(
"remove_braces",
"Remove unnecessary braces",
SourceChange::from_text_edit(file_id, edit),
use_range,
))),
)])),
);
}

Expand Down Expand Up @@ -309,9 +309,23 @@ mod tests {
/// Takes a multi-file input fixture with annotated cursor positions,
/// and checks that:
/// * a diagnostic is produced
/// * this diagnostic fix trigger range touches the input cursor position
/// * the first diagnostic fix trigger range touches the input cursor position
/// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
check_nth_fix(0, ra_fixture_before, ra_fixture_after);
}
/// Takes a multi-file input fixture with annotated cursor positions,
/// and checks that:
/// * a diagnostic is produced
/// * every diagnostic fixes trigger range touches the input cursor position
/// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
check_nth_fix(i, ra_fixture_before, ra_fixture_after)
}
}

fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
let after = trim_indent(ra_fixture_after);

let (analysis, file_position) = fixture::position(ra_fixture_before);
Expand All @@ -324,9 +338,9 @@ mod tests {
.unwrap()
.pop()
.unwrap();
let fix = diagnostic.fix.unwrap();
let fix = &diagnostic.fixes.unwrap()[nth];
let actual = {
let source_change = fix.source_change.unwrap();
let source_change = fix.source_change.as_ref().unwrap();
let file_id = *source_change.source_file_edits.keys().next().unwrap();
let mut actual = analysis.file_text(file_id).unwrap().to_string();

Expand All @@ -344,7 +358,6 @@ mod tests {
file_position.offset
);
}

/// Checks that there's a diagnostic *without* fix at `$0`.
fn check_no_fix(ra_fixture: &str) {
let (analysis, file_position) = fixture::position(ra_fixture);
Expand All @@ -357,7 +370,7 @@ mod tests {
.unwrap()
.pop()
.unwrap();
assert!(diagnostic.fix.is_none(), "got a fix when none was expected: {:?}", diagnostic);
assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
}

/// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
Expand Down Expand Up @@ -393,7 +406,7 @@ mod tests {
message: "unresolved macro `foo::bar!`",
range: 5..8,
severity: Error,
fix: None,
fixes: None,
unused: false,
code: Some(
DiagnosticCode(
Expand Down Expand Up @@ -542,18 +555,26 @@ mod a {
#[test]
fn unlinked_file_prepend_first_item() {
cov_mark::check!(unlinked_file_prepend_before_first_item);
check_fix(
// Only tests the first one for `pub mod` since the rest are the same
check_fixes(
r#"
//- /main.rs
fn f() {}
//- /foo.rs
$0
"#,
r#"
vec![
r#"
mod foo;
fn f() {}
"#,
r#"
pub mod foo;
fn f() {}
"#,
],
);
}

Expand Down
13 changes: 6 additions & 7 deletions crates/ide/src/diagnostics/field_shorthand.rs
Expand Up @@ -46,14 +46,13 @@ fn check_expr_field_shorthand(

let field_range = record_field.syntax().text_range();
acc.push(
Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
Some(fix(
Diagnostic::hint(field_range, "Shorthand struct initialization".to_string())
.with_fixes(Some(vec![fix(
"use_expr_field_shorthand",
"Use struct shorthand initialization",
SourceChange::from_text_edit(file_id, edit),
field_range,
)),
),
)])),
);
}
}
Expand Down Expand Up @@ -86,13 +85,13 @@ fn check_pat_field_shorthand(
let edit = edit_builder.finish();

let field_range = record_pat_field.syntax().text_range();
acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
Some(fix(
acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fixes(
Some(vec![fix(
"use_pat_field_shorthand",
"Use struct field shorthand",
SourceChange::from_text_edit(file_id, edit),
field_range,
)),
)]),
));
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/ide/src/diagnostics/fixes.rs
Expand Up @@ -17,15 +17,15 @@ use crate::Assist;
/// A [Diagnostic] that potentially has a fix available.
///
/// [Diagnostic]: hir::diagnostics::Diagnostic
pub(crate) trait DiagnosticWithFix: Diagnostic {
pub(crate) trait DiagnosticWithFixes: Diagnostic {
/// `resolve` determines if the diagnostic should fill in the `edit` field
/// of the assist.
///
/// If `resolve` is false, the edit will be computed later, on demand, and
/// can be omitted.
fn fix(
fn fixes(
&self,
sema: &Semantics<RootDatabase>,
_resolve: &AssistResolveStrategy,
) -> Option<Assist>;
) -> Option<Vec<Assist>>;
}
10 changes: 5 additions & 5 deletions crates/ide/src/diagnostics/fixes/change_case.rs
Expand Up @@ -4,16 +4,16 @@ use ide_db::{base_db::FilePosition, RootDatabase};
use syntax::AstNode;

use crate::{
diagnostics::{unresolved_fix, DiagnosticWithFix},
diagnostics::{unresolved_fix, DiagnosticWithFixes},
references::rename::rename_with_semantics,
};

impl DiagnosticWithFix for IncorrectCase {
fn fix(
impl DiagnosticWithFixes for IncorrectCase {
fn fixes(
&self,
sema: &Semantics<RootDatabase>,
resolve: &AssistResolveStrategy,
) -> Option<Assist> {
) -> Option<Vec<Assist>> {
let root = sema.db.parse_or_expand(self.file)?;
let name_node = self.ident.to_node(&root);

Expand All @@ -28,7 +28,7 @@ impl DiagnosticWithFix for IncorrectCase {
res.source_change = Some(source_change.ok().unwrap_or_default());
}

Some(res)
Some(vec![res])
}
}

Expand Down
19 changes: 9 additions & 10 deletions crates/ide/src/diagnostics/fixes/create_field.rs
Expand Up @@ -7,30 +7,29 @@ use syntax::{
use text_edit::TextEdit;

use crate::{
diagnostics::{fix, DiagnosticWithFix},
diagnostics::{fix, DiagnosticWithFixes},
Assist, AssistResolveStrategy,
};

impl DiagnosticWithFix for NoSuchField {
fn fix(
impl DiagnosticWithFixes for NoSuchField {
fn fixes(
&self,
sema: &Semantics<RootDatabase>,
_resolve: &AssistResolveStrategy,
) -> Option<Assist> {
) -> Option<Vec<Assist>> {
let root = sema.db.parse_or_expand(self.file)?;
missing_record_expr_field_fix(
missing_record_expr_field_fixes(
&sema,
self.file.original_file(sema.db),
&self.field.to_node(&root),
)
}
}

fn missing_record_expr_field_fix(
fn missing_record_expr_field_fixes(
sema: &Semantics<RootDatabase>,
usage_file_id: FileId,
record_expr_field: &ast::RecordExprField,
) -> Option<Assist> {
) -> Option<Vec<Assist>> {
let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
let def_id = sema.resolve_variant(record_lit)?;
let module;
Expand Down Expand Up @@ -89,12 +88,12 @@ fn missing_record_expr_field_fix(
TextEdit::insert(last_field_syntax.text_range().end(), new_field),
);

return Some(fix(
return Some(vec![fix(
"create_field",
"Create field",
source_change,
record_expr_field.syntax().text_range(),
));
)]);

fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
match field_def_list {
Expand Down
12 changes: 6 additions & 6 deletions crates/ide/src/diagnostics/fixes/fill_missing_fields.rs
Expand Up @@ -5,16 +5,16 @@ use syntax::{algo, ast::make, AstNode};
use text_edit::TextEdit;

use crate::{
diagnostics::{fix, fixes::DiagnosticWithFix},
diagnostics::{fix, fixes::DiagnosticWithFixes},
Assist,
};

impl DiagnosticWithFix for MissingFields {
fn fix(
impl DiagnosticWithFixes for MissingFields {
fn fixes(
&self,
sema: &Semantics<RootDatabase>,
_resolve: &AssistResolveStrategy,
) -> Option<Assist> {
) -> Option<Vec<Assist>> {
// Note that although we could add a diagnostics to
// fill the missing tuple field, e.g :
// `struct A(usize);`
Expand All @@ -41,12 +41,12 @@ impl DiagnosticWithFix for MissingFields {
.into_text_edit(&mut builder);
builder.finish()
};
Some(fix(
Some(vec![fix(
"fill_missing_fields",
"Fill struct fields",
SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
sema.original_range(&field_list_parent.syntax()).range,
))
)])
}
}

Expand Down
10 changes: 5 additions & 5 deletions crates/ide/src/diagnostics/fixes/remove_semicolon.rs
Expand Up @@ -4,14 +4,14 @@ use ide_db::{source_change::SourceChange, RootDatabase};
use syntax::{ast, AstNode};
use text_edit::TextEdit;

use crate::diagnostics::{fix, DiagnosticWithFix};
use crate::diagnostics::{fix, DiagnosticWithFixes};

impl DiagnosticWithFix for RemoveThisSemicolon {
fn fix(
impl DiagnosticWithFixes for RemoveThisSemicolon {
fn fixes(
&self,
sema: &Semantics<RootDatabase>,
_resolve: &AssistResolveStrategy,
) -> Option<Assist> {
) -> Option<Vec<Assist>> {
let root = sema.db.parse_or_expand(self.file)?;

let semicolon = self
Expand All @@ -26,7 +26,7 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
let edit = TextEdit::delete(semicolon);
let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);

Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon))
Some(vec![fix("remove_semicolon", "Remove this semicolon", source_change, semicolon)])
}
}

Expand Down

0 comments on commit 6112376

Please sign in to comment.