Skip to content

Commit

Permalink
Infer type of property based on what was passed to object constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
klesun committed Mar 3, 2018
1 parent 8354356 commit 326466b
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 13 deletions.
8 changes: 8 additions & 0 deletions src/org/klesun/deep_assoc_completion/DeepType.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.klesun.deep_assoc_completion.helpers.FuncCtx;
import org.klesun.deep_assoc_completion.helpers.MultiType;
import org.klesun.lang.Lang;
import org.klesun.lang.Opt;
import org.klesun.lang.Tls;

import java.util.*;
Expand All @@ -31,6 +32,7 @@ public class DeepType extends Lang
// list of functions that take arg list and return list of return types
public final L<F<FuncCtx, L<DeepType>>> returnTypeGetters = L();
public final L<DeepType> pdoTypes = L();
public Opt<FuncCtx> ctorArgs = opt(null);
public final @Nullable String stringValue;
public final PsiElement definition;
public final PhpType briefType;
Expand Down Expand Up @@ -78,6 +80,12 @@ public DeepType(PhpExpressionImpl numPsi, Integer number)
this.isNumber = true;
}

public DeepType(PhpExpression numPsi, FuncCtx ctorArgs)
{
this(numPsi, numPsi.getType(), null);
this.ctorArgs = opt(ctorArgs);
}

public L<DeepType> getReturnTypes(FuncCtx ctx)
{
L<DeepType> result = returnTypeGetters.fap(g -> g.apply(ctx));
Expand Down
24 changes: 20 additions & 4 deletions src/org/klesun/deep_assoc_completion/helpers/FuncCtx.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.klesun.deep_assoc_completion.helpers;

import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.psi.elements.FunctionReference;
import com.jetbrains.php.lang.psi.elements.PhpExpression;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
import org.jetbrains.annotations.NotNull;
import org.klesun.deep_assoc_completion.DeepType;
Expand All @@ -12,8 +11,6 @@

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

/** a node in called function stack trace with args */
public class FuncCtx extends Lang
Expand All @@ -24,6 +21,7 @@ enum EArgPsiType {DIRECT, ARR, NONE, INDIRECT};
final private Opt<PsiElement> uniqueRef;
final private SearchContext search;
final private L<Lang.S<MultiType>> argGetters;
public Opt<Lang.S<MultiType>> instGetter = opt(null);
final private EArgPsiType argPsiType;

private HashMap<Integer, MultiType> cachedArgs = new HashMap<>();
Expand Down Expand Up @@ -83,7 +81,25 @@ public MultiType findExprType(PhpExpression expr)
}

/** when you simply call function */
public FuncCtx subCtxDirect(MethodReference funcCall)
{
FuncCtx self = subCtxDirectGeneric(funcCall);
self.instGetter = opt(funcCall.getClassReference())
.map(obj -> () -> findExprType(obj));
return self;
}

public FuncCtx subCtxDirect(FunctionReference funcCall)
{
return subCtxDirectGeneric(funcCall);
}

public FuncCtx subCtxDirect(NewExpression funcCall)
{
return subCtxDirectGeneric(funcCall);
}

