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
131 changes: 38 additions & 93 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,17 @@ impl<'run, 'src> Analyzer<'run, 'src> {
functions.insert(function.clone());
}

AssignmentResolver::resolve_assignments(&assignments, &functions)?;
let mut variable_resolver = VariableResolver::new(&assignments, &functions)?;

let mut variable_references = HashSet::new();

for set in self.sets.values() {
for expression in set.value.expressions() {
Analyzer::resolve_references(&assignments, &functions, expression)?;
variable_resolver.resolve_expression(
expression,
&ExpressionContext::new(),
&mut variable_references,
)?;
}
}

Expand All @@ -213,52 +219,36 @@ impl<'run, 'src> Analyzer<'run, 'src> {
.values()
.any(|set| matches!(set.value, Setting::Lists(true)));

let variable_references = self
.sets
.values()
.flat_map(|set| set.value.expressions())
.chain(
self
.recipes
.iter()
.flat_map(|recipe| &recipe.attributes)
.flat_map(|attribute| {
let mut expressions = Vec::new();
match attribute {
Attribute::Arg {
help_property,
pattern_property,
..
} => {
if let Some((_, expression)) = help_property {
expressions.push(expression);
}
if let Some((_, expression)) = pattern_property {
expressions.push(expression);
}
}
Attribute::Doc(Some(expression)) => expressions.push(expression),
_ => {}
}
expressions
}),
)
.chain(module_docs.iter().map(|(_, expression)| *expression))
.flat_map(|expression| expression.references())
.filter_map(|reference| {
if let Reference::Variable(variable) = reference {
Some(variable.lexeme())
} else {
None
for attribute in self.recipes.iter().flat_map(|recipe| &recipe.attributes) {
match attribute {
Attribute::Arg {
help_property,
pattern_property,
..
} => {
if let Some((_, expression)) = help_property {
variable_resolver.collect_references(expression, &mut variable_references);
}
if let Some((_, expression)) = pattern_property {
variable_resolver.collect_references(expression, &mut variable_references);
}
}
})
.collect::<BTreeSet<&str>>();
Attribute::Doc(Some(expression)) => {
variable_resolver.collect_references(expression, &mut variable_references);
}
_ => {}
}
}

for (_name, expression) in &module_docs {
variable_resolver.collect_references(expression, &mut variable_references);
}

Evaluator::evaluate_const_assignments(
&assignments,
overrides,
&scope,
variable_references,
&variable_references,
lists,
)?
};
Expand All @@ -276,7 +266,11 @@ impl<'run, 'src> Analyzer<'run, 'src> {
}

for (name, expression) in module_docs {
Analyzer::resolve_references(&assignments, &functions, expression)?;
variable_resolver.resolve_expression(
expression,
&ExpressionContext::new(),
&mut variable_references,
)?;
let value = evaluator.evaluate_value_const(expression)?;
self.modules.get_mut(name).unwrap().doc = if value.is_empty() {
None
Expand Down Expand Up @@ -341,13 +335,12 @@ impl<'run, 'src> Analyzer<'run, 'src> {

let (recipes, disabled_recipes) = RecipeResolver::resolve_recipes(
&absent_modules,
&assignments,
&mut evaluator,
&functions,
&ast.module_path,
&self.modules,
&settings,
deduplicated_recipes,
&mut variable_resolver,
)?;

let mut recipe_aliases = Table::new();
Expand Down Expand Up @@ -535,54 +528,6 @@ impl<'run, 'src> Analyzer<'run, 'src> {

Ok(())
}

fn resolve_references(
assignments: &Table<'src, Assignment<'src>>,
functions: &'run Table<'src, FunctionDefinition<'src>>,
expression: &Expression<'src>,
) -> CompileResult<'src> {
for reference in expression.references() {
match reference {
Reference::Call { name, arguments } => {
Self::resolve_call(functions, name, arguments)?;
}
Reference::Variable(variable) => {
let name = variable.lexeme();
if !assignments.contains_key(name) && !constants().contains_key(name) {
return Err(variable.error(UndefinedVariable { variable: name }));
}
}
}
}

Ok(())
}

pub(crate) fn resolve_call(
functions: &'run Table<'src, FunctionDefinition<'src>>,
name: Name<'src>,
arguments: usize,
) -> CompileResult<'src> {
let function = name.lexeme();

let expected = if let Some(function) = functions.get(function) {
function.parameters.len()..=function.parameters.len()
} else if let Some(function) = function::get(function) {
function.expected_arguments()
} else {
return Err(name.error(CompileErrorKind::UndefinedFunction { function }));
};

if !expected.contains(&arguments) {
return Err(name.error(CompileErrorKind::FunctionArgumentCountMismatch {
arguments,
expected,
function,
}));
}

Ok(())
}
}

#[cfg(test)]
Expand Down
4 changes: 2 additions & 2 deletions src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
assignments: &'run Table<'src, Assignment<'src>>,
overrides: &'run HashMap<Number, String>,
scope: &'run Scope<'src, 'run>,
variable_references: BTreeSet<&str>,
variable_references: &HashSet<Number>,
lists: bool,
) -> CompileResult<'src, Self> {
let mut evaluator = Self {
Expand All @@ -42,7 +42,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
};

