use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project idea-php-typo3-plugin by cedricziel.
the class PhpLangUtil method getClassName.
public static String getClassName(@NotNull PsiElement element) {
ParameterList parameterList = PsiTreeUtil.getParentOfType(element, ParameterList.class);
if (parameterList == null) {
return null;
}
MethodReference methodReference = PsiTreeUtil.getParentOfType(element, MethodReference.class);
if (methodReference == null) {
return null;
}
Variable variableBeingCalledOn = PsiTreeUtil.findChildOfType(methodReference, Variable.class);
if (variableBeingCalledOn != null && variableBeingCalledOn.getInferredType() != null) {
PhpType inferredType = variableBeingCalledOn.getInferredType();
return inferredType.toString();
}
ClassReference classReference = PsiTreeUtil.getChildOfType(methodReference, ClassReference.class);
return extractFqnFromClassReference(methodReference, classReference);
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project idea-php-typo3-plugin by cedricziel.
the class ObjectManagerTypeProvider method getType.
@Nullable
@Override
public PhpType getType(PsiElement psiElement) {
if (DumbService.getInstance(psiElement.getProject()).isDumb()) {
return null;
}
if (!(psiElement instanceof MethodReference) || !PhpElementsUtil.isMethodWithFirstStringOrFieldReference(psiElement, "get")) {
return null;
}
MethodReference methodReference = (MethodReference) psiElement;
if (methodReference.getParameters().length == 0) {
return null;
}
PsiElement firstParam = methodReference.getParameters()[0];
if (firstParam instanceof PhpReference) {
PhpReference ref = (PhpReference) firstParam;
if (ref.getText().toLowerCase().contains("::class")) {
return new PhpType().add("#" + this.getKey() + ref.getSignature());
}
}
return null;
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project idea-php-typo3-plugin by cedricziel.
the class PhpGlobalsTypeProvider method getType.
@Nullable
@Override
public PhpType getType(PsiElement psiElement) {
// $GLOBALS['TYPO3_DB']
if (!(psiElement instanceof ArrayAccessExpression)) {
return null;
}
VariableImpl variable = PsiTreeUtil.getChildOfType(psiElement, VariableImpl.class);
if (variable == null || !variable.getName().equals("GLOBALS")) {
return null;
}
ArrayIndex arrayIndex = PsiTreeUtil.getChildOfType(psiElement, ArrayIndex.class);
if (arrayIndex == null) {
return null;
}
StringLiteralExpression arrayIndexName = PsiTreeUtil.getChildOfType(arrayIndex, StringLiteralExpressionImpl.class);
if (arrayIndexName == null) {
return null;
}
switch(arrayIndexName.getContents()) {
case "TYPO3_DB":
return new PhpType().add("#C\\TYPO3\\CMS\\Core\\Database\\DatabaseConnection");
case "TSFE":
return new PhpType().add("#C\\TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController");
case "BE_USER":
return new PhpType().add("#C\\TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication");
case "LANG":
return new PhpType().add("#C\\TYPO3\\CMS\\Lang\\LanguageService");
default:
return null;
}
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class CascadeStringReplacementInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpReturn(@NotNull PhpReturn returnStatement) {
final FunctionReference functionCall = this.getFunctionReference(returnStatement);
if (functionCall != null) {
this.analyze(functionCall, returnStatement);
}
}
@Override
public void visitPhpAssignmentExpression(@NotNull AssignmentExpression assignmentExpression) {
final FunctionReference functionCall = this.getFunctionReference(assignmentExpression);
if (functionCall != null) {
this.analyze(functionCall, assignmentExpression);
}
}
private void analyze(@NotNull FunctionReference functionCall, @NotNull PsiElement expression) {
final PsiElement[] arguments = functionCall.getParameters();
if (arguments.length == 3) {
/* case: cascading replacements */
final AssignmentExpression previous = this.getPreviousAssignment(expression);
final FunctionReference previousCall = previous == null ? null : this.getFunctionReference(previous);
if (previousCall != null) {
final PsiElement transitionVariable = previous.getVariable();
if (transitionVariable instanceof Variable && arguments[2] instanceof Variable) {
final Variable callSubject = (Variable) arguments[2];
final Variable previousVariable = (Variable) transitionVariable;
final PsiElement callResultStorage = expression instanceof AssignmentExpression ? ((AssignmentExpression) expression).getVariable() : callSubject;
if (callResultStorage != null && callSubject.getName().equals(previousVariable.getName()) && OpenapiEquivalenceUtil.areEqual(transitionVariable, callResultStorage) && this.canMergeArguments(functionCall, previousCall)) {
holder.registerProblem(functionCall, MessagesPresentationUtil.prefixWithEa(messageCascading), new MergeStringReplaceCallsFix(holder.getProject(), functionCall, previousCall, USE_SHORT_ARRAYS_SYNTAX));
}
}
}
/* case: nested replacements */
this.checkNestedCalls(holder.getProject(), arguments[2], functionCall);
/* case: search simplification */
if (arguments[1] instanceof StringLiteralExpression) {
final PsiElement search = arguments[0];
if (search instanceof ArrayCreationExpression) {
this.checkForSimplification((ArrayCreationExpression) search);
}
}
}
}
private void checkForSimplification(@NotNull ArrayCreationExpression candidate) {
final Set<String> replacements = new HashSet<>();
for (final PsiElement oneReplacement : candidate.getChildren()) {
if (oneReplacement instanceof PhpPsiElement) {
final PhpPsiElement item = ((PhpPsiElement) oneReplacement).getFirstPsiChild();
if (!(item instanceof StringLiteralExpression)) {
replacements.clear();
return;
}
replacements.add(item.getText());
}
}
if (replacements.size() == 1) {
holder.registerProblem(candidate, MessagesPresentationUtil.prefixWithEa(messageReplacements), ProblemHighlightType.WEAK_WARNING, new SimplifyReplacementFix(replacements.iterator().next()));
}
replacements.clear();
}
private void checkNestedCalls(@NotNull Project project, @NotNull PsiElement callCandidate, @NotNull FunctionReference parentCall) {
if (OpenapiTypesUtil.isFunctionReference(callCandidate)) {
final FunctionReference functionCall = (FunctionReference) callCandidate;
final String functionName = functionCall.getName();
if (functionName != null && functionName.equals("str_replace") && this.canMergeArguments(functionCall, parentCall)) {
holder.registerProblem(callCandidate, MessagesPresentationUtil.prefixWithEa(messageNesting), new MergeStringReplaceCallsFix(project, parentCall, functionCall, USE_SHORT_ARRAYS_SYNTAX));
}
}
}
private boolean canMergeArguments(@NotNull FunctionReference call, @NotNull FunctionReference previousCall) {
final PsiElement[] arguments = call.getParameters();
final PsiElement[] previousArguments = previousCall.getParameters();
// If an argument is array (non-implicit), we need PHP 7.4+ for QF
final boolean haveArrayType = Stream.of(arguments[0], arguments[1], previousArguments[0], previousArguments[1]).filter(a -> a instanceof PhpTypedElement && !(a instanceof ArrayCreationExpression)).anyMatch(a -> {
final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) a, holder.getProject());
if (resolved != null) {
final Set<String> types = resolved.filterUnknown().getTypes().stream().map(Types::getType).collect(Collectors.toSet());
return types.contains(Types.strArray) && !types.contains(Types.strString);
}
return false;
});
if (haveArrayType) {
return PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740);
}
return true;
}
@Nullable
private FunctionReference getFunctionReference(@NotNull AssignmentExpression assignment) {
FunctionReference result = null;
final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(assignment.getValue());
if (OpenapiTypesUtil.isFunctionReference(value)) {
final String functionName = ((FunctionReference) value).getName();
if (functionName != null && functionName.equals("str_replace")) {
result = (FunctionReference) value;
}
}
return result;
}
@Nullable
private FunctionReference getFunctionReference(@NotNull PhpReturn phpReturn) {
FunctionReference result = null;
final PsiElement value = ExpressionSemanticUtil.getExpressionTroughParenthesis(ExpressionSemanticUtil.getReturnValue(phpReturn));
if (OpenapiTypesUtil.isFunctionReference(value)) {
final String functionName = ((FunctionReference) value).getName();
if (functionName != null && functionName.equals("str_replace")) {
result = (FunctionReference) value;
}
}
return result;
}
@Nullable
private AssignmentExpression getPreviousAssignment(@NotNull PsiElement returnOrAssignment) {
/* get previous non-comment, non-php-doc expression */
PsiElement previous = null;
if (returnOrAssignment instanceof PhpReturn) {
previous = ((PhpReturn) returnOrAssignment).getPrevPsiSibling();
} else if (returnOrAssignment instanceof AssignmentExpression) {
previous = returnOrAssignment.getParent().getPrevSibling();
}
while (previous != null && !(previous instanceof PhpPsiElement)) {
previous = previous.getPrevSibling();
}
while (previous instanceof PhpDocComment) {
previous = ((PhpDocComment) previous).getPrevPsiSibling();
}
/* grab the target assignment */
final AssignmentExpression result;
if (previous != null && previous.getFirstChild() instanceof AssignmentExpression) {
result = (AssignmentExpression) previous.getFirstChild();
} else {
result = null;
}
return result;
}
};
}
use of com.jetbrains.php.lang.psi.resolve.types.PhpType in project phpinspectionsea by kalessil.
the class UnSafeIsSetOverArrayInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpIsset(@NotNull PhpIsset issetExpression) {
/*
* if no parameters, we don't check;
* if multiple parameters, perhaps if-inspection fulfilled and isset's were merged
*
* TODO: still needs analysis regarding concatenations in indexes
*/
final PhpExpression[] arguments = issetExpression.getVariables();
if (arguments.length != 1) {
return;
}
/* gather context information */
PsiElement issetParent = issetExpression.getParent();
boolean issetInverted = false;
if (issetParent instanceof UnaryExpression) {
final PsiElement operator = ((UnaryExpression) issetParent).getOperation();
if (OpenapiTypesUtil.is(operator, PhpTokenTypes.opNOT)) {
issetInverted = true;
issetParent = issetParent.getParent();
}
}
boolean isResultStored = (issetParent instanceof AssignmentExpression || issetParent instanceof PhpReturn);
/* false-positives: ternaries using isset-or-null semantics, there array_key_exist can introduce bugs */
final PsiElement conditionCandidate = issetInverted ? issetExpression.getParent() : issetExpression;
boolean isTernaryCondition = issetParent instanceof TernaryExpression && conditionCandidate == ((TernaryExpression) issetParent).getCondition();
if (isTernaryCondition) {
final TernaryExpression ternary = (TernaryExpression) issetParent;
final PsiElement nullCandidate = issetInverted ? ternary.getTrueVariant() : ternary.getFalseVariant();
if (PhpLanguageUtil.isNull(nullCandidate)) {
return;
}
}
/* do analyze */
final PsiElement argument = ExpressionSemanticUtil.getExpressionTroughParenthesis(arguments[0]);
if (argument == null) {
return;
}
/* false positives: variables in template/global context - too unreliable */
if (argument instanceof Variable && ExpressionSemanticUtil.getBlockScope(argument) == null) {
return;
}
if (!(argument instanceof ArrayAccessExpression)) {
if (argument instanceof FieldReference) {
/* if field is not resolved, it's probably dynamic and isset has a purpose */
final PsiReference referencedField = argument.getReference();
final PsiElement resolvedField = referencedField == null ? null : OpenapiResolveUtil.resolveReference(referencedField);
if (resolvedField == null || !(ExpressionSemanticUtil.getBlockScope(resolvedField) instanceof PhpClass)) {
return;
}
}
if (SUGGEST_TO_USE_NULL_COMPARISON) {
/* false-positives: finally, perhaps fallback to initialization in try */
if (PsiTreeUtil.getParentOfType(issetExpression, Finally.class) == null) {
final List<String> fragments = Arrays.asList(argument.getText(), issetInverted ? "===" : "!==", "null");
if (!ComparisonStyle.isRegular()) {
Collections.reverse(fragments);
}
final String replacement = String.join(" ", fragments);
holder.registerProblem(issetInverted ? issetExpression.getParent() : issetExpression, String.format(MessagesPresentationUtil.prefixWithEa(patternUseNullComparison), replacement), ProblemHighlightType.WEAK_WARNING, new CompareToNullFix(replacement));
}
}
return;
}
/* TODO: has method/function reference as index */
if (REPORT_CONCATENATION_IN_INDEXES && !isResultStored && this.hasConcatenationAsIndex((ArrayAccessExpression) argument)) {
holder.registerProblem(argument, MessagesPresentationUtil.prefixWithEa(messageConcatenationInIndex));
return;
}
if (SUGGEST_TO_USE_ARRAY_KEY_EXISTS && !isArrayAccess((ArrayAccessExpression) argument)) {
holder.registerProblem(argument, MessagesPresentationUtil.prefixWithEa(messageUseArrayKeyExists), ProblemHighlightType.WEAK_WARNING);
}
}
/* checks if any of indexes is concatenation expression */
/* TODO: iterator for array access expression */
private boolean hasConcatenationAsIndex(@NotNull ArrayAccessExpression expression) {
PsiElement expressionToInspect = expression;
while (expressionToInspect instanceof ArrayAccessExpression) {
final ArrayIndex index = ((ArrayAccessExpression) expressionToInspect).getIndex();
if (index != null && index.getValue() instanceof BinaryExpression) {
final PsiElement operation = ((BinaryExpression) index.getValue()).getOperation();
if (operation != null && operation.getNode().getElementType() == PhpTokenTypes.opCONCAT) {
return true;
}
}
expressionToInspect = expressionToInspect.getParent();
}
return false;
}
// TODO: partially duplicates semanticalAnalysis.OffsetOperationsInspector.isContainerSupportsArrayAccess()
private boolean isArrayAccess(@NotNull ArrayAccessExpression expression) {
/* ok JB parses `$var[]= ...` always as array, lets make it working properly and report them later */
final PsiElement container = expression.getValue();
if (!(container instanceof PhpTypedElement)) {
return false;
}
final Set<String> containerTypes = new HashSet<>();
final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) container, holder.getProject());
if (resolved != null) {
resolved.filterUnknown().getTypes().forEach(t -> containerTypes.add(Types.getType(t)));
}
/* failed to resolve, don't try to guess anything */
if (containerTypes.isEmpty()) {
return false;
}
boolean supportsOffsets = false;
for (final String typeToCheck : containerTypes) {
/* assume is just null-ble declaration or we shall just rust to mixed */
if (typeToCheck.equals(Types.strNull)) {
continue;
}
if (typeToCheck.equals(Types.strMixed)) {
supportsOffsets = true;
continue;
}
/* some of possible types are scalars, what's wrong */
if (!StringUtils.isEmpty(typeToCheck) && typeToCheck.charAt(0) != '\\') {
supportsOffsets = false;
break;
}
/* assume class has what is needed, OffsetOperationsInspector should report if not */
supportsOffsets = true;
}
containerTypes.clear();
return supportsOffsets;
}
};
}
Aggregations