use of com.jetbrains.php.PhpIndex in project idea-php-typo3-plugin by cedricziel.
the class RouteHelper method getTargetMethods.
@NotNull
private static PsiElement[] getTargetMethods(@NotNull Project project, @NotNull String routeName) {
List<PsiElement> result = new ArrayList<>();
List<RouteStub> values = FileBasedIndex.getInstance().getValues(RouteIndex.KEY, routeName, GlobalSearchScope.allScope(project));
PhpIndex phpIndex = PhpIndex.getInstance(project);
for (RouteStub routeStub : values) {
String fqn = routeStub.getController();
Collection<PhpClass> classesByFQN = phpIndex.getClassesByFQN(fqn);
classesByFQN.forEach(c -> {
if (c.findMethodByName(routeStub.getMethod()) != null) {
result.add(c.findMethodByName(routeStub.getMethod()));
}
});
}
return result.toArray(new PsiElement[result.size()]);
}
use of com.jetbrains.php.PhpIndex in project phpinspectionsea by kalessil.
the class UnqualifiedReferenceInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
final String functionName = reference.getName();
if (functionName != null && !functionName.isEmpty()) {
/* ensure php version is at least PHP 7.0; makes sense only with PHP7+ opcode */
if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
if (REPORT_ALL_FUNCTIONS || advancedOpcode.contains(functionName)) {
this.analyzeReference(reference, functionName);
}
if (callbacksPositions.containsKey(functionName)) {
this.analyzeCallback(reference, functionName);
}
}
}
}
@Override
public void visitPhpConstantReference(@NotNull ConstantReference reference) {
final String constantName = reference.getName();
if (constantName != null && !constantName.isEmpty() && REPORT_CONSTANTS) {
/* ensure php version is at least PHP 7.0; makes sense only with PHP7+ opcode */
if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP700)) {
this.analyzeReference(reference, constantName);
}
}
}
private void analyzeCallback(@NotNull FunctionReference reference, @NotNull String functionName) {
final PsiElement[] arguments = reference.getParameters();
if (arguments.length >= 2) {
final Integer callbackPosition = callbacksPositions.get(functionName);
if (arguments[callbackPosition] instanceof StringLiteralExpression) {
final StringLiteralExpression callback = (StringLiteralExpression) arguments[callbackPosition];
if (callback.getFirstPsiChild() == null) {
final String function = callback.getContents();
final boolean isCandidate = !function.startsWith("\\") && !function.contains("::");
if (isCandidate && (REPORT_ALL_FUNCTIONS || advancedOpcode.contains(function))) {
final PhpIndex index = PhpIndex.getInstance(holder.getProject());
if (!index.getFunctionsByFQN('\\' + functionName).isEmpty()) {
holder.registerProblem(callback, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), function), new TheLocalFix());
}
}
}
}
}
}
private void analyzeReference(@NotNull PhpReference reference, @NotNull String referenceName) {
/* some constants prefixing is making no sense IMO */
if (reference instanceof ConstantReference && falsePositives.contains(referenceName)) {
return;
}
/* NS specification is identified differently for { define } and { call, constant } */
final PsiElement nsCandidate = reference.getFirstChild();
if (nsCandidate instanceof PhpNamespaceReference || OpenapiTypesUtil.is(nsCandidate, PhpTokenTypes.NAMESPACE_RESOLUTION)) {
return;
}
final PhpNamespace ns = this.findNamespace(reference);
if (ns == null) {
return;
}
/* resolve the constant/function, report if it's from the root NS */
final PsiElement subject = OpenapiResolveUtil.resolveReference(reference);
final boolean isFunction = subject instanceof Function;
if (isFunction || subject instanceof Constant) {
/* false-positives: non-root NS function/constant referenced */
if (((PhpNamedElement) subject).getFQN().equals('\\' + referenceName)) {
final GroupStatement body = ns.getStatements();
if (body != null) {
final List<PhpUse> imports = new ArrayList<>();
for (final PsiElement child : body.getChildren()) {
if (child instanceof PhpUseList) {
Collections.addAll(imports, ((PhpUseList) child).getDeclarations());
}
}
/* false-positive: function/constant are imported already */
boolean isImported = false;
for (final PhpUse use : imports) {
final PsiElement candidate = use.getFirstPsiChild();
String importedSymbol = null;
if (candidate instanceof FunctionReference) {
importedSymbol = ((FunctionReference) candidate).getName();
} else if (candidate instanceof ConstantReference) {
importedSymbol = ((ConstantReference) candidate).getName();
}
if (isImported = referenceName.equals(importedSymbol)) {
break;
}
}
imports.clear();
if (!isImported) {
holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), referenceName + (isFunction ? "(...)" : "")), new TheLocalFix());
}
}
}
}
}
@Nullable
private PhpNamespace findNamespace(@NotNull PhpReference reference) {
final PsiFile file = reference.getContainingFile();
if (file.getFileType() == PhpFileType.INSTANCE) {
final List<PhpNamespace> namespaces = new ArrayList<>();
((PhpFile) file).getTopLevelDefs().values().stream().filter(definition -> definition instanceof PhpNamespace).forEach(definition -> namespaces.add((PhpNamespace) definition));
if (namespaces.isEmpty()) {
return null;
} else if (namespaces.size() == 1) {
return namespaces.get(0);
}
namespaces.clear();
}
return (PhpNamespace) PsiTreeUtil.findFirstParent(reference, PARENT_NAMESPACE);
}
};
}
use of com.jetbrains.php.PhpIndex in project phpinspectionsea by kalessil.
the class MissUsingParentKeywordInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpMethodReference(@NotNull MethodReference reference) {
final PsiElement base = reference.getClassReference();
if (base instanceof ClassReference && base.getText().equals("parent")) {
final Function scope = ExpressionSemanticUtil.getScope(reference);
if (scope instanceof Method) {
final Method context = (Method) scope;
final PhpClass clazz = context.getContainingClass();
if (clazz != null && !clazz.isTrait() && !context.isStatic()) {
final String methodName = scope.getName();
final String referenceName = reference.getName();
if (referenceName != null && !referenceName.equals(methodName)) {
final boolean isTarget = clazz.findOwnMethodByName(referenceName) == null && !this.isOverridden(clazz, referenceName);
if (isTarget) {
final PsiElement resolved = OpenapiResolveUtil.resolveReference(reference);
if (resolved instanceof Method) {
final PsiElement parameters = reference.getParameterList();
final String replacement = String.format(((Method) resolved).isStatic() ? "self::%s(%s)" : "$this->%s(%s)", referenceName, parameters == null ? "" : parameters.getText());
holder.registerProblem(reference, String.format(MessagesPresentationUtil.prefixWithEa(messagePattern), replacement), new NormalizeClassReferenceFix(replacement));
}
}
}
}
}
}
}
final boolean isOverridden(@NotNull PhpClass clazz, @NotNull String methodName) {
if (!clazz.isFinal()) {
final PhpIndex index = PhpIndex.getInstance(holder.getProject());
final Collection<PhpClass> children = OpenapiResolveUtil.resolveChildClasses(clazz.getFQN(), index);
return children.stream().anyMatch(c -> c.findOwnMethodByName(methodName) != null);
}
return false;
}
};
}
use of com.jetbrains.php.PhpIndex in project phpinspectionsea by kalessil.
the class ProperNullCoalescingOperatorUsageInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpBinaryExpression(@NotNull BinaryExpression binary) {
if (binary.getOperationType() == PhpTokenTypes.opCOALESCE && !this.isPartOfCoalesce(binary) && !this.isTypeCasted(binary)) {
final PsiElement left = binary.getLeftOperand();
final PsiElement right = binary.getRightOperand();
if (left != null && right != null) {
/* case: `call() ?? null` */
if (PhpLanguageUtil.isNull(right)) {
if (left instanceof FunctionReference) {
holder.registerProblem(binary, String.format(MessagesPresentationUtil.prefixWithEa(messageSimplify), left.getText()), new UseLeftOperandFix(left.getText()));
}
return;
}
/* case: `returns_string_or_null() ?? []` */
if (ANALYZE_TYPES && left instanceof PhpTypedElement && right instanceof PhpTypedElement) {
final Function scope = ExpressionSemanticUtil.getScope(binary);
if (scope != null) {
final Set<String> leftTypes = this.resolve((PhpTypedElement) left);
if (leftTypes != null && !leftTypes.isEmpty()) {
final Set<String> rightTypes = this.resolve((PhpTypedElement) right);
if (rightTypes != null && !rightTypes.isEmpty()) {
final boolean complimentary = ALLOW_OVERLAPPING_TYPES ? rightTypes.stream().anyMatch(leftTypes::contains) : rightTypes.containsAll(leftTypes);
if (!complimentary && !this.areRelated(rightTypes, leftTypes)) {
holder.registerProblem(binary, String.format(MessagesPresentationUtil.prefixWithEa(messageMismatch), leftTypes.toString(), rightTypes.toString()));
}
rightTypes.clear();
}
leftTypes.clear();
}
}
}
}
}
}
private boolean isTypeCasted(@NotNull BinaryExpression binary) {
final PsiElement parent = binary.getParent();
if (parent instanceof ParenthesizedExpression) {
final PsiElement grandParent = parent.getParent();
if (grandParent instanceof UnaryExpression) {
final PsiElement operator = ((UnaryExpression) grandParent).getOperation();
return operator != null && PhpTokenTypes.tsCAST_OPS.contains(operator.getNode().getElementType());
}
}
return false;
}
private boolean isPartOfCoalesce(@NotNull BinaryExpression binary) {
final PsiElement parent = binary.getParent();
return parent instanceof BinaryExpression && ((BinaryExpression) parent).getOperationType() == PhpTokenTypes.opCOALESCE;
}
private boolean areRelated(@NotNull Set<String> rightTypes, @NotNull Set<String> leftTypes) {
final Set<PhpClass> left = this.extractClasses(leftTypes);
if (!left.isEmpty()) {
final Set<PhpClass> right = this.extractClasses(rightTypes);
if (!right.isEmpty() && left.stream().anyMatch(right::contains)) {
left.clear();
right.clear();
return true;
}
left.clear();
}
return false;
}
private HashSet<PhpClass> extractClasses(@NotNull Set<String> types) {
final HashSet<PhpClass> classes = new HashSet<>();
final PhpIndex index = PhpIndex.getInstance(holder.getProject());
types.stream().filter(t -> t.startsWith("\\")).forEach(t -> OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(t, index).forEach(c -> classes.addAll(InterfacesExtractUtil.getCrawlInheritanceTree(c, true))));
return classes;
}
@Nullable
private Set<String> resolve(@NotNull PhpTypedElement subject) {
final PhpType type = OpenapiResolveUtil.resolveType(subject, holder.getProject());
if (type != null && !type.hasUnknown()) {
final Set<String> types = type.getTypes().stream().map(Types::getType).collect(Collectors.toSet());
if (!types.isEmpty() && !types.contains(Types.strMixed) && !types.contains(Types.strObject)) {
types.remove(Types.strStatic);
types.remove(Types.strNull);
return types;
}
types.clear();
}
return null;
}
};
}
use of com.jetbrains.php.PhpIndex in project phpinspectionsea by kalessil.
the class ClassConstantCanBeUsedInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpFunctionCall(@NotNull FunctionReference reference) {
if (PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP550)) {
final String functionName = reference.getName();
if (functionName != null) {
if (functionName.equals("get_called_class")) {
final PsiElement[] arguments = reference.getParameters();
if (arguments.length == 0) {
holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageUseStatic), new UseStaticClassConstantFix());
}
} else if (functionName.equals("get_parent_class")) {
final PsiElement[] arguments = reference.getParameters();
if (arguments.length == 0 && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP550)) {
holder.registerProblem(reference, MessagesPresentationUtil.prefixWithEa(messageUseParent), new UseParentClassConstantFix());
}
}
}
}
}
@Override
public void visitPhpStringLiteralExpression(@NotNull StringLiteralExpression expression) {
/* ensure selected language level supports the ::class feature*/
final Project project = holder.getProject();
if (PhpLanguageLevel.get(project).below(PhpLanguageLevel.PHP550)) {
return;
}
/* Skip certain contexts processing and strings with inline injections */
if (!OpenapiTypesUtil.isString(expression) || expression.getFirstPsiChild() != null) {
return;
}
final PsiElement parent = expression.getParent();
if (parent instanceof BinaryExpression) {
boolean process = false;
final BinaryExpression binary = (BinaryExpression) parent;
final PsiElement left = binary.getLeftOperand();
if (binary.getOperationType() == PhpTokenTypes.opCONCAT && left instanceof ConstantReference) {
final String constantName = ((ConstantReference) left).getName();
process = constantName != null && constantName.equals("__NAMESPACE__");
}
if (!process) {
return;
}
}
if (parent instanceof SelfAssignmentExpression || this.isClassAlias(expression)) {
return;
}
/* Process if has no inline statements and at least 3 chars long (foo, bar and etc. are not a case) */
final String contents = this.populateLiteralContent(expression, parent instanceof BinaryExpression);
if (contents.length() > 3 && classNameRegex.matcher(contents).matches()) {
/* do not process lowercase-only strings */
if (contents.indexOf('\\') == -1 && contents.toLowerCase().equals(contents)) {
return;
}
String normalizedContents = contents.replaceAll("\\\\\\\\", "\\\\");
final boolean isFull = normalizedContents.charAt(0) == '\\';
final Set<String> namesToLookup = new HashSet<>();
if (isFull) {
namesToLookup.add(normalizedContents);
} else {
if (LOOK_ROOT_NS_UP || normalizedContents.contains("\\")) {
normalizedContents = '\\' + normalizedContents;
namesToLookup.add(normalizedContents);
}
}
/* if we could find an appropriate candidate and resolved the class => report (case must match) */
if (1 == namesToLookup.size()) {
final String fqn = namesToLookup.iterator().next();
final PhpIndex index = PhpIndex.getInstance(project);
final List<PhpClass> classes = OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(fqn, index);
/* check resolved items */
if (!classes.isEmpty()) {
if (1 == classes.size() && classes.get(0).getFQN().equals(fqn)) {
if (parent instanceof BinaryExpression) {
normalizedContents = expression.getContents().replaceAll("\\\\\\\\", "\\\\").replaceAll("^\\\\", "");
holder.registerProblem(parent, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%c%", normalizedContents)), new TheLocalFix(normalizedContents, IMPORT_CLASSES_ON_QF, USE_RELATIVE_QF));
} else {
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(messagePattern.replace("%c%", normalizedContents)), new TheLocalFix(normalizedContents, IMPORT_CLASSES_ON_QF, USE_RELATIVE_QF));
}
}
classes.clear();
}
}
namesToLookup.clear();
}
}
@NotNull
private String populateLiteralContent(@NotNull StringLiteralExpression literal, boolean prependNamespace) {
String content = literal.getContents();
if (prependNamespace) {
final PsiElement clazz = PsiTreeUtil.findFirstParent(literal, PARENT_CLASS);
if (clazz != null) {
content = ((PhpClass) clazz).getNamespaceName() + content.replaceAll("^\\\\", "");
}
}
return content;
}
private boolean isClassAlias(@NotNull StringLiteralExpression literal) {
boolean result = false;
final PsiElement parent = literal.getParent();
if (parent instanceof ParameterList) {
final PsiElement grandParent = parent.getParent();
if (OpenapiTypesUtil.isFunctionReference(grandParent)) {
final FunctionReference reference = (FunctionReference) grandParent;
final String functionName = reference.getName();
if (functionName != null && functionName.equals("class_alias")) {
final PsiElement[] arguments = reference.getParameters();
result = arguments.length == 2 && arguments[1] == literal;
}
}
}
return result;
}
};
}
Aggregations