Skip to content

Commit

Permalink
Migrate awaitables to TAwaitable to support effects
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed May 21, 2024
1 parent e52570a commit c2b58fa
Show file tree
Hide file tree
Showing 29 changed files with 424 additions and 143 deletions.
10 changes: 10 additions & 0 deletions src/analyzer/expr/call/arguments_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ pub(crate) fn check_arguments_match(

for (_, arg_expr) in args.iter() {
let was_inside_call = context.inside_general_use;
let was_inside_asio_join = context.inside_asio_join;

if matches!(functionlike_info.effects, FnEffect::Some(_))
|| matches!(functionlike_info.effects, FnEffect::Arg(_))
Expand All @@ -186,6 +187,11 @@ pub(crate) fn check_arguments_match(
|| functionlike_info.method_info.is_some()
{
context.inside_general_use = true;
} else if matches!(
functionlike_id,
FunctionLikeIdentifier::Function(StrId::ASIO_JOIN)
) {
context.inside_asio_join = true;
}

// don't analyse closures here
Expand All @@ -202,6 +208,10 @@ pub(crate) fn check_arguments_match(
if !was_inside_call {
context.inside_general_use = false;
}

if !was_inside_asio_join {
context.inside_asio_join = false;
}
}

let mut reordered_args = args.iter().enumerate().collect::<Vec<_>>();
Expand Down
47 changes: 42 additions & 5 deletions src/analyzer/expr/call/function_call_return_type_fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ use hakana_reflection_info::functionlike_info::FunctionLikeInfo;
use hakana_reflection_info::t_atomic::{DictKey, TAtomic};
use hakana_reflection_info::t_union::TUnion;
use hakana_reflection_info::taint::SinkType;
use hakana_reflection_info::GenericParent;
use hakana_reflection_info::{GenericParent, EFFECT_IMPURE};
use hakana_str::{Interner, StrId};
use hakana_type::type_comparator::type_comparison_result::TypeComparisonResult;
use hakana_type::type_comparator::union_type_comparator;
use hakana_type::type_expander::TypeExpansionOptions;
use hakana_type::{
add_union_type, get_arrayish_params, get_float, get_int, get_literal_string, get_mixed,
get_mixed_any, get_mixed_vec, get_nothing, get_null, get_object, get_string, get_vec, template,
type_expander, wrap_atomic,
add_union_type, extend_dataflow_uniquely, get_arrayish_params, get_float, get_int,
get_literal_string, get_mixed, get_mixed_any, get_mixed_vec, get_nothing, get_null, get_object,
get_string, get_vec, template, type_expander, wrap_atomic,
};
use rustc_hash::FxHashMap;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -132,6 +132,7 @@ pub(crate) fn fetch(
.get_file_source()
.file_path,
),
effects: Some(function_storage.effects.to_u8().unwrap_or(EFFECT_IMPURE)),
..Default::default()
},
&mut analysis_data.data_flow_graph,
Expand Down Expand Up @@ -599,6 +600,42 @@ fn handle_special_functions(

None
}
&StrId::ASIO_JOIN => {
if args.len() == 1 {
let mut awaited_type = analysis_data
.get_expr_type(args[0].1.pos())
.cloned()
.unwrap_or(get_mixed_any());

let awaited_types = awaited_type.types.drain(..).collect::<Vec<_>>();

let mut new_types = vec![];

for atomic_type in awaited_types {
if let TAtomic::TAwaitable { value, effects } = atomic_type {
let inside_type = (*value).clone();
extend_dataflow_uniquely(
&mut awaited_type.parent_nodes,
inside_type.parent_nodes,
);
new_types.extend(inside_type.types);

analysis_data.expr_effects.insert(
(pos.start_offset() as u32, pos.end_offset() as u32),
effects.unwrap_or(EFFECT_IMPURE),
);
} else {
new_types.push(atomic_type);
}
}

awaited_type.types = new_types;

Some(awaited_type)
} else {
None
}
}
_ => None,
}
}
Expand Down Expand Up @@ -942,6 +979,7 @@ fn get_special_argument_nodes(
) -> (Vec<(usize, PathKind)>, Option<PathKind>) {
match functionlike_id {
FunctionLikeIdentifier::Function(function_name) => match *function_name {
StrId::ASIO_JOIN => (vec![], None),
StrId::VAR_EXPORT
| StrId::PRINT_R
| StrId::HIGHLIGHT_STRING
Expand All @@ -954,7 +992,6 @@ fn get_special_argument_nodes(
| StrId::LIB_STR_UPPERCASE
| StrId::LIB_STR_CAPITALIZE
| StrId::LIB_STR_CAPITALIZE_WORDS
| StrId::ASIO_JOIN
| StrId::STRIP_TAGS
| StrId::STRIPSLASHES
| StrId::STRIPCSLASHES
Expand Down
8 changes: 7 additions & 1 deletion src/analyzer/expr/call/method_call_return_type_fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::rc::Rc;

use hakana_reflection_info::functionlike_identifier::FunctionLikeIdentifier;
use hakana_reflection_info::{ExprId, GenericParent, VarId};
use hakana_reflection_info::{ExprId, GenericParent, VarId, EFFECT_IMPURE};
use hakana_str::{Interner, StrId};
use hakana_type::get_mixed;
use oxidized::{aast, ast_defs};
Expand Down Expand Up @@ -133,6 +133,12 @@ pub(crate) fn fetch(
.get_file_source()
.file_path,
),
effects: Some(
functionlike_storage
.effects
.to_u8()
.unwrap_or(EFFECT_IMPURE),
),
..Default::default()
},
&mut analysis_data.data_flow_graph,
Expand Down
113 changes: 75 additions & 38 deletions src/analyzer/expr/variable_fetch_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use crate::{
use hakana_reflection_info::{
code_location::HPos,
data_flow::{
graph::GraphKind,
graph::{DataFlowGraph, GraphKind},
node::{DataFlowNode, DataFlowNodeId, DataFlowNodeKind},
path::PathKind,
},
issue::{Issue, IssueKind},
t_atomic::TAtomic,
t_union::TUnion,
taint::SourceType,
VarId, EFFECT_READ_GLOBALS,
VarId, EFFECT_IMPURE, EFFECT_PURE, EFFECT_READ_GLOBALS,
};
use hakana_type::{get_int, get_mixed_any, get_mixed_dict};
use oxidized::{ast_defs::Pos, tast::Lid};
Expand Down Expand Up @@ -206,47 +207,83 @@ fn add_dataflow_to_variable(
if data_flow_graph.kind == GraphKind::FunctionBody
&& (context.inside_general_use || context.inside_throw || context.inside_isset)
{
let pos = statements_analyzer.get_hpos(pos);

let assignment_node = DataFlowNode {
id: if let Some(var_id) = statements_analyzer.get_interner().get(&lid.1 .1) {
DataFlowNodeId::Var(
VarId(var_id),
pos.file_path,
pos.start_offset,
pos.end_offset,
)
} else {
DataFlowNodeId::LocalString(
lid.1 .1.to_string(),
pos.file_path,
pos.start_offset,
pos.end_offset,
)
},
kind: DataFlowNodeKind::VariableUseSink { pos },
};
add_dataflow_to_used_var(
statements_analyzer,
pos,
lid,
data_flow_graph,
&mut stmt_type,
);
} else if data_flow_graph.kind == GraphKind::FunctionBody && context.inside_asio_join {
if let TAtomic::TAwaitable { effects, .. } = stmt_type.get_single() {
if effects.unwrap_or(EFFECT_IMPURE) != EFFECT_PURE {
add_dataflow_to_used_var(
statements_analyzer,
pos,
lid,
data_flow_graph,
&mut stmt_type,
);
}
} else {
add_dataflow_to_used_var(
statements_analyzer,
pos,
lid,
data_flow_graph,
&mut stmt_type,
);
}
}

data_flow_graph.add_node(assignment_node.clone());
stmt_type
}

let mut parent_nodes = stmt_type.parent_nodes.clone();
fn add_dataflow_to_used_var(
statements_analyzer: &StatementsAnalyzer,
pos: &Pos,
lid: &Lid,
data_flow_graph: &mut DataFlowGraph,
stmt_type: &mut TUnion,
) {
let pos = statements_analyzer.get_hpos(pos);

if parent_nodes.is_empty() {
parent_nodes.push(assignment_node);
let assignment_node = DataFlowNode {
id: if let Some(var_id) = statements_analyzer.get_interner().get(&lid.1 .1) {
DataFlowNodeId::Var(
VarId(var_id),
pos.file_path,
pos.start_offset,
pos.end_offset,
)
} else {
for parent_node in &parent_nodes {
data_flow_graph.add_path(
parent_node,
&assignment_node,
PathKind::Default,
vec![],
vec![],
);
}
}
DataFlowNodeId::LocalString(
lid.1 .1.to_string(),
pos.file_path,
pos.start_offset,
pos.end_offset,
)
},
kind: DataFlowNodeKind::VariableUseSink { pos },
};

data_flow_graph.add_node(assignment_node.clone());

let mut parent_nodes = stmt_type.parent_nodes.clone();

stmt_type.parent_nodes = parent_nodes;
if parent_nodes.is_empty() {
parent_nodes.push(assignment_node);
} else {
for parent_node in &parent_nodes {
data_flow_graph.add_path(
parent_node,
&assignment_node,
PathKind::Default,
vec![],
vec![],
);
}
}

stmt_type
stmt_type.parent_nodes = parent_nodes;
}
36 changes: 15 additions & 21 deletions src/analyzer/expression_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,34 +411,28 @@ pub(crate) fn analyze(
.cloned()
.unwrap_or(get_mixed_any());

analysis_data.expr_effects.insert(
(expr.1.start_offset() as u32, expr.1.end_offset() as u32),
EFFECT_IMPURE,
);

let awaited_types = awaited_stmt_type.types.drain(..).collect::<Vec<_>>();

let mut new_types = vec![];

for atomic_type in awaited_types {
if let TAtomic::TNamedObject {
name: StrId::AWAITABLE,
type_params: Some(ref type_params),
..
} = atomic_type
{
if type_params.len() == 1 {
let inside_type = type_params.first().unwrap().clone();
extend_dataflow_uniquely(
&mut awaited_stmt_type.parent_nodes,
inside_type.parent_nodes,
);
new_types.extend(inside_type.types);
} else {
new_types.push(atomic_type);
}
if let TAtomic::TAwaitable { value, effects } = atomic_type {
let inside_type = (*value).clone();
extend_dataflow_uniquely(
&mut awaited_stmt_type.parent_nodes,
inside_type.parent_nodes,
);
new_types.extend(inside_type.types);
analysis_data.expr_effects.insert(
(expr.1.start_offset() as u32, expr.1.end_offset() as u32),
effects.unwrap_or(EFFECT_IMPURE),
);
} else {
new_types.push(atomic_type);
analysis_data.expr_effects.insert(
(expr.1.start_offset() as u32, expr.1.end_offset() as u32),
EFFECT_IMPURE,
);
}
}

Expand Down
18 changes: 6 additions & 12 deletions src/analyzer/functionlike_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,12 +703,9 @@ impl<'a> FunctionLikeAnalyzer<'a> {
get_void()
};
inferred_return_type = Some(if functionlike_storage.is_async {
wrap_atomic(TAtomic::TNamedObject {
name: StrId::AWAITABLE,
type_params: Some(vec![fn_return_value]),
is_this: false,
extra_types: None,
remapped_params: false,
wrap_atomic(TAtomic::TAwaitable {
value: Box::new(fn_return_value),
effects: functionlike_storage.effects.to_u8(),
})
} else {
fn_return_value
Expand Down Expand Up @@ -740,12 +737,9 @@ impl<'a> FunctionLikeAnalyzer<'a> {
}
} else {
inferred_return_type = Some(if functionlike_storage.is_async {
wrap_atomic(TAtomic::TNamedObject {
name: StrId::AWAITABLE,
type_params: Some(vec![get_void()]),
is_this: false,
extra_types: None,
remapped_params: false,
wrap_atomic(TAtomic::TAwaitable {
value: Box::new(get_void()),
effects: functionlike_storage.effects.to_u8(),
})
} else {
get_void()
Expand Down
6 changes: 6 additions & 0 deletions src/analyzer/scope_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ pub struct ScopeContext {

pub inside_awaitall: bool,

/**
* Whether or not we're inside an HH\Asio\join
*/
pub inside_asio_join: bool,

pub include_location: Option<Pos>,

/**
Expand Down Expand Up @@ -193,6 +198,7 @@ impl ScopeContext {
inside_try: false,
inside_awaitall: false,
inside_loop_exprs: false,
inside_asio_join: false,

inside_negation: false,
has_returned: false,
Expand Down
9 changes: 3 additions & 6 deletions src/analyzer/stmt/return_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,9 @@ pub(crate) fn analyze(

if functionlike_storage.is_async {
let parent_nodes = inferred_return_type.parent_nodes.clone();
inferred_return_type = wrap_atomic(TAtomic::TNamedObject {
name: StrId::AWAITABLE,
type_params: Some(vec![inferred_return_type]),
is_this: false,
extra_types: None,
remapped_params: false,
inferred_return_type = wrap_atomic(TAtomic::TAwaitable {
value: Box::new(inferred_return_type),
effects: None,
});
extend_dataflow_uniquely(&mut inferred_return_type.parent_nodes, parent_nodes);
}
Expand Down
Loading

0 comments on commit c2b58fa

Please sign in to comment.