Don't resolve forward outgoing references in class except cython and pyi (PY-26368)

This commit is contained in:
Semyon Proshev
2017-10-25 19:04:00 +03:00
parent a2e13fee96
commit a41365069e
6 changed files with 80 additions and 30 deletions
@@ -1,18 +1,4 @@
/*
* 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.
*/
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.psi.resolve;
import com.intellij.openapi.extensions.ExtensionPointName;
@@ -34,4 +20,8 @@ public interface PyReferenceResolveProvider {
*/
@NotNull
List<RatedResolveResult> resolveName(@NotNull PyQualifiedExpression element, @NotNull TypeEvalContext context);
default boolean allowsForwardOutgoingReferencesInClass(@NotNull PyQualifiedExpression element) {
return false;
}
}
@@ -712,6 +712,7 @@
<typeProvider implementation="com.jetbrains.python.pyi.PyiTypeProvider"/>
<pyModuleMembersProvider implementation="com.jetbrains.python.pyi.PyiModuleMembersProvider"/>
<pyClassMembersProvider implementation="com.jetbrains.python.pyi.PyiClassMembersProvider"/>
<pyReferenceResolveProvider implementation="com.jetbrains.python.pyi.PyiReferenceResolveProvider"/>
<visitorFilter language="PythonStub" implementationClass="com.jetbrains.python.pyi.PyiVisitorFilter"/>
<inspectionExtension implementation="com.jetbrains.python.pyi.PyiInspectionExtension"/>
<inspectionExtension implementation="com.jetbrains.python.codeInsight.typing.PyTypingInspectionExtension"/>
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 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.
*/
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.psi.impl.references;
import com.google.common.collect.Lists;
@@ -297,6 +283,17 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
}
}
else if (referenceOwner != null) {
if (!allowsForwardOutgoingReferencesInClass(myElement)) {
final PyClass outermostNestedClass = outermostNestedClass(referenceOwner, resolvedOwner);
if (outermostNestedClass != null) {
final List<Instruction> instructions =
PyDefUseUtil.getLatestDefs(resolvedOwner, referencedName, outermostNestedClass, false, true);
return resolveToLatestDefs(instructions, outermostNestedClass, referencedName, typeEvalContext);
}
}
final Scope referenceScope = ControlFlowCache.getScope(referenceOwner);
if (referenceScope.containsDeclaration(referencedName)) {
unreachableLocalDeclaration = true;
@@ -360,6 +357,25 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
return true;
}
private static boolean allowsForwardOutgoingReferencesInClass(@NotNull PyQualifiedExpression element) {
return ContainerUtil.exists(Extensions.getExtensions(PyReferenceResolveProvider.EP_NAME),
provider -> provider.allowsForwardOutgoingReferencesInClass(element));
}
@Nullable
private static PyClass outermostNestedClass(@NotNull ScopeOwner referenceOwner, @NotNull ScopeOwner resolvedOwner) {
PyClass current = PyUtil.as(referenceOwner, PyClass.class);
ScopeOwner outer = ScopeUtil.getScopeOwner(current);
while (outer != resolvedOwner) {
current = PyUtil.as(outer, PyClass.class);
if (current == null) return null;
outer = ScopeUtil.getScopeOwner(outer);
}
return current;
}
@NotNull
private ResolveResultList resolveByOverridingReferenceResolveProviders() {
final ResolveResultList results = new ResolveResultList();
@@ -0,0 +1,16 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.pyi
import com.jetbrains.python.psi.PyQualifiedExpression
import com.jetbrains.python.psi.resolve.PyReferenceResolveProvider
import com.jetbrains.python.psi.resolve.RatedResolveResult
import com.jetbrains.python.psi.types.TypeEvalContext
class PyiReferenceResolveProvider: PyReferenceResolveProvider {
override fun resolveName(element: PyQualifiedExpression, context: TypeEvalContext): List<RatedResolveResult> = emptyList()
override fun allowsForwardOutgoingReferencesInClass(element: PyQualifiedExpression): Boolean {
return PyiUtil.isInsideStubAnnotation(element)
}
}
@@ -0,0 +1,22 @@
class A1:
foo = <error descr="Unresolved reference 'B1'">B1</error>()
class B1:
pass
class A21:
class A22:
bar = <error descr="Unresolved reference 'B2'">B2</error>()
class B2:
pass
class A31:
def baz(self):
class A32:
egg = B3()
class B3:
pass
@@ -643,6 +643,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
doTest();
}
// PY-26368
public void testForwardReferencesInClassBody() {
doTest();
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {