1use crate::search::{
5 Embedder, SemanticParams, SemanticScope, SemanticUnit, Thresholds, UnitSource,
6 find_semantic_clones,
7};
8use crate::units::{extract_units, supports_units};
9use cpd_core::models::CpdClone;
10use cpd_finder::pass::{ClonePass, PassContext, PassSource};
11use std::sync::{Arc, Mutex, MutexGuard};
12
13#[derive(Default)]
17pub struct UnitReader {
18 sources: Mutex<Vec<UnitSource>>,
19}
20
21impl UnitReader {
22 pub fn take_sources(&self) -> Vec<UnitSource> {
25 let mut sources = std::mem::take(&mut *self.lock());
26 sources.sort_by(|a, b| a.id.cmp(&b.id));
27 sources
28 }
29
30 fn lock(&self) -> MutexGuard<'_, Vec<UnitSource>> {
31 self.sources
33 .lock()
34 .unwrap_or_else(|poisoned| poisoned.into_inner())
35 }
36}
37
38impl ClonePass for UnitReader {
39 fn name(&self) -> &'static str {
42 "--compare"
43 }
44
45 fn reads(&self, format: &str) -> bool {
46 supports_units(format)
47 }
48
49 fn read(&self, format: &str, content: &str, sources: &[PassSource<'_>]) {
50 let maps = extract_units(content, format);
51 let found: Vec<UnitSource> = sources
52 .iter()
53 .filter_map(|source| {
54 let map = maps.iter().find(|m| m.format == source.format)?;
57 let units: Vec<SemanticUnit> = map
58 .units
59 .iter()
60 .filter_map(|u| {
61 let unit = SemanticUnit::build(
62 u.grammar,
63 u.name.clone(),
64 u.start.clone(),
65 u.end.clone(),
66 u.text.clone(),
67 source.spans,
68 )?;
69 Some(SemanticUnit {
70 test: u.test,
71 ..unit
72 })
73 })
74 .collect();
75 (!units.is_empty()).then(|| UnitSource {
76 id: source.id.to_string(),
77 format: source.format.to_string(),
78 units,
79 path_label: Default::default(),
80 })
81 })
82 .collect();
83 if !found.is_empty() {
84 self.lock().extend(found);
85 }
86 }
87
88 fn find(&self, _: &PassContext<'_>) -> Result<Vec<CpdClone>, String> {
90 Ok(Vec::new())
91 }
92}
93
94pub struct SemanticPass {
98 embedder: Arc<dyn Embedder>,
99 thresholds: Thresholds,
100 scope: SemanticScope,
101 reader: UnitReader,
102}
103
104impl SemanticPass {
105 pub fn new(embedder: Arc<dyn Embedder>, thresholds: Thresholds, scope: SemanticScope) -> Self {
109 Self {
110 embedder,
111 thresholds,
112 scope,
113 reader: UnitReader::default(),
114 }
115 }
116}
117
118impl ClonePass for SemanticPass {
119 fn name(&self) -> &'static str {
120 "--semantic"
121 }
122
123 fn reads(&self, format: &str) -> bool {
124 self.reader.reads(format)
125 }
126
127 fn read(&self, format: &str, content: &str, sources: &[PassSource<'_>]) {
128 self.reader.read(format, content, sources);
129 }
130
131 fn find(&self, context: &PassContext<'_>) -> Result<Vec<CpdClone>, String> {
132 let mut sources = self.reader.take_sources();
133 for source in &mut sources {
134 source.path_label = (context.label)(&source.id);
135 }
136 let params = SemanticParams {
137 thresholds: self.thresholds,
138 min_tokens: context.min_tokens,
139 min_lines: context.min_lines,
140 scope: self.scope,
141 };
142 find_semantic_clones(&sources, self.embedder.as_ref(), ¶ms, context.existing)
143 }
144}