for assignment in assignments.values() {
if variable_references.contains(assignment.name.lexeme()) {
if variable_references.contains(&assignment.number) {
match evaluator
.evaluate_assignment(assignment)
.map_err(Error::unwrap_const)
Expand Down
38 changes: 38 additions & 0 deletions src/expression_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use super::*;

#[derive(Default)]
pub(crate) struct ExpressionContext<'src> {
bindings: HashMap<&'src str, Number>,
}

impl ExpressionContext<'_> {
pub(crate) fn new() -> Self {
Self::default()
}

pub(crate) fn shadows(&self, name: &str) -> bool {
self.bindings.contains_key(name)
}
}

impl<'src> From<&[(Name<'src>, Number)]> for ExpressionContext<'src> {
fn from(parameters: &[(Name<'src>, Number)]) -> Self {
Self {
bindings: parameters
.iter()
.map(|(name, number)| (name.lexeme(), *number))
.collect(),
}
}
}

impl<'src> From<&[Parameter<'src>]> for ExpressionContext<'src> {
fn from(parameters: &[Parameter<'src>]) -> Self {
Self {
bindings: parameters
.iter()
.map(|parameter| (parameter.name.lexeme(), parameter.number))
.collect(),
}
}
}
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub(crate) use {
analyzer::Analyzer,
arg_attribute::ArgAttribute,
assignment::Assignment,
assignment_resolver::AssignmentResolver,
ast::Ast,
attribute::{Attribute, AttributeKind},
attribute_set::AttributeSet,
Expand Down Expand Up @@ -54,6 +53,7 @@ pub(crate) use {
execution_context::ExecutionContext,
executor::Executor,
expression::Expression,
expression_context::ExpressionContext,
format_string_part::FormatStringPart,
fragment::Fragment,
function::Function,
Expand Down Expand Up @@ -134,6 +134,7 @@ pub(crate) use {
usage::Usage,
use_color::UseColor,
value::Value,
variable_resolver::VariableResolver,
verbosity::Verbosity,
version::Version,
warning::Warning,
Expand Down Expand Up @@ -232,7 +233,6 @@ mod analyzer;
mod arg_attribute;
mod arguments;
mod assignment;
mod assignment_resolver;
mod ast;
mod attribute;
mod attribute_set;
Expand Down Expand Up @@ -275,6 +275,7 @@ mod evaluator;
mod execution_context;
mod executor;
mod expression;
mod expression_context;
mod filesystem;
mod format_string_part;
mod fragment;
Expand Down Expand Up @@ -359,6 +360,7 @@ mod unstable_feature;
mod usage;
mod use_color;
mod value;
mod variable_resolver;
mod verbosity;
mod version;
mod warning;
Expand Down
12 changes: 4 additions & 8 deletions src/recipe_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,36 @@ use {super::*, CompileErrorKind::*};

pub(crate) struct RecipeResolver<'src: 'run, 'run> {
absent_modules: &'run BTreeSet<String>,
assignments: &'run Table<'src, Assignment<'src>>,
disabled_recipes: Table<'src, Disabled<'src>>,
evaluator: &'run mut Evaluator<'src, 'run>,
functions: &'run Table<'src, FunctionDefinition<'src>>,
modulepath: &'run Modulepath,
modules: &'run Table<'src, Justfile<'src>>,
resolved_recipes: Table<'src, Arc<Recipe<'src>>>,
settings: &'run Settings,
unresolved_recipes: Table<'src, UnresolvedRecipe<'src>>,
variable_resolver: &'run mut VariableResolver<'src, 'run>,
}

impl<'src: 'run, 'run> RecipeResolver<'src, 'run> {
pub(crate) fn resolve_recipes(
absent_modules: &'run BTreeSet<String>,
assignments: &'run Table<'src, Assignment<'src>>,
evaluator: &'run mut Evaluator<'src, 'run>,
functions: &'run Table<'src, FunctionDefinition<'src>>,
modulepath: &'run Modulepath,
modules: &'run Table<'src, Justfile<'src>>,
settings: &'run Settings,
unresolved_recipes: Table<'src, UnresolvedRecipe<'src>>,
variable_resolver: &'run mut VariableResolver<'src, 'run>,
) -> CompileResult<'src, (Table<'src, Arc<Recipe<'src>>>, Table<'src, Disabled<'src>>)> {
let mut resolver = Self {
absent_modules,
assignments,
disabled_recipes: Table::new(),
evaluator,
functions,
modulepath,
modules,
resolved_recipes: Table::new(),
settings,
unresolved_recipes,
variable_resolver,
};

while let Some(unresolved) = resolver.unresolved_recipes.pop() {
Expand Down Expand Up @@ -80,12 +77,11 @@ impl<'src: 'run, 'run> RecipeResolver<'src, 'run> {

if disabled_by.is_empty() {
let resolved = Arc::new(recipe.resolve(
self.assignments,
self.evaluator,
self.functions,
self.modulepath,
dependencies,
self.settings,
self.variable_resolver,
)?);
self.resolved_recipes.insert(Arc::clone(&resolved));
Ok(Resolution::Resolved(resolved))
Expand Down
Loading
Loading