1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
use std::ops::Range;
use crate::core::rules::base::{EditType, LintFix};
/// A stored reference to a fix in the non-templated file.
#[derive(Hash, Debug, Clone, PartialEq)]
pub struct SourceFix {
pub(crate) edit: String,
pub(crate) source_slice: Range<usize>,
// TODO: It might be possible to refactor this to not require
// a templated_slice (because in theory it's unnecessary).
// However much of the fix handling code assumes we need
// a position in the templated file to interpret it.
// More work required to achieve that if desired.
pub(crate) templated_slice: Range<usize>,
}
impl SourceFix {
pub fn new(edit: String, source_slice: Range<usize>, templated_slice: Range<usize>) -> Self {
SourceFix { edit, source_slice, templated_slice }
}
}
/// An edit patch for a source file.
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct FixPatch {
templated_slice: Range<usize>,
pub fixed_raw: String,
// The patch category, functions mostly for debugging and explanation
// than for function. It allows traceability of *why* this patch was
// generated. It has no significance for processing.
patch_category: String,
pub source_slice: Range<usize>,
templated_str: String,
source_str: String,
}
impl FixPatch {
pub fn new(
templated_slice: Range<usize>,
fixed_raw: String,
patch_category: String,
source_slice: Range<usize>,
templated_str: String,
source_str: String,
) -> Self {
FixPatch {
templated_slice,
fixed_raw,
patch_category,
source_slice,
templated_str,
source_str,
}
}
/// Generate a tuple of this fix for deduping.
pub fn dedupe_tuple(&self) -> (Range<usize>, String) {
(self.source_slice.clone(), self.fixed_raw.clone())
}
}
/// For a given fix anchor, count of the fix edit types and fixes for it."""
#[derive(Debug, Clone)]
#[derive(Default)]
pub struct AnchorEditInfo {
pub delete: usize,
pub replace: usize,
pub create_before: usize,
pub create_after: usize,
pub fixes: Vec<LintFix>,
pub source_fixes: Vec<SourceFix>,
// First fix of edit_type "replace" in "fixes"
pub first_replace: Option<LintFix>,
}
impl AnchorEditInfo {
/// Returns total count of fixes.
#[allow(dead_code)]
fn total(&self) -> usize {
self.delete + self.replace + self.create_before + self.create_after
}
/// Returns True if valid combination of fixes for anchor.
///
/// Cases:
/// * 0-1 fixes of any type: Valid
/// * 2 fixes: Valid if and only if types are create_before and create_after
#[allow(dead_code)]
fn is_valid(&self) -> bool {
let total = self.total();
if total <= 1 {
// Definitely valid (i.e. no conflict) if 0 or 1. In practice, this
// function probably won't be called if there are 0 fixes, but 0 is
// valid; it simply means "no fixes to apply".
true
} else if total == 2 {
// This is only OK for this special case. We allow this because
// the intent is clear (i.e. no conflict): Insert something *before*
// the segment and something else *after* the segment.
self.create_before == 1 && self.create_after == 1
} else {
// Definitely bad if > 2.
false
}
}
/// Adds the fix and updates stats.
///
/// We also allow potentially multiple source fixes on the same anchor by
/// condensing them together here.
pub fn add(&mut self, fix: LintFix) {
if self.fixes.contains(&fix) {
// Deduplicate fixes in case it's already in there.
return;
};
if fix.is_just_source_edit() {
let edit = fix.edit.as_ref().unwrap();
self.source_fixes.extend(edit[0].get_source_fixes());
if let Some(_first_replace) = &self.first_replace {
unimplemented!();
}
}
self.fixes.push(fix.clone());
if fix.edit_type == EditType::Replace && self.first_replace.is_none() {
self.first_replace = Some(fix.clone());
}
match fix.edit_type {
EditType::CreateBefore => self.create_before += 1,
EditType::CreateAfter => self.create_after += 1,
EditType::Replace => self.replace += 1,
EditType::Delete => self.delete += 1,
};
}
}