Add Diagnostic.try_amend() to simplify error handling (#3701)

This commit is contained in:
Jonathan Plasse 2023-03-24 22:10:11 +01:00 committed by GitHub
parent 1bac206995
commit dc4d7619ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 192 additions and 300 deletions

2
Cargo.lock generated
View file

@ -2131,6 +2131,8 @@ dependencies = [
name = "ruff_diagnostics"
version = "0.0.0"
dependencies = [
"anyhow",
"log",
"ruff_python_ast",
"rustpython-parser",
"serde",

View file

@ -3797,21 +3797,12 @@ where
name_range,
);
if self.patch(Rule::UnusedVariable) {
match pyflakes::fixes::remove_exception_handler_assignment(
excepthandler,
self.locator,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => {
error!(
"Failed to remove exception handler \
assignment: {}",
e
);
}
}
diagnostic.try_amend(|| {
pyflakes::fixes::remove_exception_handler_assignment(
excepthandler,
self.locator,
)
});
}
self.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Violation};
@ -668,12 +667,9 @@ pub fn definition(
helpers::identifier_range(stmt, checker.locator),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::add_return_annotation(checker.locator, stmt, "None") {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::add_return_annotation(checker.locator, stmt, "None")
});
}
diagnostics.push(diagnostic);
}
@ -693,12 +689,9 @@ pub fn definition(
let return_type = SIMPLE_MAGIC_RETURN_TYPES.get(name);
if let Some(return_type) = return_type {
if checker.patch(diagnostic.kind.rule()) {
match fixes::add_return_annotation(checker.locator, stmt, return_type) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::add_return_annotation(checker.locator, stmt, return_type)
});
}
}
diagnostics.push(diagnostic);

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -87,12 +86,9 @@ pub fn unnecessary_call_around_sorted(
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_call_around_sorted(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_call_around_sorted(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -91,12 +90,9 @@ pub fn unnecessary_collection_call(
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_collection_call(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_collection_call(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Comprehension, Expr, ExprKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -68,12 +67,9 @@ fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_comprehension(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_comprehension(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -115,13 +115,13 @@ pub fn unnecessary_double_cast_or_process(
{
let mut diagnostic = create_diagnostic(inner, outer, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
if let Ok(fix) = fixes::fix_unnecessary_double_cast_or_process(
checker.locator,
checker.stylist,
expr,
) {
diagnostic.amend(fix);
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_double_cast_or_process(
checker.locator,
checker.stylist,
expr,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -60,17 +59,14 @@ pub fn unnecessary_generator_dict(
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorDict, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_dict(
checker.locator,
checker.stylist,
expr,
parent,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_generator_dict(
checker.locator,
checker.stylist,
expr,
parent,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -60,12 +59,9 @@ pub fn unnecessary_generator_list(
if let ExprKind::GeneratorExp { .. } = argument {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_list(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_generator_list(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -61,17 +60,9 @@ pub fn unnecessary_generator_set(
if let ExprKind::GeneratorExp { .. } = argument {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorSet, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_set(
checker.locator,
checker.stylist,
expr,
parent,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_generator_set(checker.locator, checker.stylist, expr, parent)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -53,12 +52,8 @@ pub fn unnecessary_list_call(checker: &mut Checker, expr: &Expr, func: &Expr, ar
}
let mut diagnostic = Diagnostic::new(UnnecessaryListCall, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_call(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic
.try_amend(|| fixes::fix_unnecessary_list_call(checker.locator, checker.stylist, expr));
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -66,13 +65,9 @@ pub fn unnecessary_list_comprehension_dict(
}
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionDict, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_comprehension_dict(checker.locator, checker.stylist, expr)
{
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_list_comprehension_dict(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -58,16 +57,13 @@ pub fn unnecessary_list_comprehension_set(
if let ExprKind::ListComp { .. } = &argument {
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionSet, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_comprehension_set(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_list_comprehension_set(
checker.locator,
checker.stylist,
expr,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -81,12 +80,9 @@ pub fn unnecessary_literal_dict(
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_dict(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_literal_dict(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -75,12 +74,9 @@ pub fn unnecessary_literal_set(
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_set(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_literal_set(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -92,16 +91,9 @@ pub fn unnecessary_literal_within_list_call(
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_within_list_call(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_literal_within_list_call(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
@ -93,16 +92,9 @@ pub fn unnecessary_literal_within_tuple_call(
Range::from(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_within_tuple_call(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_literal_within_tuple_call(checker.locator, checker.stylist, expr)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,4 +1,3 @@
use log::error;
use rustpython_parser::ast::{Expr, ExprKind};
use ruff_diagnostics::Diagnostic;
@ -108,18 +107,15 @@ pub fn unnecessary_map(
if args.len() == 2 && matches!(&args[0].node, ExprKind::Lambda { .. }) {
let mut diagnostic = create_diagnostic("generator", Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_map(
checker.locator,
checker.stylist,
expr,
parent,
"generator",
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_map(
checker.locator,
checker.stylist,
expr,
parent,
"generator",
)
});
}
checker.diagnostics.push(diagnostic);
}
@ -140,18 +136,15 @@ pub fn unnecessary_map(
if let ExprKind::Lambda { .. } = argument {
let mut diagnostic = create_diagnostic(id, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_map(
checker.locator,
checker.stylist,
expr,
parent,
id,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_map(
checker.locator,
checker.stylist,
expr,
parent,
id,
)
});
}
checker.diagnostics.push(diagnostic);
}
@ -173,18 +166,15 @@ pub fn unnecessary_map(
{
let mut diagnostic = create_diagnostic(id, Range::from(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_map(
checker.locator,
checker.stylist,
expr,
parent,
id,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_map(
checker.locator,
checker.stylist,
expr,
parent,
id,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -192,21 +192,16 @@ pub fn no_unnecessary_pass(checker: &mut Checker, body: &[Stmt]) {
),
));
} else {
match delete_stmt(
pass_stmt,
None,
&[],
checker.locator,
checker.indexer,
checker.stylist,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => {
error!("Failed to delete `pass` statement: {}", e);
}
}
diagnostic.try_amend(|| {
delete_stmt(
pass_stmt,
None,
&[],
checker.locator,
checker.indexer,
checker.stylist,
)
});
}
}
checker.diagnostics.push(diagnostic);
@ -353,16 +348,13 @@ pub fn unnecessary_comprehension_any_all(
let mut diagnostic =
Diagnostic::new(UnnecessaryComprehensionAnyAll, Range::from(&args[0]));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_comprehension_any_all(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
diagnostic.try_amend(|| {
fixes::fix_unnecessary_comprehension_any_all(
checker.locator,
checker.stylist,
expr,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -435,9 +435,8 @@ pub fn composite_condition(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg:
let mut diagnostic =
Diagnostic::new(PytestCompositeAssertion { fixable }, Range::from(stmt));
if fixable && checker.patch(diagnostic.kind.rule()) {
if let Ok(fix) = fix_composite_condition(stmt, checker.locator, checker.stylist) {
diagnostic.amend(fix);
}
diagnostic
.try_amend(|| fix_composite_condition(stmt, checker.locator, checker.stylist));
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,5 +1,4 @@
use anyhow::Result;
use log::error;
use rustpython_parser::ast::{Arguments, Expr, ExprKind, Keyword, Location, Stmt, StmtKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Violation};
@ -317,19 +316,18 @@ fn check_fixture_decorator(checker: &mut Checker, func_name: &str, decorator: &E
Range::from(scope_keyword),
);
if checker.patch(diagnostic.kind.rule()) {
match fix_extraneous_scope_function(
checker.locator,
decorator.location,
diagnostic.location,
diagnostic.end_location,
args,
keywords,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
let location = diagnostic.location;
let end_location = diagnostic.end_location;
diagnostic.try_amend(|| {
fix_extraneous_scope_function(
checker.locator,
decorator.location,
location,
end_location,
args,
keywords,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -1,6 +1,5 @@
use std::string::ToString;
use log::error;
use rustc_hash::FxHashSet;
use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword, KeywordData};
@ -312,17 +311,14 @@ pub(crate) fn percent_format_extra_named_arguments(
location,
);
if checker.patch(diagnostic.kind.rule()) {
match remove_unused_format_arguments_from_dict(
&missing,
right,
checker.locator,
checker.stylist,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to remove unused format arguments: {e}"),
}
diagnostic.try_amend(|| {
remove_unused_format_arguments_from_dict(
&missing,
right,
checker.locator,
checker.stylist,
)
});
}
checker.diagnostics.push(diagnostic);
}
@ -478,17 +474,14 @@ pub(crate) fn string_dot_format_extra_named_arguments(
location,
);
if checker.patch(diagnostic.kind.rule()) {
match remove_unused_keyword_arguments_from_format_call(
&missing,
location,
checker.locator,
checker.stylist,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to remove unused keyword arguments: {e}"),
}
diagnostic.try_amend(|| {
remove_unused_keyword_arguments_from_format_call(
&missing,
location,
checker.locator,
checker.stylist,
)
});
}
checker.diagnostics.push(diagnostic);
}
@ -522,18 +515,15 @@ pub(crate) fn string_dot_format_extra_positional_arguments(
location,
);
if checker.patch(diagnostic.kind.rule()) {
match remove_unused_positional_arguments_from_format_call(
&missing,
location,
checker.locator,
checker.stylist,
&summary.format_string,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to remove unused positional arguments: {e}"),
}
diagnostic.try_amend(|| {
remove_unused_positional_arguments_from_format_call(
&missing,
location,
checker.locator,
checker.stylist,
&summary.format_string,
)
});
}
checker.diagnostics.push(diagnostic);
}

View file

@ -58,12 +58,8 @@ pub fn remove_class_def_base(
expr_end: Location,
bases: &[Expr],
keywords: &[Keyword],
) -> Option<Fix> {
if let Ok(fix) = remove_argument(locator, stmt_at, expr_at, expr_end, bases, keywords, true) {
Some(fix)
} else {
None
}
) -> Result<Fix> {
remove_argument(locator, stmt_at, expr_at, expr_end, bases, keywords, true)
}
/// Generate a fix to remove arguments from a `super` call.

View file

@ -332,16 +332,17 @@ pub fn deprecated_mock_import(checker: &mut Checker, stmt: &Stmt) {
);
if checker.patch(diagnostic.kind.rule()) {
if let Some(indent) = indentation(checker.locator, stmt) {
match format_import_from(stmt, indent, checker.locator, checker.stylist) {
Ok(content) => {
diagnostic.amend(Fix::replacement(
content,
stmt.location,
stmt.end_location.unwrap(),
));
}
Err(e) => error!("Failed to rewrite `mock` import: {e}"),
}
diagnostic.try_amend(|| {
format_import_from(stmt, indent, checker.locator, checker.stylist).map(
|content| {
Fix::replacement(
content,
stmt.location,
stmt.end_location.unwrap(),
)
},
)
});
}
}
checker.diagnostics.push(diagnostic);

View file

@ -1,7 +1,6 @@
use std::str::FromStr;
use anyhow::{anyhow, Result};
use log::error;
use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword, Location};
use rustpython_parser::{lexer, Mode, Tok};
@ -122,12 +121,7 @@ fn create_check(
mode_param.end_location.unwrap(),
));
} else {
match create_remove_param_fix(locator, expr, mode_param) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to remove parameter: {e}"),
}
diagnostic.try_amend(|| create_remove_param_fix(locator, expr, mode_param));
}
}
diagnostic

View file

@ -63,20 +63,21 @@ pub fn useless_object_inheritance(
bases: &[Expr],
keywords: &[Keyword],
) {
let Some(mut diagnostic) = rule(name, bases, checker.ctx.scope(), &checker.ctx.bindings) else {
return;
};
if checker.patch(diagnostic.kind.rule()) {
if let Some(fix) = fixes::remove_class_def_base(
checker.locator,
stmt.location,
diagnostic.location,
diagnostic.end_location,
bases,
keywords,
) {
diagnostic.amend(fix);
if let Some(mut diagnostic) = rule(name, bases, checker.ctx.scope(), &checker.ctx.bindings) {
if checker.patch(diagnostic.kind.rule()) {
let location = diagnostic.location;
let end_location = diagnostic.end_location;
diagnostic.try_amend(|| {
fixes::remove_class_def_base(
checker.locator,
stmt.location,
location,
end_location,
bases,
keywords,
)
});
}
checker.diagnostics.push(diagnostic);
}
checker.diagnostics.push(diagnostic);
}

View file

@ -8,6 +8,8 @@ rust-version = { workspace = true }
[lib]
[dependencies]
anyhow = { workspace = true }
log = { workspace = true }
ruff_python_ast = { path = "../ruff_python_ast" }
rustpython-parser = { workspace = true }

View file

@ -1,3 +1,5 @@
use anyhow::Result;
use log::error;
use rustpython_parser::ast::Location;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -45,6 +47,14 @@ impl Diagnostic {
self
}
pub fn try_amend(&mut self, func: impl FnOnce() -> Result<Fix>) -> &mut Self {
match func() {
Ok(fix) => self.fix = Some(fix),
Err(err) => error!("Failed to create fix: {}", err),
}
self
}
pub fn parent(&mut self, parent: Location) -> &mut Self {
self.parent = Some(parent);
self