blob: 487e0a4bc3e9cb0d48e66ee62d82d10ba47747d1 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.magicConstant;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInspection.*;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
import com.intellij.openapi.roots.JdkOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.slicer.*;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.containers.ConcurrentSoftValueHashMap;
import gnu.trove.THashSet;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class MagicConstantInspection extends BaseJavaLocalInspectionTool {
public static final Key<Boolean> NO_ANNOTATIONS_FOUND = Key.create("REPORTED_NO_ANNOTATIONS_FOUND");
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return GroupNames.BUGS_GROUP_NAME;
}
@Nls
@NotNull
@Override
public String getDisplayName() {
return "Magic Constant";
}
@NotNull
@Override
public String getShortName() {
return "MagicConstant";
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder,
boolean isOnTheFly,
@NotNull LocalInspectionToolSession session) {
return new JavaElementVisitor() {
@Override
public void visitJavaFile(PsiJavaFile file) {
checkAnnotationsJarAttached(file, holder);
}
@Override
public void visitCallExpression(PsiCallExpression callExpression) {
checkCall(callExpression, holder);
}
@Override
public void visitAssignmentExpression(PsiAssignmentExpression expression) {
PsiExpression r = expression.getRExpression();
if (r == null) return;
PsiExpression l = expression.getLExpression();
if (!(l instanceof PsiReferenceExpression)) return;
PsiElement resolved = ((PsiReferenceExpression)l).resolve();
if (!(resolved instanceof PsiModifierListOwner)) return;
PsiModifierListOwner owner = (PsiModifierListOwner)resolved;
PsiType type = expression.getType();
checkExpression(r, owner, type, holder);
}
@Override
public void visitReturnStatement(PsiReturnStatement statement) {
PsiExpression value = statement.getReturnValue();
if (value == null) return;
PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
if (method == null) return;
checkExpression(value, method, value.getType(), holder);
}
@Override
public void visitNameValuePair(PsiNameValuePair pair) {
PsiAnnotationMemberValue value = pair.getValue();
if (!(value instanceof PsiExpression)) return;
PsiReference ref = pair.getReference();
if (ref == null) return;
PsiMethod method = (PsiMethod)ref.resolve();
if (method == null) return;
checkExpression((PsiExpression)value, method, method.getReturnType(), holder);
}
@Override
public void visitBinaryExpression(PsiBinaryExpression expression) {
IElementType tokenType = expression.getOperationTokenType();
if (tokenType != JavaTokenType.EQEQ && tokenType != JavaTokenType.NE) return;
PsiExpression l = expression.getLOperand();
PsiExpression r = expression.getROperand();
if (r == null) return;
checkBinary(l, r);
checkBinary(r, l);
}
private void checkBinary(PsiExpression l, PsiExpression r) {
if (l instanceof PsiReference) {
PsiElement resolved = ((PsiReference)l).resolve();
if (resolved instanceof PsiModifierListOwner) {
checkExpression(r, (PsiModifierListOwner)resolved, getType((PsiModifierListOwner)resolved), holder);
}
}
else if (l instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression)l).resolveMethod();
if (method != null) {
checkExpression(r, method, method.getReturnType(), holder);
}
}
}
};
}
@Override
public void cleanup(Project project) {
super.cleanup(project);
project.putUserData(NO_ANNOTATIONS_FOUND, null);
}
private static void checkAnnotationsJarAttached(@NotNull PsiFile file, @NotNull ProblemsHolder holder) {
final Project project = file.getProject();
if (!holder.isOnTheFly()) {
final Boolean found = project.getUserData(NO_ANNOTATIONS_FOUND);
if (found != null) return;
}
PsiClass event = JavaPsiFacade.getInstance(project).findClass("java.awt.event.InputEvent", GlobalSearchScope.allScope(project));
if (event == null) return; // no jdk to attach
PsiMethod[] methods = event.findMethodsByName("getModifiers", false);
if (methods.length != 1) return; // no jdk to attach
PsiMethod getModifiers = methods[0];
PsiAnnotation annotation = ExternalAnnotationsManager.getInstance(project).findExternalAnnotation(getModifiers, MagicConstant.class.getName());
if (annotation != null) return;
final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(getModifiers);
if (virtualFile == null) return; // no jdk to attach
final List<OrderEntry> entries = ProjectRootManager.getInstance(project).getFileIndex().getOrderEntriesForFile(virtualFile);
Sdk jdk = null;
for (OrderEntry orderEntry : entries) {
if (orderEntry instanceof JdkOrderEntry) {
jdk = ((JdkOrderEntry)orderEntry).getJdk();
if (jdk != null) break;
}
}
if (jdk == null) return; // no jdk to attach
if (!holder.isOnTheFly()) {
project.putUserData(NO_ANNOTATIONS_FOUND, Boolean.TRUE);
}
final Sdk finalJdk = jdk;
String path = finalJdk.getHomePath();
String text = "No IDEA annotations attached to the JDK " + finalJdk.getName() + (path == null ? "" : " (" + FileUtil.toSystemDependentName(path) + ")")
+", some issues will not be found";
holder.registerProblem(file, text, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new LocalQuickFix() {
@NotNull
@Override
public String getName() {
return "Attach annotations";
}
@NotNull
@Override
public String getFamilyName() {
return getName();
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
SdkModificator modificator = finalJdk.getSdkModificator();
JavaSdkImpl.attachJdkAnnotations(modificator);
modificator.commitChanges();
}
});
}
});
}
private static void checkExpression(PsiExpression expression,
PsiModifierListOwner owner,
PsiType type,
ProblemsHolder holder) {
AllowedValues allowed = getAllowedValues(owner, type, null);
if (allowed == null) return;
PsiElement scope = PsiUtil.getTopLevelEnclosingCodeBlock(expression, null);
if (scope == null) scope = expression;
if (!isAllowed(scope, expression, allowed, expression.getManager(), null)) {
registerProblem(expression, allowed, holder);
}
}
private static void checkCall(@NotNull PsiCallExpression methodCall, @NotNull ProblemsHolder holder) {
PsiMethod method = methodCall.resolveMethod();
if (method == null) return;
PsiParameter[] parameters = method.getParameterList().getParameters();
PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
AllowedValues values = getAllowedValues(parameter, parameter.getType(), null);
if (values == null) continue;
if (i >= arguments.length) break;
PsiExpression argument = arguments[i];
argument = PsiUtil.deparenthesizeExpression(argument);
if (argument == null) continue;
checkMagicParameterArgument(parameter, argument, values, holder);
}
}
static class AllowedValues {
final PsiAnnotationMemberValue[] values;
final boolean canBeOred;
private AllowedValues(@NotNull PsiAnnotationMemberValue[] values, boolean canBeOred) {
this.values = values;
this.canBeOred = canBeOred;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AllowedValues a2 = (AllowedValues)o;
if (canBeOred != a2.canBeOred) {
return false;
}
Set<PsiAnnotationMemberValue> v1 = new THashSet<PsiAnnotationMemberValue>(Arrays.asList(values));
Set<PsiAnnotationMemberValue> v2 = new THashSet<PsiAnnotationMemberValue>(Arrays.asList(a2.values));
if (v1.size() != v2.size()) {
return false;
}
for (PsiAnnotationMemberValue value : v1) {
for (PsiAnnotationMemberValue value2 : v2) {
if (same(value, value2, value.getManager())) {
v2.remove(value2);
break;
}
}
}
return v2.isEmpty();
}
@Override
public int hashCode() {
int result = Arrays.hashCode(values);
result = 31 * result + (canBeOred ? 1 : 0);
return result;
}
public boolean isSubsetOf(@NotNull AllowedValues other, @NotNull PsiManager manager) {
for (PsiAnnotationMemberValue value : values) {
boolean found = false;
for (PsiAnnotationMemberValue otherValue : other.values) {
if (same(value, otherValue, manager)) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
}
private static AllowedValues getAllowedValuesFromMagic(@NotNull PsiModifierListOwner element,
@NotNull PsiType type,
@NotNull PsiAnnotation magic,
@NotNull PsiManager manager) {
PsiAnnotationMemberValue[] allowedValues;
final boolean canBeOred;
if (TypeConversionUtil.getTypeRank(type) <= TypeConversionUtil.LONG_RANK) {
PsiAnnotationMemberValue intValues = magic.findAttributeValue("intValues");
allowedValues = intValues instanceof PsiArrayInitializerMemberValue ? ((PsiArrayInitializerMemberValue)intValues).getInitializers() : PsiAnnotationMemberValue.EMPTY_ARRAY;
if (allowedValues.length == 0) {
PsiAnnotationMemberValue orValue = magic.findAttributeValue("flags");
allowedValues = orValue instanceof PsiArrayInitializerMemberValue ? ((PsiArrayInitializerMemberValue)orValue).getInitializers() : PsiAnnotationMemberValue.EMPTY_ARRAY;
canBeOred = true;
}
else {
canBeOred = false;
}
}
else if (type.equals(PsiType.getJavaLangString(manager, GlobalSearchScope.allScope(manager.getProject())))) {
PsiAnnotationMemberValue strValuesAttr = magic.findAttributeValue("stringValues");
allowedValues = strValuesAttr instanceof PsiArrayInitializerMemberValue ? ((PsiArrayInitializerMemberValue)strValuesAttr).getInitializers() : PsiAnnotationMemberValue.EMPTY_ARRAY;
canBeOred = false;
}
else {
return null; //other types not supported
}
if (allowedValues.length != 0) {
return new AllowedValues(allowedValues, canBeOred);
}
// last resort: try valuesFromClass
PsiAnnotationMemberValue[] values = readFromClass("valuesFromClass", magic, type, manager);
boolean ored = false;
if (values == null) {
values = readFromClass("flagsFromClass", magic, type, manager);
ored = true;
}
if (values == null) return null;
return new AllowedValues(values, ored);
}
private static PsiAnnotationMemberValue[] readFromClass(@NonNls @NotNull String attributeName,
@NotNull PsiAnnotation magic,
@NotNull PsiType type,
@NotNull PsiManager manager) {
PsiAnnotationMemberValue fromClassAttr = magic.findAttributeValue(attributeName);
PsiType fromClassType = fromClassAttr instanceof PsiClassObjectAccessExpression ? ((PsiClassObjectAccessExpression)fromClassAttr).getOperand().getType() : null;
PsiClass fromClass = fromClassType instanceof PsiClassType ? ((PsiClassType)fromClassType).resolve() : null;
if (fromClass == null) return null;
String fqn = fromClass.getQualifiedName();
if (fqn == null) return null;
List<PsiAnnotationMemberValue> constants = new ArrayList<PsiAnnotationMemberValue>();
for (PsiField field : fromClass.getFields()) {
if (!field.hasModifierProperty(PsiModifier.PUBLIC) || !field.hasModifierProperty(PsiModifier.STATIC) || !field.hasModifierProperty(PsiModifier.FINAL)) continue;
PsiType fieldType = field.getType();
if (!Comparing.equal(fieldType, type)) continue;
PsiAssignmentExpression e = (PsiAssignmentExpression)JavaPsiFacade.getElementFactory(manager.getProject()).createExpressionFromText("x="+fqn + "." + field.getName(), field);
PsiReferenceExpression refToField = (PsiReferenceExpression)e.getRExpression();
constants.add(refToField);
}
if (constants.isEmpty()) return null;
return constants.toArray(new PsiAnnotationMemberValue[constants.size()]);
}
static AllowedValues getAllowedValues(@NotNull PsiModifierListOwner element, PsiType type, Set<PsiClass> visited) {
PsiAnnotation[] annotations = AnnotationUtil.getAllAnnotations(element, true, null);
PsiManager manager = element.getManager();
for (PsiAnnotation annotation : annotations) {
AllowedValues values;
if (type != null && MagicConstant.class.getName().equals(annotation.getQualifiedName())) {
//PsiAnnotation magic = AnnotationUtil.findAnnotationInHierarchy(element, Collections.singleton(MagicConstant.class.getName()));
values = getAllowedValuesFromMagic(element, type, annotation, manager);
if (values != null) return values;
}
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
PsiElement resolved = ref == null ? null : ref.resolve();
if (!(resolved instanceof PsiClass) || !((PsiClass)resolved).isAnnotationType()) continue;
PsiClass aClass = (PsiClass)resolved;
if (visited == null) visited = new THashSet<PsiClass>();
if (!visited.add(aClass)) continue;
values = getAllowedValues(aClass, type, visited);
if (values != null) return values;
}
return parseBeanInfo(element, manager);
}
private static AllowedValues parseBeanInfo(@NotNull PsiModifierListOwner owner, @NotNull PsiManager manager) {
PsiMethod method = null;
if (owner instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter)owner;
PsiElement scope = parameter.getDeclarationScope();
if (!(scope instanceof PsiMethod)) return null;
PsiElement nav = scope.getNavigationElement();
if (!(nav instanceof PsiMethod)) return null;
method = (PsiMethod)nav;
if (method.isConstructor()) {
// not a property, try the @ConstructorProperties({"prop"})
PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, "java.beans.ConstructorProperties");
if (annotation == null) return null;
PsiAnnotationMemberValue value = annotation.findAttributeValue("value");
if (!(value instanceof PsiArrayInitializerMemberValue)) return null;
PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue)value).getInitializers();
PsiElement parent = parameter.getParent();
if (!(parent instanceof PsiParameterList)) return null;
int index = ((PsiParameterList)parent).getParameterIndex(parameter);
if (index >= initializers.length) return null;
PsiAnnotationMemberValue initializer = initializers[index];
if (!(initializer instanceof PsiLiteralExpression)) return null;
Object val = ((PsiLiteralExpression)initializer).getValue();
if (!(val instanceof String)) return null;
PsiMethod setter = PropertyUtil.findPropertySetter(method.getContainingClass(), (String)val, false, false);
if (setter == null) return null;
// try the @beaninfo of the corresponding setter
PsiElement navigationElement = setter.getNavigationElement();
if (!(navigationElement instanceof PsiMethod)) return null;
method = (PsiMethod)navigationElement;
}
}
else if (owner instanceof PsiMethod) {
PsiElement nav = owner.getNavigationElement();
if (!(nav instanceof PsiMethod)) return null;
method = (PsiMethod)nav;
}
if (method == null) return null;
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
if (PropertyUtil.isSimplePropertyGetter(method)) {
List<PsiMethod> setters = PropertyUtil.getSetters(aClass, PropertyUtil.getPropertyNameByGetter(method));
if (setters.size() != 1) return null;
method = setters.get(0);
}
if (!PropertyUtil.isSimplePropertySetter(method)) return null;
PsiDocComment doc = method.getDocComment();
if (doc == null) return null;
PsiDocTag beaninfo = doc.findTagByName("beaninfo");
if (beaninfo == null) return null;
String data = StringUtil.join(beaninfo.getDataElements(), new Function<PsiElement, String>() {
@Override
public String fun(PsiElement element) {
return element.getText();
}
}, "\n");
int enumIndex = StringUtil.indexOfSubstringEnd(data, "enum:");
if (enumIndex == -1) return null;
data = data.substring(enumIndex);
int colon = data.indexOf(":");
int last = colon == -1 ? data.length() : data.substring(0,colon).lastIndexOf("\n");
data = data.substring(0, last);
List<PsiAnnotationMemberValue> values = new ArrayList<PsiAnnotationMemberValue>();
for (String line : StringUtil.splitByLines(data)) {
List<String> words = StringUtil.split(line, " ", true, true);
if (words.size() != 2) continue;
String ref = words.get(1);
PsiExpression constRef = JavaPsiFacade.getElementFactory(manager.getProject()).createExpressionFromText(ref, aClass);
if (!(constRef instanceof PsiReferenceExpression)) continue;
PsiReferenceExpression expr = (PsiReferenceExpression)constRef;
values.add(expr);
}
if (values.isEmpty()) return null;
PsiAnnotationMemberValue[] array = values.toArray(new PsiAnnotationMemberValue[values.size()]);
return new AllowedValues(array, false);
}
private static PsiType getType(@NotNull PsiModifierListOwner element) {
return element instanceof PsiVariable ? ((PsiVariable)element).getType() : element instanceof PsiMethod ? ((PsiMethod)element).getReturnType() : null;
}
private static void checkMagicParameterArgument(@NotNull PsiParameter parameter,
@NotNull PsiExpression argument,
@NotNull AllowedValues allowedValues,
@NotNull ProblemsHolder holder) {
final PsiManager manager = PsiManager.getInstance(holder.getProject());
if (!argument.getTextRange().isEmpty() && !isAllowed(parameter.getDeclarationScope(), argument, allowedValues, manager, null)) {
registerProblem(argument, allowedValues, holder);
}
}
private static void registerProblem(@NotNull PsiExpression argument, @NotNull AllowedValues allowedValues, @NotNull ProblemsHolder holder) {
String values = StringUtil.join(allowedValues.values,
new Function<PsiAnnotationMemberValue, String>() {
@Override
public String fun(PsiAnnotationMemberValue value) {
if (value instanceof PsiReferenceExpression) {
PsiElement resolved = ((PsiReferenceExpression)value).resolve();
if (resolved instanceof PsiVariable) {
return PsiFormatUtil.formatVariable((PsiVariable)resolved, PsiFormatUtilBase.SHOW_NAME |
PsiFormatUtilBase.SHOW_CONTAINING_CLASS, PsiSubstitutor.EMPTY);
}
}
return value.getText();
}
}, ", ");
holder.registerProblem(argument, "Must be one of: "+ values);
}
private static boolean isAllowed(@NotNull final PsiElement scope,
@NotNull final PsiExpression argument,
@NotNull final AllowedValues allowedValues,
@NotNull final PsiManager manager,
final Set<PsiExpression> visited) {
if (isGoodExpression(argument, allowedValues, scope, manager, visited)) return true;
return processValuesFlownTo(argument, scope, manager, new Processor<PsiExpression>() {
@Override
public boolean process(PsiExpression expression) {
return isGoodExpression(expression, allowedValues, scope, manager, visited);
}
});
}
private static boolean isGoodExpression(@NotNull PsiExpression e,
@NotNull AllowedValues allowedValues,
@NotNull PsiElement scope,
@NotNull PsiManager manager,
@Nullable Set<PsiExpression> visited) {
PsiExpression expression = PsiUtil.deparenthesizeExpression(e);
if (expression == null) return true;
if (visited == null) visited = new THashSet<PsiExpression>();
if (!visited.add(expression)) return true;
if (expression instanceof PsiConditionalExpression) {
PsiExpression thenExpression = ((PsiConditionalExpression)expression).getThenExpression();
boolean thenAllowed = thenExpression == null || isAllowed(scope, thenExpression, allowedValues, manager, visited);
if (!thenAllowed) return false;
PsiExpression elseExpression = ((PsiConditionalExpression)expression).getElseExpression();
return elseExpression == null || isAllowed(scope, elseExpression, allowedValues, manager, visited);
}
if (isOneOf(expression, allowedValues, manager)) return true;
if (allowedValues.canBeOred) {
PsiExpression zero = getLiteralExpression(expression, manager, "0");
if (same(expression, zero, manager)) return true;
PsiExpression mOne = getLiteralExpression(expression, manager, "-1");
if (same(expression, mOne, manager)) return true;
if (expression instanceof PsiPolyadicExpression) {
IElementType tokenType = ((PsiPolyadicExpression)expression).getOperationTokenType();
if (JavaTokenType.OR.equals(tokenType) || JavaTokenType.AND.equals(tokenType) || JavaTokenType.PLUS.equals(tokenType)) {
for (PsiExpression operand : ((PsiPolyadicExpression)expression).getOperands()) {
if (!isAllowed(scope, operand, allowedValues, manager, visited)) return false;
}
return true;
}
}
if (expression instanceof PsiPrefixExpression &&
JavaTokenType.TILDE.equals(((PsiPrefixExpression)expression).getOperationTokenType())) {
PsiExpression operand = ((PsiPrefixExpression)expression).getOperand();
return operand == null || isAllowed(scope, operand, allowedValues, manager, visited);
}
}
PsiElement resolved = null;
if (expression instanceof PsiReference) {
resolved = ((PsiReference)expression).resolve();
}
else if (expression instanceof PsiCallExpression) {
resolved = ((PsiCallExpression)expression).resolveMethod();
}
AllowedValues allowedForRef;
if (resolved instanceof PsiModifierListOwner &&
(allowedForRef = getAllowedValues((PsiModifierListOwner)resolved, getType((PsiModifierListOwner)resolved), null)) != null &&
allowedForRef.isSubsetOf(allowedValues, manager)) return true;
return PsiType.NULL.equals(expression.getType());
}
private static final Key<Map<String, PsiExpression>> LITERAL_EXPRESSION_CACHE = Key.create("LITERAL_EXPRESSION_CACHE");
private static PsiExpression getLiteralExpression(@NotNull PsiExpression context, @NotNull PsiManager manager, @NotNull String text) {
Map<String, PsiExpression> cache = LITERAL_EXPRESSION_CACHE.get(manager);
if (cache == null) {
cache = new ConcurrentSoftValueHashMap<String, PsiExpression>();
cache = manager.putUserDataIfAbsent(LITERAL_EXPRESSION_CACHE, cache);
}
PsiExpression expression = cache.get(text);
if (expression == null) {
expression = JavaPsiFacade.getElementFactory(manager.getProject()).createExpressionFromText(text, context);
cache.put(text, expression);
}
return expression;
}
private static boolean isOneOf(@NotNull PsiExpression expression, @NotNull AllowedValues allowedValues, @NotNull PsiManager manager) {
for (PsiAnnotationMemberValue allowedValue : allowedValues.values) {
if (same(allowedValue, expression, manager)) return true;
}
return false;
}
private static boolean same(PsiElement e1, PsiElement e2, @NotNull PsiManager manager) {
if (e1 instanceof PsiLiteralExpression && e2 instanceof PsiLiteralExpression) {
return Comparing.equal(((PsiLiteralExpression)e1).getValue(), ((PsiLiteralExpression)e2).getValue());
}
if (e1 instanceof PsiPrefixExpression && e2 instanceof PsiPrefixExpression && ((PsiPrefixExpression)e1).getOperationTokenType() == ((PsiPrefixExpression)e2).getOperationTokenType()) {
return same(((PsiPrefixExpression)e1).getOperand(), ((PsiPrefixExpression)e2).getOperand(), manager);
}
if (e1 instanceof PsiReference && e2 instanceof PsiReference) {
e1 = ((PsiReference)e1).resolve();
e2 = ((PsiReference)e2).resolve();
}
return manager.areElementsEquivalent(e2, e1);
}
private static boolean processValuesFlownTo(@NotNull final PsiExpression argument,
@NotNull PsiElement scope,
@NotNull PsiManager manager,
@NotNull final Processor<PsiExpression> processor) {
SliceAnalysisParams params = new SliceAnalysisParams();
params.dataFlowToThis = true;
params.scope = new AnalysisScope(new LocalSearchScope(scope), manager.getProject());
SliceRootNode rootNode = new SliceRootNode(manager.getProject(), new DuplicateMap(), SliceUsage.createRootUsage(argument, params));
Collection<? extends AbstractTreeNode> children = rootNode.getChildren().iterator().next().getChildren();
for (AbstractTreeNode child : children) {
SliceUsage usage = (SliceUsage)child.getValue();
PsiElement element = usage.getElement();
if (element instanceof PsiExpression && !processor.process((PsiExpression)element)) return false;
}
return !children.isEmpty();
}
}