use super::{
AnalysisContext, Scope, ScopeAnalyzer, ScopeIssue, is_builtin_global, is_capture_variable,
is_known_function, split_variable_name,
};
use crate::ast::{Node, NodeKind};
use crate::pragma_tracker::PragmaState;
use std::rc::Rc;
#[allow(clippy::too_many_arguments)]
pub(super) fn handle_variable<'a>(
analyzer: &ScopeAnalyzer,
node: &'a Node,
sigil: &'a str,
name: &'a str,
scope: &Rc<Scope>,
ancestors: &[&'a Node],
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'a>,
strict_vars_mode: bool,
) -> bool {
if sigil == "$" && is_capture_variable(name) {
if !scope.regex_match_in_scope() {
let full_name = format!("{}{}", sigil, name);
issues.push(ScopeIssue {
kind: super::IssueKind::CaptureVarWithoutRegexMatch,
variable_name: full_name.clone(),
line: context.get_line(node.location.start),
range: (node.location.start, node.location.end),
description: format!(
"Capture variable '{}' used without a preceding regex match in scope",
full_name
),
});
}
return true;
}
if is_builtin_global(sigil, name) && !scope.has_variable_parts(sigil, name) {
return true;
}
if name.contains("::") {
return true;
}
let (lookup_sigil, lookup_name) =
analyzer.resolve_variable_use_target(node, ancestors, context).unwrap_or((sigil, name));
let (variable_used, is_initialized) =
analyzer.use_variable_parts_in_context(scope, lookup_sigil, lookup_name, context);
if !variable_used {
if strict_vars_mode {
analyzer.push_undeclared_variable_issue(issues, context, node, sigil, name);
}
} else if !is_initialized {
analyzer.push_uninitialized_variable_issue(issues, context, node, sigil, name);
}
false
}
pub(super) fn handle_typeglob(
analyzer: &ScopeAnalyzer,
node: &Node,
name: &str,
scope: &Rc<Scope>,
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'_>,
strict_vars_mode: bool,
) {
let (sigil, var_name) = split_variable_name(name);
if !sigil.is_empty() && !var_name.is_empty() && !var_name.contains("::") {
analyzer.record_variable_use(
scope,
strict_vars_mode,
context,
issues,
node,
sigil,
var_name,
);
}
}
pub(super) fn handle_readline(
analyzer: &ScopeAnalyzer,
node: &Node,
filehandle: &str,
scope: &Rc<Scope>,
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'_>,
strict_vars_mode: bool,
) {
let (sigil, var_name) = split_variable_name(filehandle);
if !sigil.is_empty() && !var_name.is_empty() && !var_name.contains("::") {
analyzer.record_variable_use(
scope,
strict_vars_mode,
context,
issues,
node,
sigil,
var_name,
);
}
}
pub(super) fn handle_assignment<'a>(
analyzer: &ScopeAnalyzer,
_node: &'a Node,
lhs: &'a Node,
rhs: &'a Node,
scope: &Rc<Scope>,
ancestors: &mut Vec<&'a Node>,
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'a>,
) -> bool {
analyzer.analyze_node(rhs, scope, ancestors, issues, context);
if let NodeKind::Variable { sigil, name } = &lhs.kind {
if !name.contains("::") && !is_builtin_global(sigil, name) {
if analyzer.initialize_and_use_variable_parts_in_context(scope, sigil, name, context) {
return true;
}
}
}
analyzer.mark_initialized(lhs, scope, context);
analyzer.analyze_node(lhs, scope, ancestors, issues, context);
false
}
#[allow(clippy::too_many_arguments)]
pub(super) fn handle_tie<'a>(
analyzer: &ScopeAnalyzer,
node: &'a Node,
variable: &'a Node,
package: &'a Node,
args: &'a [Node],
scope: &Rc<Scope>,
ancestors: &mut Vec<&'a Node>,
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'a>,
) {
ancestors.push(node);
analyzer.analyze_node(package, scope, ancestors, issues, context);
for arg in args {
analyzer.analyze_node(arg, scope, ancestors, issues, context);
}
if let NodeKind::VariableDeclaration { .. } = variable.kind {
analyzer.analyze_node(variable, scope, ancestors, issues, context);
analyzer.mark_initialized(variable, scope, context);
} else {
analyzer.mark_initialized(variable, scope, context);
analyzer.analyze_node(variable, scope, ancestors, issues, context);
}
ancestors.pop();
}
pub(super) fn handle_untie<'a>(
analyzer: &ScopeAnalyzer,
node: &'a Node,
variable: &'a Node,
scope: &Rc<Scope>,
ancestors: &mut Vec<&'a Node>,
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'a>,
) {
ancestors.push(node);
analyzer.analyze_node(variable, scope, ancestors, issues, context);
ancestors.pop();
}
#[allow(clippy::too_many_arguments)]
pub(super) fn handle_identifier(
analyzer: &ScopeAnalyzer,
node: &Node,
name: &str,
issues: &mut Vec<ScopeIssue>,
context: &AnalysisContext<'_>,
ancestors: &[&Node],
pragma_state: &PragmaState,
strict_subs_mode: bool,
) {
if strict_subs_mode
&& !analyzer.is_in_hash_key_context(node, ancestors, 1)
&& !is_known_function(name)
&& !pragma_state.has_builtin_import(name)
&& !context.has_imported_bareword(name)
&& !analyzer.is_in_hash_key_context(node, ancestors, 10)
{
issues.push(ScopeIssue {
kind: super::IssueKind::UnquotedBareword,
variable_name: name.to_string(),
line: context.get_line(node.location.start),
range: (node.location.start, node.location.end),
description: format!("Bareword '{}' not allowed under 'use strict'", name),
});
}
}