public FuncCtx subCtxDirectGeneric(ParameterListOwner funcCall)
{
L<PsiElement> psiArgs = L(funcCall.getParameters());
L<S<MultiType>> argGetters = psiArgs.map((psi) -> () -> Tls.cast(PhpExpression.class, psi)
Expand Down
6 changes: 6 additions & 0 deletions src/org/klesun/deep_assoc_completion/helpers/MultiType.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.klesun.deep_assoc_completion.DeepType;
import org.klesun.lang.Lang;
import org.klesun.lang.NonNull;
import org.klesun.lang.Opt;
import org.klesun.lang.Tls;

import java.util.*;
Expand Down Expand Up @@ -189,4 +190,9 @@ public boolean isInt()
{
return types.any(t -> t.isNumber());
}

public L<FuncCtx> getArgsPassedToCtor()
{
return types.fop(t -> t.ctorArgs);
}
}
17 changes: 12 additions & 5 deletions src/org/klesun/deep_assoc_completion/resolvers/FieldRes.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package org.klesun.deep_assoc_completion.resolvers;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.PhpExpression;
import com.jetbrains.php.lang.psi.elements.impl.*;
import org.klesun.deep_assoc_completion.*;
import org.klesun.deep_assoc_completion.helpers.FuncCtx;
import org.klesun.deep_assoc_completion.helpers.MultiType;
import org.klesun.deep_assoc_completion.resolvers.var_res.AssRes;
import org.klesun.lang.Lang;
import org.klesun.lang.Opt;
import org.klesun.lang.Tls;

import java.util.List;
Expand Down Expand Up @@ -70,7 +68,16 @@ public MultiType resolve(FieldReferenceImpl fieldRef)
L<Assign> asses = opt(resolved.getContainingFile())
.map(file -> findReferences(file, fieldRef.getName()))
.def(L())
.fop(psi -> (new AssRes(implCtx)).collectAssignment(psi, false));
.fap(psi -> Tls.findParent(psi, Method.class, a -> true)
.flt(meth -> meth.getName().equals("__construct"))
.map(meth -> fieldRef.getClassReference())
.fop(toCast(PhpExpression.class))
.fop(ref -> ref.getText().equals("$this")
? ctx.instGetter.map(g -> g.get())
: opt(ctx.findExprType(ref)))
.fap(mt -> mt.getArgsPassedToCtor())
.wap(ctxs -> ctxs.size() > 0 ? ctxs : list(implCtx))
.fop(methCtx -> (new AssRes(methCtx)).collectAssignment(psi, false)));

List<DeepType> types = AssRes.assignmentsToTypes(asses);
result.addAll(types);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package org.klesun.deep_assoc_completion.resolvers;

import com.intellij.database.model.ObjectKind;
import com.intellij.database.psi.DbColumn;
import com.intellij.database.psi.DbPsiFacade;
import com.intellij.database.psi.DbTable;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.MethodReference;
Expand All @@ -15,7 +12,6 @@
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
import org.klesun.deep_assoc_completion.DeepType;
import org.klesun.deep_assoc_completion.helpers.FuncCtx;
import org.klesun.deep_assoc_completion.helpers.FuncCtx;
import org.klesun.deep_assoc_completion.helpers.MultiType;
import org.klesun.lang.Lang;
import org.klesun.lang.Opt;
Expand Down
3 changes: 3 additions & 0 deletions src/org/klesun/deep_assoc_completion/resolvers/MiscRes.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.psi.elements.PhpExpression;
import com.jetbrains.php.lang.psi.elements.impl.BinaryExpressionImpl;
import com.jetbrains.php.lang.psi.elements.impl.NewExpressionImpl;
import com.jetbrains.php.lang.psi.elements.impl.TernaryExpressionImpl;
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
import org.apache.commons.lang.StringEscapeUtils;
Expand Down Expand Up @@ -85,6 +86,8 @@ public Opt<List<DeepType>> resolve(PsiElement expr)
DeepType type = new DeepType(bin, PhpType.STRING, unescaped);
return list(type);
}))
, Tls.cast(NewExpressionImpl.class, expr)
.map(newExp -> list(new DeepType(newExp, ctx.subCtxDirect(newExp))))
, Tls.cast(PhpExpression.class, expr)
.map(t -> list(new DeepType(t)))
// , Tls.cast(ConstantReferenceImpl.class, expr)
Expand Down
84 changes: 84 additions & 0 deletions tests/src/Lib/Result.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php
namespace Lib;

/**
* Basically the same thing as Rust's std::Result is, only using PHP Exceptions
*/
class Result
{
public $isOk;
public $result;
public $error;

public static function makeOk($result)
{
return new self(true, $result);
}

public static function makeError(\Exception $error)
{
return new self(false, null, $error);
}

private function __construct($isOk, $result, \Exception $error = null)
{
if (!$isOk && is_null($error)) {
$error = new \Exception('Result Error');
}

$this->isOk = $isOk;
$this->result = $result;
$this->error = $error;
}

public function isOk()
{
return $this->isOk;
}

public function unwrap()
{
if ($this->isOk()) {
return $this->result;
} else {
throw $this->error;
}
}

public function getUsingDefault($default = null)
{
if ($this->isOk()) {
return $this->result;
} else {
return $default;
}
}

/** @return \Exception */
public function getErrorUsingDefault($default = null)
{
if ($this->isOk()) {
return $default;
} else {
return $this->error;
}
}

/**
* @param \Closure $mapper - returns new Result
* @return Result - same if was error or mapped if ok
*/
public function flatMap(\Closure $mapper): Result
{
return $this->isOk() ? $mapper($this->unwrap()) : $this;
}

public function __toString()
{
if ($this->isOk()) {
return 'Result<Ok:'.strval($this->result).'>';
} else {
return 'Result<Error>';
}
}
}
16 changes: 16 additions & 0 deletions tests/src/Lib/UnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,22 @@ public static function fetchTdData(string $ticketDesignator)
];
}

public static function provideValPassedToObjCtor()
{
$result = Result::makeError(new \Exception('asd'));
if (rand() % 2) {
$result = Result::makeOk(['a' => 5, 'b' => 6]);
} elseif (rand() % 3) {
$result = Result::makeOk(['e' => 5, 'f' => 6]);
}
$result->result[''];
$list[] = [$result->result, ['a' => [], 'b' => [], 'e' => [], 'f' => []]];
$unwrapped = $result->unwrap();
$unwrapped[''];
$list[] = [$unwrapped, ['a' => [], 'b' => [], 'e' => [], 'f' => []]];
return $list;
}

/**
* following not resolved yet
*/
Expand Down

0 comments on commit 326466b

Please sign in to comment.