use of com.intellij.psi.PsiElementVisitor in project Perl5-IDEA by Camelcade.
the class Perl5RegexpInjector method getLanguagesToInject.
@Override
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
assert context instanceof PsiPerlPerlRegexImpl : "Got " + context;
if (!((PsiPerlPerlRegexImpl) context).isValidHost() || context.getTextLength() == 0) {
return;
}
int[] sourceOffset = new int[] { 0 };
boolean[] hasStarted = new boolean[] { false };
context.acceptChildren(new PsiElementVisitor() {
@Override
public void visitElement(PsiElement element) {
if (PsiUtilCore.getElementType(element) == REGEX_TOKEN) {
if (!hasStarted[0]) {
registrar.startInjecting(Perl5RegexpLanguage.INSTANCE);
hasStarted[0] = true;
}
registrar.addPlace(null, null, (PsiPerlPerlRegexImpl) context, TextRange.from(sourceOffset[0], element.getTextLength()));
}
sourceOffset[0] += element.getTextLength();
}
});
if (hasStarted[0]) {
registrar.doneInjecting();
}
}
use of com.intellij.psi.PsiElementVisitor in project Perl5-IDEA by Camelcade.
the class PerlParsingPerformanceTest method analyzeFile.
private void analyzeFile(PsiFile psiFile) {
final Map<IElementType, Integer> tokensMap = new THashMap<>();
final int[] totalTokens = new int[] { 0 };
psiFile.accept(new PsiElementVisitor() {
@Override
public void visitElement(PsiElement element) {
IElementType elementType = PsiUtilCore.getElementType(element);
if (TERMINAL_TOKENS.contains(elementType)) {
Integer count = tokensMap.get(elementType);
tokensMap.put(elementType, count == null ? 1 : count + 1);
totalTokens[0]++;
}
element.acceptChildren(this);
super.visitElement(element);
}
});
List<Map.Entry<IElementType, Integer>> entries = new ArrayList<>(tokensMap.entrySet());
Collections.sort(entries, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));
for (Map.Entry<IElementType, Integer> entry : entries) {
Integer elementCount = entry.getValue();
float percent = (float) elementCount / totalTokens[0];
System.err.println(entry.getKey() + ": " + elementCount + " " + percent * 100 + "%");
}
}
use of com.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.
the class IsEmptyFunctionUsageInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpEmpty(@NotNull PhpEmpty emptyExpression) {
final PhpExpression[] values = emptyExpression.getVariables();
if (values.length == 1) {
final PsiElement subject = ExpressionSemanticUtil.getExpressionTroughParenthesis(values[0]);
if (subject == null || subject instanceof ArrayAccessExpression) {
/* currently, php docs lacks of array structure notations, skip it */
return;
}
final PsiElement parent = emptyExpression.getParent();
final PsiElement operation = parent instanceof UnaryExpression ? ((UnaryExpression) parent).getOperation() : null;
final boolean isInverted = OpenapiTypesUtil.is(operation, PhpTokenTypes.opNOT);
/* extract types */
final Set<String> resolvedTypes = new HashSet<>();
if (subject instanceof PhpTypedElement) {
final PhpType resolved = OpenapiResolveUtil.resolveType((PhpTypedElement) subject, holder.getProject());
if (resolved != null) {
resolved.filterUnknown().getTypes().forEach(t -> resolvedTypes.add(Types.getType(t)));
}
}
/* Case 1: empty(array) - hidden logic - empty array */
if (SUGGEST_TO_USE_COUNT_CHECK && this.isCountableType(resolvedTypes)) {
final String comparison = isInverted ? "!==" : "===";
final String replacement = ComparisonStyle.isRegular() ? String.format("count(%s) %s 0", subject.getText(), comparison) : String.format("0 %s count(%s)", comparison, subject.getText());
final PsiElement target = isInverted ? parent : emptyExpression;
holder.registerProblem(target, String.format(MessagesPresentationUtil.prefixWithEa(patternAlternative), replacement), new UseCountFix(replacement));
resolvedTypes.clear();
return;
}
/* case 2: nullable classes, nullable target core types */
if (SUGGEST_TO_USE_NULL_COMPARISON && ((SUGGEST_NULL_COMPARISON_FOR_SCALARS && this.isNullableCoreType(resolvedTypes)) || TypesSemanticsUtil.isNullableObjectInterface(resolvedTypes))) {
/* false-positive: a field reference used in the subject expression */
PsiElement base = subject;
while (base instanceof PhpPsiElement) {
if (base instanceof FieldReference) {
break;
}
base = ((PhpPsiElement) base).getFirstPsiChild();
}
if (!(base instanceof FieldReference)) {
final String comparison = isInverted ? "!==" : "===";
final String replacement = ComparisonStyle.isRegular() ? String.format("%s %s null", subject.getText(), comparison) : String.format("null %s %s", comparison, subject.getText());
holder.registerProblem(isInverted ? parent : emptyExpression, String.format(MessagesPresentationUtil.prefixWithEa(patternAlternative), replacement), new CompareToNullFix(replacement));
}
resolvedTypes.clear();
return;
}
resolvedTypes.clear();
}
if (REPORT_EMPTY_USAGE) {
holder.registerProblem(emptyExpression, MessagesPresentationUtil.prefixWithEa(messageDoNotUse));
}
}
private boolean isCountableType(@NotNull Set<String> resolvedTypesSet) {
if (!resolvedTypesSet.isEmpty()) {
return resolvedTypesSet.stream().allMatch(t -> {
boolean isIterable = false;
if (t.equals(Types.strArray)) {
isIterable = true;
} else if (t.startsWith("\\")) {
final List<PhpClass> resolved = OpenapiResolveUtil.resolveClassesAndInterfacesByFQN(t, PhpIndex.getInstance(holder.getProject()));
isIterable = resolved.stream().anyMatch(r -> InterfacesExtractUtil.getCrawlInheritanceTree(r, true).stream().anyMatch(c -> c.getFQN().equals("\\Countable")));
}
return isIterable;
});
}
return false;
}
private boolean isNullableCoreType(@NotNull Set<String> resolvedTypesSet) {
boolean result = false;
if (resolvedTypesSet.size() == 2 && resolvedTypesSet.contains(Types.strNull)) {
result = resolvedTypesSet.contains(Types.strInteger) || resolvedTypesSet.contains(Types.strFloat) || resolvedTypesSet.contains(Types.strBoolean) || resolvedTypesSet.contains(Types.strResource);
}
return result;
}
};
}
use of com.intellij.psi.PsiElementVisitor 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.intellij.psi.PsiElementVisitor in project phpinspectionsea by kalessil.
the class PropertyInitializationFlawsInspector method buildVisitor.
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new BasePhpElementVisitor() {
@Override
public void visitPhpField(@NotNull Field field) {
if (REPORT_DEFAULTS_FLAWS && !field.isConstant()) {
final PhpClass clazz = field.getContainingClass();
final PhpClass parentClazz = clazz == null ? null : OpenapiResolveUtil.resolveSuperClass(clazz);
final Field originField = parentClazz == null ? null : OpenapiResolveUtil.resolveField(parentClazz, field.getName());
final PsiElement fieldDefault = OpenapiResolveUtil.resolveDefaultValue(field);
final PsiElement originDefault = originField == null ? null : OpenapiResolveUtil.resolveDefaultValue(originField);
if (PhpLanguageUtil.isNull(fieldDefault)) {
/* false-positives: typed properties PS will take care of them */
if (!this.isNullableTypedProperty(field)) {
holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageDefaultNull), ProblemHighlightType.LIKE_UNUSED_SYMBOL, new DropFieldDefaultValueFix());
}
} else if (fieldDefault instanceof PhpPsiElement && originDefault instanceof PhpPsiElement) {
final boolean isDefaultDuplicate = !originField.getModifier().getAccess().isPrivate() && OpenapiEquivalenceUtil.areEqual(fieldDefault, originDefault);
if (isDefaultDuplicate) {
boolean report = true;
/* false-positives: classes reference are the same, but resolved to different classes */
final Set<String> originalClasses = this.findReferencedClasses(originDefault);
if (!originalClasses.isEmpty()) {
final Set<String> fieldClasses = this.findReferencedClasses(fieldDefault);
report = !originalClasses.addAll(fieldClasses);
fieldClasses.clear();
originalClasses.clear();
}
if (report) {
holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageSenselessWrite), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
}
}
}
}
private boolean isNullableTypedProperty(@Nullable Field field) {
if (field != null && PhpLanguageLevel.get(holder.getProject()).atLeast(PhpLanguageLevel.PHP740)) {
final PhpType resolved = OpenapiResolveUtil.resolveDeclaredType(field);
return !resolved.isEmpty() && resolved.getTypes().stream().map(Types::getType).anyMatch(t -> t.equals(Types.strNull) || t.equals(Types.strMixed));
}
return false;
}
@NotNull
private Set<String> findReferencedClasses(@NotNull PsiElement where) {
return PsiTreeUtil.findChildrenOfType(where, ClassReference.class).stream().map(r -> {
final PsiElement resolved = OpenapiResolveUtil.resolveReference(r);
return resolved instanceof PhpClass ? ((PhpClass) resolved).getFQN() : null;
}).collect(Collectors.toSet());
}
@Override
public void visitPhpMethod(@NotNull Method method) {
/* configuration-based toggle */
if (!REPORT_INIT_FLAWS) {
return;
}
/* process only constructors with non-empty body */
final PhpClass clazz = method.getContainingClass();
if (null == clazz || !method.getName().equals("__construct") || clazz.isInterface() || clazz.isTrait()) {
return;
}
final GroupStatement body = ExpressionSemanticUtil.getGroupStatement(method);
if (null == body || 0 == ExpressionSemanticUtil.countExpressionsInGroup(body)) {
return;
}
/* collect private properties with default values; stop inspection if none found */
/* protected/public properties init in __construct can be bypassed, so defaults might have sense */
final Map<String, PsiElement> propertiesToCheck = new HashMap<>();
for (final Field field : clazz.getOwnFields()) {
if (!field.isConstant()) {
final PhpModifier modifiers = field.getModifier();
if (modifiers.isPrivate() && !modifiers.isStatic()) {
final PsiElement defaultValue = OpenapiResolveUtil.resolveDefaultValue(field);
if (defaultValue instanceof PhpPsiElement && !PhpLanguageUtil.isNull(defaultValue)) {
propertiesToCheck.put(field.getName(), defaultValue);
} else {
propertiesToCheck.put(field.getName(), null);
}
}
}
}
if (propertiesToCheck.isEmpty()) {
return;
}
/* iterate 1st level instructions and analyze overriding properties */
for (final PsiElement expression : body.getChildren()) {
final PsiElement assignmentCandidate = expression.getFirstChild();
if (!OpenapiTypesUtil.isAssignment(assignmentCandidate)) {
continue;
}
final AssignmentExpression assignment = (AssignmentExpression) assignmentCandidate;
final PsiElement container = assignment.getVariable();
final PsiElement value = assignment.getValue();
if (container instanceof FieldReference && container.getFirstChild().getText().equals("$this")) {
final String overriddenProperty = ((FieldReference) container).getName();
if (null == value || null == overriddenProperty || !propertiesToCheck.containsKey(overriddenProperty)) {
continue;
}
final PsiElement fieldDefault = propertiesToCheck.get(overriddenProperty);
/* Pattern: written and default values are identical */
if ((null == fieldDefault && PhpLanguageUtil.isNull(value)) || (null != fieldDefault && OpenapiEquivalenceUtil.areEqual(value, fieldDefault))) {
/* false-positives: typed properties */
if (!this.isNullableTypedProperty(OpenapiResolveUtil.resolveField(clazz, overriddenProperty))) {
holder.registerProblem(expression, MessagesPresentationUtil.prefixWithEa(messageSenselessWrite), ProblemHighlightType.LIKE_UNUSED_SYMBOL);
}
continue;
}
if (null == fieldDefault) {
continue;
}
/* false-positive: property is involved into generating new value */
boolean isPropertyReused = false;
for (final FieldReference candidate : PsiTreeUtil.findChildrenOfType(value, FieldReference.class)) {
if (OpenapiEquivalenceUtil.areEqual(container, candidate)) {
isPropertyReused = true;
break;
}
}
if (!isPropertyReused && REPORT_DEFAULTS_FLAWS) {
holder.registerProblem(fieldDefault, MessagesPresentationUtil.prefixWithEa(messageDefaultOverride), new DropFieldDefaultValueFix());
}
}
}
propertiesToCheck.clear();
}
};
}
Aggregations