mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 06:51:17 +07:00
Fix #PY-18115, #PY-18401 and rename problems
* Stop show unresolved reference warning if argument of format string is PyReferenceExpression * Fix exception caused by trying to rename not PsiNamedElement: register rename processor, that accepts all literal expressions, but do rename for string only and throw UnsupportedOperationException for others * Add resolve for packed dicts, lists and tuples
This commit is contained in:
@@ -452,6 +452,7 @@
|
||||
<renamePsiElementProcessor implementation="com.jetbrains.python.refactoring.rename.RenamePyClassProcessor" order="before pyvar"/>
|
||||
<renamePsiElementProcessor implementation="com.jetbrains.python.magicLiteral.PyMagicLiteralRenameProcessor" order="before pyvar"/>
|
||||
<renamePsiElementProcessor implementation="com.jetbrains.python.refactoring.rename.RenamePyFileProcessor" order="first"/>
|
||||
<renamePsiElementProcessor implementation="com.jetbrains.python.refactoring.rename.RenamePyLiteralExpressionProcessor"/>
|
||||
|
||||
<automaticRenamerFactory implementation="com.jetbrains.python.refactoring.rename.PyContainingFileRenamerFactory"/>
|
||||
<automaticRenamerFactory implementation="com.jetbrains.python.refactoring.rename.PyInheritorRenameFactory"/>
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiteralExpression> implements PsiReferenceEx{
|
||||
private final int myPosition;
|
||||
private final PyStringFormatParser.SubstitutionChunk myChunk;
|
||||
private boolean myIgnoreUnresolved = false;
|
||||
|
||||
public PySubstitutionChunkReference(@NotNull final PyStringLiteralExpression element,
|
||||
@NotNull final PyStringFormatParser.SubstitutionChunk chunk, final int position) {
|
||||
@@ -37,10 +38,11 @@ public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiter
|
||||
myChunk = chunk;
|
||||
myPosition = position;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public HighlightSeverity getUnresolvedHighlightSeverity(@NotNull final TypeEvalContext context) {
|
||||
return HighlightSeverity.INFORMATION;
|
||||
return HighlightSeverity.WARNING;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -62,31 +64,37 @@ public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiter
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
PsiElement result = resolveFormatString();
|
||||
if (result == null) {
|
||||
result = resolvePercentString();
|
||||
boolean isPercentString = myElement.getParent() instanceof PyBinaryExpression;
|
||||
if (isPercentString) {
|
||||
return resolvePercentString();
|
||||
}
|
||||
else {
|
||||
return resolveFormatString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement resolveFormatString() {
|
||||
final PyArgumentList argumentList = getArgumentList(getElement());
|
||||
if (argumentList != null) {
|
||||
if (argumentList != null && argumentList.getArguments().length > 0) {
|
||||
final PyExpression[] arguments = argumentList.getArguments();
|
||||
if (myChunk.getMappingKey() != null) {
|
||||
|
||||
boolean isStarArgument = arguments.length == 1 && arguments[0] instanceof PyStarArgument;
|
||||
if (isStarArgument) return getUnderStarExpression(arguments);
|
||||
|
||||
boolean isKeywordSubstitution = myChunk.getMappingKey() != null;
|
||||
if (isKeywordSubstitution) {
|
||||
return argumentList.getKeywordArgument(myChunk.getMappingKey());
|
||||
}
|
||||
else {
|
||||
final int position = myChunk.getPosition() == null ? myPosition : myChunk.getPosition();
|
||||
if (arguments.length == 1 && arguments[0] instanceof PyStarArgument) {
|
||||
return arguments[0];
|
||||
}
|
||||
else if (position < arguments.length) {
|
||||
return arguments[position];
|
||||
}
|
||||
if (position < arguments.length) return arguments[position];
|
||||
|
||||
if (arguments[0] instanceof PyBinaryExpression && ((PyBinaryExpression)arguments[0]).isOperator("+")) {
|
||||
return processNotNestedBinaryExpression((PyBinaryExpression)arguments[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -94,24 +102,27 @@ public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiter
|
||||
@Nullable
|
||||
private PsiElement resolvePercentString() {
|
||||
PsiElement result = null;
|
||||
|
||||
|
||||
final PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(getElement(), PyBinaryExpression.class);
|
||||
if (binaryExpression != null) {
|
||||
final PyExpression rightExpression = binaryExpression.getRightExpression();
|
||||
if (rightExpression instanceof PyParenthesizedExpression) {
|
||||
result = resolvePositional((PyParenthesizedExpression)rightExpression);
|
||||
}
|
||||
else if (rightExpression instanceof PyDictLiteralExpression) {
|
||||
result = resolveKeyword((PyDictLiteralExpression)rightExpression);
|
||||
}
|
||||
|
||||
boolean isKeyWordSubstitution = myChunk.getMappingKey() != null;
|
||||
result = isKeyWordSubstitution? resolveKeyword(rightExpression) : resolvePositional(rightExpression);
|
||||
}
|
||||
return result == null ? getElement() : result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement resolveKeyword(PyDictLiteralExpression rightExpression) {
|
||||
if (myChunk.getMappingKey() != null) {
|
||||
final PyKeyValueExpression[] keyValueExpressions = rightExpression.getElements();
|
||||
private PsiElement resolveKeyword(PyExpression pyExpression) {
|
||||
PyExpression expression = pyExpression;
|
||||
if (pyExpression instanceof PyParenthesizedExpression) {
|
||||
expression = getContainedExpression((PyParenthesizedExpression)pyExpression);
|
||||
}
|
||||
|
||||
myIgnoreUnresolved = expression instanceof PyReferenceExpression;
|
||||
if (expression instanceof PyDictLiteralExpression) {
|
||||
final PyKeyValueExpression[] keyValueExpressions = ((PyDictLiteralExpression)expression).getElements();
|
||||
for (PyKeyValueExpression keyValueExpression: keyValueExpressions) {
|
||||
final PyStringLiteralExpression key = (PyStringLiteralExpression)keyValueExpression.getKey();
|
||||
if (key.getStringValue().equals(myChunk.getMappingKey())) {
|
||||
@@ -119,22 +130,31 @@ public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement resolvePositional(PyParenthesizedExpression rightExpression) {
|
||||
private PsiElement resolvePositional(PyExpression expression) {
|
||||
PsiElement result = null;
|
||||
|
||||
final PyExpression containedExpression = getContainedExpression(rightExpression);
|
||||
if (containedExpression instanceof PyTupleExpression) {
|
||||
final PyExpression[] elements = ((PySequenceExpression)containedExpression).getElements();
|
||||
if (elements.length > myPosition) {
|
||||
result = elements[myPosition];
|
||||
if (expression instanceof PyParenthesizedExpression) {
|
||||
final PyExpression containedExpression = getContainedExpression((PyParenthesizedExpression)expression);
|
||||
|
||||
if (containedExpression instanceof PyTupleExpression) {
|
||||
final PyExpression[] elements = ((PySequenceExpression)containedExpression).getElements();
|
||||
if (elements.length > myPosition) {
|
||||
result = elements[myPosition];
|
||||
}
|
||||
}
|
||||
else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) {
|
||||
result = processNotNestedBinaryExpression((PyBinaryExpression)containedExpression);
|
||||
}
|
||||
else if (containedExpression instanceof PyReferenceExpression) {
|
||||
myIgnoreUnresolved = true;
|
||||
}
|
||||
}
|
||||
else if (containedExpression instanceof PyBinaryExpression && ((PyBinaryExpression)containedExpression).isOperator("+")) {
|
||||
result = processNotNestedBinaryExpression((PyBinaryExpression)containedExpression);
|
||||
else if (expression instanceof PyReferenceExpression) {
|
||||
myIgnoreUnresolved = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -154,10 +174,10 @@ public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiter
|
||||
if (right instanceof PyParenthesizedExpression) {
|
||||
PyExpression rightTuple = ((PyParenthesizedExpression)right).getContainedExpression();
|
||||
if (rightTuple instanceof PyTupleExpression) {
|
||||
PyExpression[] rigthTupleElements = ((PyTupleExpression)rightTuple).getElements();
|
||||
int rightLength = rigthTupleElements.length;
|
||||
PyExpression[] rightTupleElements = ((PyTupleExpression)rightTuple).getElements();
|
||||
int rightLength = rightTupleElements.length;
|
||||
if (leftTupleLength + rightLength > myPosition)
|
||||
return rigthTupleElements[myPosition - leftTupleLength];
|
||||
return rightTupleElements[myPosition - leftTupleLength];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,9 +200,57 @@ public class PySubstitutionChunkReference extends PsiReferenceBase<PyStringLiter
|
||||
return PsiTreeUtil.getNextSiblingOfType(pyReferenceExpression, PyArgumentList.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PyExpression getUnderStarExpression(@NotNull final PyExpression[] args) {
|
||||
if (args.length == 1 && args[0] instanceof PyStarArgument) {
|
||||
PyExpression pyExpression = PsiTreeUtil.getChildOfAnyType(args[0], PyDictLiteralExpression.class,
|
||||
PyParenthesizedExpression.class,
|
||||
PyListLiteralExpression.class );
|
||||
if (pyExpression != null) {
|
||||
boolean isKeywordSubstitution = myChunk.getMappingKey() != null;
|
||||
|
||||
if (isKeywordSubstitution && pyExpression instanceof PyDictLiteralExpression) {
|
||||
PyDictLiteralExpression dictLiteralExpression = (PyDictLiteralExpression)pyExpression;
|
||||
for (PyKeyValueExpression keyValueExpression : dictLiteralExpression.getElements()) {
|
||||
if (keyValueExpression.getKey() instanceof PyStringLiteralExpression) {
|
||||
PyStringLiteralExpression key = (PyStringLiteralExpression)keyValueExpression.getKey();
|
||||
if (key.getStringValue().equals(myChunk.getMappingKey())) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
int position = myChunk.getPosition() != null ? myChunk.getPosition() : myPosition;
|
||||
PyExpression[] elements = null;
|
||||
if (pyExpression instanceof PyListLiteralExpression) {
|
||||
elements = ((PyListLiteralExpression)pyExpression).getElements();
|
||||
}
|
||||
else if (pyExpression instanceof PyParenthesizedExpression) {
|
||||
PyExpression expression = getContainedExpression((PyParenthesizedExpression)pyExpression);
|
||||
if (expression instanceof PyTupleExpression) {
|
||||
elements = ((PyTupleExpression)expression).getElements();
|
||||
}
|
||||
}
|
||||
|
||||
if (elements != null && position < elements.length) {
|
||||
return elements[position];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (PsiTreeUtil.getChildOfType(args[0], PyReferenceExpression.class) != null) {
|
||||
myIgnoreUnresolved = true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
public boolean ignoreUnresolved() {
|
||||
return myIgnoreUnresolved;
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -42,6 +42,7 @@ import com.jetbrains.python.PyCustomType;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMember;
|
||||
import com.jetbrains.python.codeInsight.PySubstitutionChunkReference;
|
||||
import com.jetbrains.python.codeInsight.PyFunctionTypeCommentReferenceContributor;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
@@ -657,6 +658,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reference instanceof PySubstitutionChunkReference && ((PySubstitutionChunkReference)reference).ignoreUnresolved()) {
|
||||
return;
|
||||
}
|
||||
registerProblem(node, description, hl_type, null, rangeInElement, actions.toArray(new LocalQuickFix[actions.size()]));
|
||||
}
|
||||
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.jetbrains.python.refactoring.rename;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RenamePyLiteralExpressionProcessor extends RenamePyElementProcessor {
|
||||
private static final Class[] UNSUPPORTED = {PyNumericLiteralExpression.class, PyNoneLiteralExpression.class, PyBoolLiteralExpression.class};
|
||||
@Override
|
||||
public boolean canProcessElement(@NotNull PsiElement element) {
|
||||
return PsiTreeUtil.instanceOf(element, PyLiteralExpression.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renameElement(PsiElement element, String newName, UsageInfo[] usages, @Nullable RefactoringElementListener listener)
|
||||
throws IncorrectOperationException {
|
||||
if (PsiTreeUtil.instanceOf(element, UNSUPPORTED)) throw new IncorrectOperationException();
|
||||
((PyStringLiteralExpression)element).updateText("\"" + newName + "\"");
|
||||
for (UsageInfo usageInfo: usages) {
|
||||
PsiReference reference = usageInfo.getReference();
|
||||
if (reference == null) return;
|
||||
reference.handleElementRename("\"" + newName + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToSearchInComments(PsiElement element) {
|
||||
return PyCodeInsightSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToSearchInComments(PsiElement element, boolean enabled) {
|
||||
PyCodeInsightSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToSearchForTextOccurrences(PsiElement element) {
|
||||
return PyCodeInsightSettings.getInstance().RENAME_SEARCH_NON_CODE_FOR_VARIABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToSearchForTextOccurrences(PsiElement element, boolean enabled) {
|
||||
PyCodeInsightSettings.getInstance().RENAME_SEARCH_NON_CODE_FOR_VARIABLE = enabled;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement substituteElementToRename(PsiElement element, @Nullable Editor editor) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user