Skip to content

Commit

Permalink
Fix a bunch of minor bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Oct 7, 2024
1 parent 580c18e commit f29ddb7
Show file tree
Hide file tree
Showing 17 changed files with 165 additions and 36 deletions.
4 changes: 2 additions & 2 deletions src/analyzer/expr/binop/arithmetic_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ pub(crate) fn analyze<'expr: 'tast, 'tast>(
results.push(if has_loop_variable {
match (&e1_type_atomic, &e2_type_atomic) {
(
TAtomic::TInt | TAtomic::TLiteralInt { .. },
TAtomic::TInt | TAtomic::TLiteralInt { .. },
TAtomic::TInt | TAtomic::TLiteralInt { .. } | TAtomic::TNothing,
TAtomic::TInt | TAtomic::TLiteralInt { .. } | TAtomic::TNothing,
) => match operator {
oxidized::ast_defs::Bop::Slash => TAtomic::TNum,
_ => TAtomic::TInt,
Expand Down
24 changes: 23 additions & 1 deletion src/analyzer/expr/call/argument_analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,11 +423,33 @@ pub(crate) fn verify_type(
Issue::new(
IssueKind::PossiblyInvalidArgument,
format!(
"Argument {} of {} expects {}, possibly different type {} provided",
"Argument {} of {} expects {}, possibly different type {} provided{}",
(argument_offset + 1),
functionlike_id.to_string(statements_analyzer.get_interner()),
param_type.get_id(Some(statements_analyzer.get_interner())),
input_type.get_id(Some(statements_analyzer.get_interner())),
if let Some(type_mismatch_parent_nodes) =
union_comparison_result.type_mismatch_parents
{
if !type_mismatch_parent_nodes.0.is_empty() {
if let Some(pos) = type_mismatch_parent_nodes.0[0].get_pos() {
format!(
" in :{}:{} is a mismatch with {}",
pos.start_line,
pos.start_column,
type_mismatch_parent_nodes
.1
.get_id(Some(statements_analyzer.get_interner()))
)
} else {
"".to_string()
}
} else {
"".to_string()
}
} else {
"".to_string()
}
),
statements_analyzer.get_hpos(input_expr.pos()),
&context.function_context.calling_functionlike_id,
Expand Down
1 change: 1 addition & 0 deletions src/analyzer/function_analysis_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ impl FunctionAnalysisData {
// missing shape field or shape field unknown
4057 | 4138 => match &issue_kind {
IssueKind::InvalidArgument
| IssueKind::PossiblyInvalidArgument
| IssueKind::LessSpecificArgument
| IssueKind::LessSpecificReturnStatement
| IssueKind::InvalidReturnStatement => return true,
Expand Down
13 changes: 6 additions & 7 deletions src/code_info/data_flow/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,14 +824,13 @@ impl DataFlowNode {
#[inline]
pub fn get_pos(&self) -> Option<HPos> {
match &self.kind {
DataFlowNodeKind::Vertex { pos, .. } | DataFlowNodeKind::TaintSource { pos, .. } => *pos,
DataFlowNodeKind::TaintSink { pos, .. } => Some(*pos),
DataFlowNodeKind::VariableUseSource { .. }
| DataFlowNodeKind::ForLoopInit { .. }
| DataFlowNodeKind::VariableUseSink { .. }
| DataFlowNodeKind::DataSource { .. } => {
panic!()
DataFlowNodeKind::Vertex { pos, .. } | DataFlowNodeKind::TaintSource { pos, .. } => {
*pos
}
DataFlowNodeKind::TaintSink { pos, .. }
| DataFlowNodeKind::VariableUseSource { pos, .. }
| DataFlowNodeKind::DataSource { pos, .. } => Some(*pos),
DataFlowNodeKind::ForLoopInit { .. } | DataFlowNodeKind::VariableUseSink { .. } => None,
}
}
}
26 changes: 16 additions & 10 deletions src/code_info/t_atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,18 +1006,24 @@ impl TAtomic {
)
}

#[inline]
pub fn is_string_subtype(&self) -> bool {
matches!(
self,
match self {
TAtomic::TLiteralClassname { .. }
| TAtomic::TLiteralString { .. }
| TAtomic::TClassname { .. }
| TAtomic::TTypename { .. }
| TAtomic::TGenericClassname { .. }
| TAtomic::TGenericTypename { .. }
| TAtomic::TStringWithFlags { .. }
)
| TAtomic::TLiteralString { .. }
| TAtomic::TClassname { .. }
| TAtomic::TTypename { .. }
| TAtomic::TGenericClassname { .. }
| TAtomic::TGenericTypename { .. }
| TAtomic::TStringWithFlags { .. } => true,
TAtomic::TTypeAlias {
as_type: Some(as_type),
..
} => {
as_type.is_single()
&& (as_type.types[0].is_string() || as_type.types[0].is_string_subtype())
}
_ => false,
}
}

#[inline]
Expand Down
43 changes: 29 additions & 14 deletions src/code_info/t_union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,28 +417,43 @@ impl TUnion {
pub fn is_literal_of(&self, other: &TUnion) -> bool {
let other_atomic_type = other.types.first().unwrap();

if let TAtomic::TString = other_atomic_type {
for self_atomic_type in &self.types {
if self_atomic_type.is_string_subtype() {
continue;
match other_atomic_type {
TAtomic::TString => {
for self_atomic_type in &self.types {
if self_atomic_type.is_string_subtype() {
continue;
}

return false;
}

return false;
true
}
TAtomic::TInt => {
for self_atomic_type in &self.types {
if let TAtomic::TLiteralInt { .. } = self_atomic_type {
continue;
}

true
} else if let TAtomic::TInt = other_atomic_type {
for self_atomic_type in &self.types {
if let TAtomic::TLiteralInt { .. } = self_atomic_type {
continue;
return false;
}

return false;
true
}
TAtomic::TEnum { name, .. } => {
for self_atomic_type in &self.types {
if let TAtomic::TEnumLiteralCase { enum_name, .. } = self_atomic_type {
if enum_name == name {
continue;
}
}

true
} else {
false
return false;
}

true
}
_ => false,
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/code_info/ttype/comparison/dict_type_comparator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ pub(crate) fn is_contained_by(
for (key, (c_u, container_property_type)) in container_known_items {
if let Some((i_u, input_property_type)) = input_known_items.get(key) {
if *i_u && !c_u {
if atomic_comparison_result.type_mismatch_parents.is_none() {
atomic_comparison_result.type_mismatch_parents = Some((
input_property_type.parent_nodes.clone(),
container_property_type.clone(),
));
}
all_types_contain = false;
}

Expand All @@ -45,6 +51,12 @@ pub(crate) fn is_contained_by(
inside_assertion,
atomic_comparison_result,
) {
if atomic_comparison_result.type_mismatch_parents.is_none() {
atomic_comparison_result.type_mismatch_parents = Some((
input_property_type.parent_nodes.clone(),
container_property_type.clone(),
));
}
all_types_contain = false;
}
} else if !c_u {
Expand Down
6 changes: 6 additions & 0 deletions src/code_info/ttype/comparison/type_comparison_result.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::sync::Arc;

use crate::data_flow::node::DataFlowNode;
use crate::{t_atomic::TAtomic, t_union::TUnion};
use crate::ttype::template::TemplateBound;

Expand Down Expand Up @@ -34,6 +37,8 @@ pub struct TypeComparisonResult {

pub type_variable_lower_bounds: Vec<(String, TemplateBound)>,
pub type_variable_upper_bounds: Vec<(String, TemplateBound)>,

pub type_mismatch_parents: Option<(Vec<DataFlowNode>, Arc<TUnion>)>,
}

impl Default for TypeComparisonResult {
Expand All @@ -55,6 +60,7 @@ impl TypeComparisonResult {
type_variable_lower_bounds: vec![],
type_variable_upper_bounds: vec![],
upcasted_awaitable: false,
type_mismatch_parents: None,
}
}
}
4 changes: 4 additions & 0 deletions src/code_info/ttype/comparison/union_type_comparator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ pub fn is_contained_by(
union_comparison_result
.type_variable_upper_bounds
.extend(atomic_comparison_result.type_variable_upper_bounds);
} else {
if union_comparison_result.type_mismatch_parents.is_none() {
union_comparison_result.type_mismatch_parents = atomic_comparison_result.type_mismatch_parents;
}
}

if atomic_comparison_result.type_coerced.unwrap_or(false) {
Expand Down
4 changes: 2 additions & 2 deletions src/code_info_builder/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,14 +771,14 @@ fn fix_function_return_type(function_name: StrId, functionlike_storage: &mut Fun
| StrId::STRTOLOWER
| StrId::STRTOUPPER
| StrId::MB_STRTOLOWER
| StrId::MB_STRTOUPPER => functionlike_storage.return_type = Some(get_string()),
| StrId::MB_STRTOUPPER
| StrId::DATE => functionlike_storage.return_type = Some(get_string()),

// falsable strings
StrId::JSON_ENCODE
| StrId::FILE_GET_CONTENTS
| StrId::HEX2BIN
| StrId::REALPATH
| StrId::DATE
| StrId::BASE64_DECODE
| StrId::DATE_FORMAT
| StrId::HASH_HMAC => {
Expand Down
21 changes: 21 additions & 0 deletions tests/inference/BinaryOperation/loopAddition/input.hack
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function foo(): void {
$p1 = 0;

while (rand(0, 1)) {
if (rand(0, 1)) {
if ($p1 === 0) {
bar($p1);
} else {
$p1 = $p1 - 1;
}
}

if (rand(0, 1)) {
$p1 = $p1 - 1;
}
}

bar($p1);
}

function bar(int $i): void {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function foo(shape('a' => string) $arr): void {
}

function bar(shape('a' => string) $arr, int $i): void {
if (rand(0, 1)) {
$arr['a'] = $i;
}
foo($arr);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ERROR: PossiblyInvalidArgument - input.hack:8:9 - Argument 1 of foo expects shape('a' => string), possibly different type shape('a' => string|int) provided in :4:45 is a mismatch with string
21 changes: 21 additions & 0 deletions tests/inference/Generics/Class/enumMemberMatchesEnum/input.hack
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
final class A<T> {
public function __construct(public T $t) {}
}

function makeA<T>(T $t): A<T> {
return new A($t);
}

enum B: int as int {
A = 1;
B = 2;
C = 3;
}

function foo(): void {
$a = makeA(B::B);
bar($a);
}

function bar(A<B> $_a): void {
}
1 change: 1 addition & 0 deletions tests/inference/Generics/Class/newtypeAs/defs.hack
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
newtype foo_t as other_t = other_t;
10 changes: 10 additions & 0 deletions tests/inference/Generics/Class/newtypeAs/input.hack
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
final class A<T> {
public function __construct(public T $t) {}
}

function foo(A<foo_t> $a): void {
bar($a);
}

function bar(A<string> $_a): void {
}
1 change: 1 addition & 0 deletions tests/inference/Generics/Class/newtypeAs/other_defs.hack
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
newtype other_t as string = string;

0 comments on commit f29ddb7

Please sign in to comment.