Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/biome_css_analyze/benches/css_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use biome_analyze::{
RuleCategoriesBuilder,
};
use biome_css_parser::CssParserOptions;
use biome_css_syntax::CssFileSource;
use biome_test_utils::BenchCase;
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use std::collections::HashMap;
Expand Down Expand Up @@ -56,11 +57,16 @@ fn bench_analyzer(criterion: &mut Criterion) {
AnalyzerConfiguration::default()
.with_jsx_runtime(JsxRuntime::default()),
);
let semantic_model = biome_css_semantic::semantic_model(&parse.tree());
let services = biome_css_analyze::CssAnalyzerServices::default()
.with_file_source(CssFileSource::default())
.with_semantic_model(&semantic_model);
b.iter(|| {
biome_css_analyze::analyze(
&parse.tree(),
filter,
&options,
services.clone(),
&[],
|event| {
black_box(event.diagnostic());
Expand Down
179 changes: 132 additions & 47 deletions crates/biome_css_analyze/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use biome_analyze::{
ControlFlow, LanguageRoot, MatchQueryParams, MetadataRegistry, Phases, PluginTargetLanguage,
PluginVisitor, RuleAction, RuleRegistry, to_analyzer_suppressions,
};
use biome_css_syntax::{CssLanguage, TextRange};
use biome_css_syntax::{CssFileSource, CssLanguage, TextRange};
use biome_diagnostics::Error;
use biome_suppression::{SuppressionDiagnostic, parse_suppression_comment};
use std::ops::Deref;
Expand All @@ -31,21 +31,51 @@ pub static METADATA: LazyLock<MetadataRegistry> = LazyLock::new(|| {
metadata
});

#[derive(Debug, Clone, Default)]
pub struct CssAnalyzerServices<'a> {
pub semantic_model: Option<&'a biome_css_semantic::model::SemanticModel>,
pub file_source: CssFileSource,
}

impl<'a> CssAnalyzerServices<'a> {
pub fn with_file_source(mut self, file_source: CssFileSource) -> Self {
self.file_source = file_source;
self
}

pub fn with_semantic_model(
mut self,
semantic_model: &'a biome_css_semantic::model::SemanticModel,
) -> Self {
self.semantic_model = Some(semantic_model);
self
}
}

/// Run the analyzer on the provided `root`: this process will use the given `filter`
/// to selectively restrict analysis to specific rules / a specific source range,
/// then call `emit_signal` when an analysis rule emits a diagnostic or action
pub fn analyze<'a, F, B>(
root: &LanguageRoot<CssLanguage>,
filter: AnalysisFilter,
options: &'a AnalyzerOptions,
services: CssAnalyzerServices,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may want to pass the services by reference instead of by value, so we can avoid a lot of cloning. Are there reasons why the analyser needs to mutate services, maybe? If so, we may want to resolve that in a separate PR, but I feel it would be a good idea to do before scaling up to other services.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, there's no reason to pass the service by value at the moment. We can pass references

plugins: AnalyzerPluginSlice<'a>,
emit_signal: F,
) -> (Option<B>, Vec<Error>)
where
F: FnMut(&dyn AnalyzerSignal<CssLanguage>) -> ControlFlow<B> + 'a,
B: 'a,
{
analyze_with_inspect_matcher(root, filter, |_| {}, options, plugins, emit_signal)
analyze_with_inspect_matcher(
root,
filter,
|_| {},
options,
services,
plugins,
emit_signal,
)
}

/// Run the analyzer on the provided `root`: this process will use the given `filter`
Expand All @@ -59,6 +89,7 @@ pub fn analyze_with_inspect_matcher<'a, V, F, B>(
filter: AnalysisFilter,
inspect_matcher: V,
options: &'a AnalyzerOptions,
css_services: CssAnalyzerServices,
plugins: AnalyzerPluginSlice<'a>,
mut emit_signal: F,
) -> (Option<B>, Vec<Error>)
Expand Down Expand Up @@ -96,7 +127,7 @@ where
let mut registry = RuleRegistry::builder(&filter, root);
visit_registry(&mut registry);

let (registry, services, diagnostics, visitors) = registry.build();
let (registry, mut services, diagnostics, visitors) = registry.build();

// Bail if we can't parse a rule option
if !diagnostics.is_empty() {
Expand All @@ -111,6 +142,11 @@ where
&mut emit_signal,
);

services.insert_service(css_services.file_source);
if let Some(semantic_model) = css_services.semantic_model {
services.insert_service(semantic_model.clone());
}

for ((phase, _), visitor) in visitors {
analyzer.add_visitor(phase, visitor);
}
Expand Down Expand Up @@ -140,19 +176,19 @@ where

#[cfg(test)]
mod tests {
use crate::{AnalysisFilter, ControlFlow, CssAnalyzerServices, analyze};
use biome_analyze::{AnalyzerOptions, Never, RuleCategoriesBuilder, RuleFilter};
use biome_console::fmt::{Formatter, Termcolor};
use biome_console::{Markup, markup};
use biome_css_parser::{CssParserOptions, parse_css};
use biome_css_syntax::TextRange;
use biome_css_semantic::semantic_model;
use biome_css_syntax::{CssFileSource, TextRange};
use biome_diagnostics::termcolor::NoColor;
use biome_diagnostics::{
Diagnostic, DiagnosticExt, PrintDiagnostic, Severity, category, print_diagnostic_to_string,
};
use std::slice;

use crate::{AnalysisFilter, ControlFlow, analyze};

#[ignore]
#[test]
fn quick_test() {
Expand Down Expand Up @@ -190,13 +226,18 @@ mod tests {
let mut error_ranges: Vec<TextRange> = Vec::new();
let rule_filter = RuleFilter::Rule("nursery", "noUnknownPseudoClass");
let options = AnalyzerOptions::default();
let css_services = CssAnalyzerServices {
semantic_model: Some(&semantic_model(&parsed.tree())),
file_source: CssFileSource::css(),
};
analyze(
&parsed.tree(),
AnalysisFilter {
enabled_rules: Some(slice::from_ref(&rule_filter)),
..AnalysisFilter::default()
},
&options,
css_services,
&[],
|signal| {
if let Some(diag) = signal.diagnostic() {
Expand Down Expand Up @@ -242,18 +283,29 @@ mod tests {
};

let options = AnalyzerOptions::default();
analyze(&parsed.tree(), filter, &options, &[], |signal| {
if let Some(diag) = signal.diagnostic() {
let error = diag
.with_file_path("dummyFile")
.with_file_source_code(SOURCE);
let text = print_diagnostic_to_string(&error);
eprintln!("{text}");
panic!("Unexpected diagnostic");
}
let css_services = CssAnalyzerServices {
semantic_model: None,
file_source: CssFileSource::css(),
};
analyze(
&parsed.tree(),
filter,
&options,
css_services,
&[],
|signal| {
if let Some(diag) = signal.diagnostic() {
let error = diag
.with_file_path("dummyFile")
.with_file_source_code(SOURCE);
let text = print_diagnostic_to_string(&error);
eprintln!("{text}");
panic!("Unexpected diagnostic");
}

ControlFlow::<Never>::Continue(())
});
ControlFlow::<Never>::Continue(())
},
);
}

#[test]
Expand Down Expand Up @@ -282,18 +334,29 @@ a {
};

let options = AnalyzerOptions::default();
analyze(&parsed.tree(), filter, &options, &[], |signal| {
if let Some(diag) = signal.diagnostic() {
let error = diag
.with_file_path("dummyFile")
.with_file_source_code(SOURCE);
let text = print_diagnostic_to_string(&error);
eprintln!("{text}");
panic!("Unexpected diagnostic");
}
let css_services = CssAnalyzerServices {
semantic_model: Some(&semantic_model(&parsed.tree())),
file_source: CssFileSource::css(),
};
analyze(
&parsed.tree(),
filter,
&options,
css_services,
&[],
|signal| {
if let Some(diag) = signal.diagnostic() {
let error = diag
.with_file_path("dummyFile")
.with_file_source_code(SOURCE);
let text = print_diagnostic_to_string(&error);
eprintln!("{text}");
panic!("Unexpected diagnostic");
}

ControlFlow::<Never>::Continue(())
});
ControlFlow::<Never>::Continue(())
},
);
}

#[test]
Expand All @@ -318,18 +381,29 @@ a {
};

let options = AnalyzerOptions::default();
analyze(&parsed.tree(), filter, &options, &[], |signal| {
if let Some(diag) = signal.diagnostic() {
let error = diag
.with_file_path("dummyFile")
.with_file_source_code(SOURCE);
let text = print_diagnostic_to_string(&error);
eprintln!("{text}");
panic!("Unexpected diagnostic");
}
let css_services = CssAnalyzerServices {
semantic_model: Some(&semantic_model(&parsed.tree())),
file_source: CssFileSource::css(),
};
analyze(
&parsed.tree(),
filter,
&options,
css_services,
&[],
|signal| {
if let Some(diag) = signal.diagnostic() {
let error = diag
.with_file_path("dummyFile")
.with_file_source_code(SOURCE);
let text = print_diagnostic_to_string(&error);
eprintln!("{text}");
panic!("Unexpected diagnostic");
}

ControlFlow::<Never>::Continue(())
});
ControlFlow::<Never>::Continue(())
},
);
}

#[test]
Expand All @@ -351,15 +425,26 @@ a {
};

let options = AnalyzerOptions::default();
analyze(&parsed.tree(), filter, &options, &[], |signal| {
if let Some(diag) = signal.diagnostic() {
let code = diag.category().unwrap();
if code != category!("suppressions/unused") {
panic!("unexpected diagnostic {code:?}");
let css_services = CssAnalyzerServices {
semantic_model: Some(&semantic_model(&parsed.tree())),
file_source: CssFileSource::css(),
};
analyze(
&parsed.tree(),
filter,
&options,
css_services,
&[],
|signal| {
if let Some(diag) = signal.diagnostic() {
let code = diag.category().unwrap();
if code != category!("suppressions/unused") {
panic!("unexpected diagnostic {code:?}");
}
}
}

ControlFlow::<Never>::Continue(())
});
ControlFlow::<Never>::Continue(())
},
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ impl Rule for NoMissingVarFunction {

fn run(ctx: &RuleContext<Self>) -> Option<Self::State> {
let node = ctx.query();
let root = ctx.root();
if is_wrapped_in_var(node) {
return None;
}
Expand All @@ -170,7 +171,7 @@ impl Rule for NoMissingVarFunction {
if rule
.declarations()
.iter()
.flat_map(|decl| decl.property().value())
.flat_map(|decl| decl.property(&root).value())
.any(|value| value.text() == custom_variable_name.text())
{
return Some(node.clone());
Expand All @@ -182,7 +183,7 @@ impl Rule for NoMissingVarFunction {
if parent_rule
.declarations()
.iter()
.flat_map(|decl| decl.property().value())
.flat_map(|decl| decl.property(&root).value())
.any(|value| value.text() == custom_variable_name.text())
{
return Some(node.clone());
Expand Down
Loading
Loading