use perl_ast::ast::Node;
use std::ops::Range;
mod args;
mod conditional;
mod features;
mod map;
mod range_builder;
mod version;
pub use map::{
CompileTimePragmaEnvironment, PragmaEntry, PragmaMap, PragmaQueryCursor, PragmaStateQuery,
};
pub use version::{
PerlVersion, features_enabled_by_version, parse_perl_version, version_implies_strict,
version_implies_warnings,
};
pub(crate) use args::{
add_disabled_warning_category, apply_builtin_imports, builtin_import_names,
normalized_pragma_token, pragma_arg_items, remove_builtin_imports,
};
pub(crate) use conditional::conditional_pragma_target;
pub(crate) use features::{apply_feature_state, canonical_feature_query};
pub(crate) use map::normalize_state;
pub(crate) use version::enable_effective_version_semantics;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct PragmaState {
pub strict_vars: bool,
pub strict_subs: bool,
pub strict_refs: bool,
pub warnings: bool,
pub utf8: bool,
pub encoding: Option<String>,
pub unicode_strings: bool,
pub locale: bool,
pub locale_scope: Option<String>,
pub disabled_warning_categories: Vec<String>,
pub signatures_strict: bool,
pub features: Vec<&'static str>,
pub builtin_imports: Vec<String>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct PragmaSnapshot {
state: PragmaState,
}
impl PragmaSnapshot {
#[must_use]
pub fn from_state(state: PragmaState) -> Self {
Self { state }
}
#[must_use]
pub fn state(&self) -> &PragmaState {
&self.state
}
#[must_use]
pub fn strict_enabled(&self) -> bool {
self.state.strict_vars && self.state.strict_subs && self.state.strict_refs
}
#[must_use]
pub fn warnings_enabled(&self) -> bool {
self.state.warnings
}
#[must_use]
pub fn has_feature(&self, feature: &str) -> bool {
self.state.has_feature(feature)
}
#[must_use]
pub fn is_warning_active(&self, category: &str) -> bool {
self.state.is_warning_active(category)
}
}
impl From<PragmaState> for PragmaSnapshot {
fn from(state: PragmaState) -> Self {
Self::from_state(state)
}
}
impl From<PragmaSnapshot> for PragmaState {
fn from(snapshot: PragmaSnapshot) -> Self {
snapshot.state
}
}
impl PragmaState {
pub fn all_strict() -> Self {
Self {
strict_vars: true,
strict_subs: true,
strict_refs: true,
warnings: false,
utf8: false,
encoding: None,
unicode_strings: false,
locale: false,
locale_scope: None,
disabled_warning_categories: Vec::new(),
signatures_strict: false,
features: Vec::new(),
builtin_imports: Vec::new(),
}
}
#[must_use]
pub fn is_warning_active(&self, category: &str) -> bool {
self.warnings && !self.disabled_warning_categories.iter().any(|c| c == category)
}
#[must_use]
pub fn has_feature(&self, feature: &str) -> bool {
let feature = canonical_feature_query(feature);
self.features.contains(&feature)
}
#[must_use]
pub fn has_builtin_import(&self, name: &str) -> bool {
self.builtin_imports.iter().any(|import| import == name)
}
}
pub struct PragmaTracker;
impl PragmaTracker {
pub fn build(ast: &Node) -> Vec<(Range<usize>, PragmaState)> {
CompileTimePragmaEnvironment::build(ast)
.as_map()
.iter()
.map(|(range, snapshot)| (range.clone(), snapshot.clone().into()))
.collect()
}
pub fn state_for_offset(
pragma_map: &[(Range<usize>, PragmaState)],
offset: usize,
) -> PragmaState {
let idx = pragma_map.partition_point(|(range, _)| range.start <= offset);
let state = if idx > 0 { pragma_map[idx - 1].1.clone() } else { PragmaState::default() };
normalize_state(state)
}
#[must_use]
pub fn final_state(pragma_map: &[(Range<usize>, PragmaState)]) -> PragmaState {
let state = pragma_map.last().map_or_else(PragmaState::default, |(_, s)| s.clone());
normalize_state(state)
}
}