mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-30 02:09:59 +07:00
[python] Make PyResolveUtil.resolveQualifiedNameInScope automatically traverse scopes
(cherry picked from commit 6013fdabf6967a193a60849ec0e81e153380b4b7) IJ-CR-151380 GitOrigin-RevId: 7f238ba2bee90f15dbe9c76cbf4e089a48f52870
This commit is contained in:
committed by
intellij-monorepo-bot
parent
651f6c3310
commit
66f10b6356
@@ -566,7 +566,7 @@ private fun resolveDataclassParameters(
|
||||
|
||||
if (dataclassTransformStub != null) {
|
||||
val resolvedFieldSpecifiers = dataclassTransformStub.fieldSpecifiers
|
||||
.flatMap { PyResolveUtil.resolveQualifiedNameInScope(it, dataclassTransformDecorator.containingFile as ScopeOwner, context) }
|
||||
.flatMap { PyResolveUtil.resolveQualifiedNameInScope(it, ScopeUtil.getScopeOwner(dataclassTransformDecorator)!!, context) }
|
||||
.filterIsInstance<PyQualifiedNameOwner>()
|
||||
.mapNotNull { it.qualifiedName }
|
||||
.map { QualifiedName.fromDottedString(it) }
|
||||
|
||||
@@ -43,12 +43,7 @@ class PyFunctoolsWrapsDecoratedFunctionTypeProvider : PyTypeProviderBase() {
|
||||
if (it == null) return@overStub emptyList<PsiElement>()
|
||||
var scopeOwner = ScopeUtil.getScopeOwner(decorator)
|
||||
val wrappedQName = QualifiedName.fromDottedString(it.wrapped)
|
||||
val resolved = mutableListOf<PsiElement>()
|
||||
while (scopeOwner != null) {
|
||||
resolved.addAll(PyResolveUtil.resolveQualifiedNameInScope(wrappedQName, scopeOwner, context))
|
||||
scopeOwner = ScopeUtil.getScopeOwner(scopeOwner)
|
||||
}
|
||||
resolved
|
||||
PyResolveUtil.resolveQualifiedNameInScope(wrappedQName, scopeOwner!!, context)
|
||||
}
|
||||
.overAst {
|
||||
val wrappedExpr = it.argumentList?.getValueExpressionForParam(PyKnownDecoratorUtil.FunctoolsWrapsParameters.WRAPPED)
|
||||
|
||||
@@ -2125,12 +2125,7 @@ public final class PyTypingTypeProvider extends PyTypeProviderWithCustomContext<
|
||||
}
|
||||
|
||||
if (scopeOwner != null && qualifiedName != null) {
|
||||
List<PsiElement> results = new ArrayList<>();
|
||||
while (scopeOwner != null) {
|
||||
results.addAll(PyResolveUtil.resolveQualifiedNameInScope(qualifiedName, scopeOwner, context));
|
||||
scopeOwner = ScopeUtil.getScopeOwner(scopeOwner);
|
||||
}
|
||||
return results;
|
||||
return PyResolveUtil.resolveQualifiedNameInScope(qualifiedName, scopeOwner, context);
|
||||
}
|
||||
return Collections.singletonList(expression);
|
||||
}
|
||||
|
||||
@@ -240,8 +240,10 @@ public final class PyResolveUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a symbol by its qualified name, starting from the specified scope and then following the chain of type members.
|
||||
* This type of resolve is stub-safe, i.e. it's not supposed to cause any un-stubbing of external files unless it explicitly
|
||||
* Resolve a symbol by its qualified name, climbing up from the specified scope until the first component
|
||||
* of the qualified is resolved and then following the chain of type members.
|
||||
* <p>
|
||||
* This type of resolve is stub-safe, i.e. it's not supposed to cause any un-stubbing of external files unless it is explicitly
|
||||
* allowed by the given type evaluation context.
|
||||
*
|
||||
* @param qualifiedName name of a symbol to resolve
|
||||
@@ -266,46 +268,21 @@ public final class PyResolveUtil {
|
||||
|
||||
final PyResolveContext resolveContext = PyResolveContext.defaultContext(context);
|
||||
|
||||
final List<? extends RatedResolveResult> unqualifiedResults;
|
||||
if (scopeOwner instanceof PyiFile fileScope) {
|
||||
// pyi-stubs are special cased because
|
||||
// `resolveMember` delegates to `multiResolveName(..., true)` and
|
||||
// it skips elements that are imported without `as`
|
||||
unqualifiedResults = fileScope.multiResolveName(firstName, false);
|
||||
List<RatedResolveResult> unqualifiedResults = new ArrayList<>();
|
||||
ScopeOwner curScope = scopeOwner;
|
||||
// Ideally, we should do `while (curScope != null && unqualifiedResults.isEmpty())` but
|
||||
// because of flow insensitivity it will break down for cases like the following in
|
||||
// flask_sqlalchemy/__init__.pyi:
|
||||
//
|
||||
// from .model import DefaultMeta as DefaultMeta, Model as Model
|
||||
//
|
||||
// class SQLAlchemy:
|
||||
// Model: Model # Model in the type hints resolves to its own target expression
|
||||
while (curScope != null) {
|
||||
unqualifiedResults.addAll(resolveShortNameInSingleScope(curScope, firstName, resolveContext));
|
||||
curScope = ScopeUtil.getScopeOwner(curScope);
|
||||
}
|
||||
else if (scopeOwner instanceof PyFunction functionScope) {
|
||||
final Stream<PsiNamedElement> targets = StreamEx
|
||||
.of(PsiTreeUtil.getStubChildrenOfTypeAsList(scopeOwner, PyTargetExpression.class))
|
||||
.filter(it -> !it.isQualified())
|
||||
.select(PsiNamedElement.class);
|
||||
|
||||
final Stream<PsiNamedElement> parameters = StreamEx
|
||||
.of(functionScope.getParameterList().getParameters())
|
||||
.select(PsiNamedElement.class);
|
||||
|
||||
unqualifiedResults = StreamEx
|
||||
.of(targets)
|
||||
.append(parameters)
|
||||
.filter(it -> firstName.equals(it.getName()))
|
||||
.map(it -> new RatedResolveResult(RatedResolveResult.RATE_NORMAL, it))
|
||||
.append(resolveTypeParameters(functionScope, firstName))
|
||||
.toList();
|
||||
}
|
||||
else if (scopeOwner instanceof PyTypeAliasStatement) {
|
||||
unqualifiedResults = resolveTypeParameters((PyTypeParameterListOwner)scopeOwner, firstName);
|
||||
}
|
||||
else {
|
||||
final PyType scopeType = context.getType((PyTypedElement)scopeOwner);
|
||||
if (scopeType == null) return Collections.emptyList();
|
||||
List<? extends RatedResolveResult> typeMembers = scopeType.resolveMember(firstName, null, AccessDirection.READ, resolveContext);
|
||||
if (scopeOwner instanceof PyClass pyClass) {
|
||||
unqualifiedResults = ContainerUtil.concat(ContainerUtil.notNullize(typeMembers), resolveTypeParameters(pyClass, firstName));
|
||||
}
|
||||
else {
|
||||
unqualifiedResults = typeMembers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final StreamEx<RatedResolveResult> initialResults;
|
||||
if (ContainerUtil.isEmpty(unqualifiedResults)) {
|
||||
final PsiElement builtin = PyBuiltinCache.getInstance(scopeOwner).getByName(firstName);
|
||||
@@ -335,6 +312,49 @@ public final class PyResolveUtil {
|
||||
return Collections.unmodifiableList(PyUtil.filterTopPriorityResults(result.toArray(RatedResolveResult[]::new)));
|
||||
}
|
||||
|
||||
private static @NotNull List<? extends RatedResolveResult> resolveShortNameInSingleScope(@NotNull ScopeOwner scopeOwner,
|
||||
@NotNull String name,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
if (scopeOwner instanceof PyiFile fileScope) {
|
||||
// pyi-stubs are special cased because
|
||||
// `resolveMember` delegates to `multiResolveName(..., true)` and
|
||||
// it skips elements that are imported without `as`
|
||||
return fileScope.multiResolveName(name, false);
|
||||
}
|
||||
else if (scopeOwner instanceof PyFunction functionScope) {
|
||||
final Stream<PsiNamedElement> targets = StreamEx
|
||||
.of(PsiTreeUtil.getStubChildrenOfTypeAsList(scopeOwner, PyTargetExpression.class))
|
||||
.filter(it -> !it.isQualified())
|
||||
.select(PsiNamedElement.class);
|
||||
|
||||
final Stream<PsiNamedElement> parameters = StreamEx
|
||||
.of(functionScope.getParameterList().getParameters())
|
||||
.select(PsiNamedElement.class);
|
||||
|
||||
return StreamEx
|
||||
.of(targets)
|
||||
.append(parameters)
|
||||
.filter(it -> name.equals(it.getName()))
|
||||
.map(it -> new RatedResolveResult(RatedResolveResult.RATE_NORMAL, it))
|
||||
.append(resolveTypeParameters(functionScope, name))
|
||||
.toList();
|
||||
}
|
||||
else if (scopeOwner instanceof PyTypeAliasStatement) {
|
||||
return resolveTypeParameters((PyTypeParameterListOwner)scopeOwner, name);
|
||||
}
|
||||
else {
|
||||
final PyType scopeType = resolveContext.getTypeEvalContext().getType((PyTypedElement)scopeOwner);
|
||||
if (scopeType == null) return Collections.emptyList();
|
||||
List<? extends RatedResolveResult> typeMembers = scopeType.resolveMember(name, null, AccessDirection.READ, resolveContext);
|
||||
if (scopeOwner instanceof PyClass pyClass) {
|
||||
return ContainerUtil.concat(ContainerUtil.notNullize(typeMembers), resolveTypeParameters(pyClass, name));
|
||||
}
|
||||
else {
|
||||
return ContainerUtil.notNullize(typeMembers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String resolveStrArgument(@NotNull PyCallExpression callExpression, int index, @NotNull String keyword) {
|
||||
// SUPPORTED CASES:
|
||||
|
||||
Reference in New Issue
Block a user