Skip to content

Commit

Permalink
Fix parameter ordering in synthetic init when overriding parameters.
Browse files Browse the repository at this point in the history
  • Loading branch information
jhominal committed Nov 7, 2016
1 parent 53f32ed commit 3d9b37d
Showing 1 changed file with 41 additions and 12 deletions.
53 changes: 41 additions & 12 deletions src/com/wishtack/pysynthetic/impl/SyntheticTypeProvider.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package com.wishtack.pysynthetic.impl;

import com.jetbrains.python.psi.PyCallable;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyParameter;
import com.jetbrains.python.psi.PyPossibleClassMember;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.*;
import com.wishtack.pysynthetic.SyntheticTypeInfo;
import com.wishtack.pysynthetic.SyntheticTypeInfoReader;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Created by Jean Hominal on 2016-11-05.
Expand All @@ -34,13 +31,45 @@ public PyType getCallableType(@NotNull PyCallable callable, @NotNull TypeEvalCon
SyntheticTypeInfo info = new SyntheticTypeInfoReader(pyClass).read();
if (info.hasSyntheticConstructor() && callable.equals(pyClass.findInitOrNew(false, null))) {
PyParameter[] originalParameters = callable.getParameterList().getParameters();
List<PyNamedParameter> syntheticParameters = info.getSyntheticInitParameters();

List<PyCallableParameter> allInitParameters =
Stream.concat(Arrays.stream(originalParameters), info.getSyntheticInitParameters().stream())
.map(PyCallableParameterImpl::new)
.collect(Collectors.toList());
// Get the index of the **kwargs arguments, so that synthetic parameters
// can be inserted before it.
int originalKeywordContainerIndex = originalParameters.length;
for (int i = 0; i < originalParameters.length; i++) {
PyNamedParameter namedParameter = originalParameters[i].getAsNamed();
if (namedParameter != null && namedParameter.isKeywordContainer()) {
originalKeywordContainerIndex = i;
break;
}
}

return new PyCallableTypeImpl(allInitParameters, context.getReturnType(callable));
// Collect argument names in order to avoid producing duplicates.
// Synthetic parameters lose to any parameter named in the original __init__.
// This does not prevent duplicates if the original definition contains them, but that
// does not really matter as the IDE will complain loudly in that case anyway.
HashSet<String> unavailableParameterNames = new HashSet<>(originalParameters.length);
for (PyParameter originalParameter : originalParameters) {
unavailableParameterNames.add(originalParameter.getName());
}

ArrayList<PyCallableParameter> allInitParameters = new ArrayList<>(originalParameters.length + syntheticParameters.size());

for (int i = 0; i < originalKeywordContainerIndex; i++) {
allInitParameters.add(new PyCallableParameterImpl(originalParameters[i]));
}

for (PyNamedParameter syntheticParameter : syntheticParameters) {
if (unavailableParameterNames.add(syntheticParameter.getName())) {
allInitParameters.add(new PyCallableParameterImpl(syntheticParameter));
}
}

for (int i = originalKeywordContainerIndex; i < originalParameters.length; i++) {
allInitParameters.add(new PyCallableParameterImpl(originalParameters[i]));
}

return new PyCallableTypeImpl(Collections.unmodifiableList(allInitParameters), context.getReturnType(callable));
}

return super.getCallableType(callable, context);
Expand Down

0 comments on commit 3d9b37d

Please sign in to comment.