diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java index a66b089f62c..054734e2c5b 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JavaDescriptorResolver.java @@ -189,6 +189,7 @@ public class JavaDescriptorResolver { TypeParameterDescriptor typeParameterDescriptor = TypeParameterDescriptor.createForFurtherModification( owner, Collections.emptyList(), // TODO + false, Variance.INVARIANT, typeParameter.getName(), typeParameter.getIndex() diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java index b3c49e458c4..695686b10b0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java @@ -21,10 +21,11 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement public static TypeParameterDescriptor createWithDefaultBound( @NotNull DeclarationDescriptor containingDeclaration, @NotNull List annotations, + boolean reified, @NotNull Variance variance, @NotNull String name, int index) { - TypeParameterDescriptor typeParameterDescriptor = createForFurtherModification(containingDeclaration, annotations, variance, name, index); + TypeParameterDescriptor typeParameterDescriptor = createForFurtherModification(containingDeclaration, annotations, reified, variance, name, index); typeParameterDescriptor.addUpperBound(JetStandardClasses.getDefaultBound()); return typeParameterDescriptor; } @@ -32,10 +33,11 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement public static TypeParameterDescriptor createForFurtherModification( @NotNull DeclarationDescriptor containingDeclaration, @NotNull List annotations, + boolean reified, @NotNull Variance variance, @NotNull String name, int index) { - return new TypeParameterDescriptor(containingDeclaration, annotations, variance, name, index); + return new TypeParameterDescriptor(containingDeclaration, annotations, reified, variance, name, index); } private final int index; @@ -47,9 +49,12 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement private final Set classObjectUpperBounds = Sets.newLinkedHashSet(); private JetType classObjectBoundsAsType; + private final boolean reified; + private TypeParameterDescriptor( @NotNull DeclarationDescriptor containingDeclaration, @NotNull List annotations, + boolean reified, @NotNull Variance variance, @NotNull String name, int index) { @@ -57,6 +62,7 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement this.index = index; this.variance = variance; this.upperBounds = Sets.newLinkedHashSet(); + this.reified = reified; // TODO: Should we actually pass the annotations on to the type constructor? this.typeConstructor = new TypeConstructorImpl( this, @@ -67,6 +73,10 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement upperBounds); } + public boolean isReified() { + return reified; + } + public Variance getVariance() { return variance; } @@ -162,6 +172,6 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement @NotNull public TypeParameterDescriptor copy(@NotNull DeclarationDescriptor newOwner) { - return new TypeParameterDescriptor(newOwner, Lists.newArrayList(getAnnotations()), variance, getName(), index); + return new TypeParameterDescriptor(newOwner, Lists.newArrayList(getAnnotations()), reified, variance, getName(), index); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClass.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClass.java index dc232e66306..c03bf3a471e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClass.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClass.java @@ -75,7 +75,7 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj @NotNull public List getAnonymousInitializers() { - JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + JetClassBody body = getBody(); if (body == null) return Collections.emptyList(); return body.getAnonymousInitializers(); @@ -90,15 +90,20 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME); } + @Override + public JetClassBody getBody() { + return (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + } + @Nullable public JetClassObject getClassObject() { - JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + JetClassBody body = getBody(); if (body == null) return null; return body.getClassObject(); } public List getProperties() { - JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + JetClassBody body = getBody(); if (body == null) return Collections.emptyList(); return body.getProperties(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java index 5b4e67347f7..ea4768639d1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassBody.java @@ -1,10 +1,13 @@ package org.jetbrains.jet.lang.psi; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; +import org.jetbrains.jet.lexer.JetTokens; import java.util.List; @@ -48,4 +51,9 @@ public class JetClassBody extends JetElement { public JetClassObject getClassObject() { return (JetClassObject) findChildByType(JetNodeTypes.CLASS_OBJECT); } + + public PsiElement getRBrace() { + final ASTNode[] children = getNode().getChildren(TokenSet.create(JetTokens.RBRACE)); + return children.length == 1 ? children[0].getPsi() : null; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassOrObject.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassOrObject.java index 07f0160cff6..efa701ec1ee 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassOrObject.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassOrObject.java @@ -32,4 +32,7 @@ public interface JetClassOrObject extends PsiElement { @Nullable JetObjectDeclarationName getNameAsDeclaration(); + + @Nullable + JetClassBody getBody(); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetObjectDeclaration.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetObjectDeclaration.java index 27bea8777bd..7c3d58de4c8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetObjectDeclaration.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetObjectDeclaration.java @@ -69,10 +69,15 @@ public class JetObjectDeclaration extends JetNamedDeclaration implements JetClas return true; } + @Override + public JetClassBody getBody() { + return (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + } + @Override @NotNull public List getDeclarations() { - JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + JetClassBody body = getBody(); if (body == null) return Collections.emptyList(); return body.getDeclarations(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java index ab9ad23aa7c..4c0dced694d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java @@ -54,7 +54,7 @@ public class JetPsiFactory { return createProperty(project, text); } - private static JetProperty createProperty(Project project, String text) { + public static JetProperty createProperty(Project project, String text) { return createDeclaration(project, text, JetProperty.class); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 112c798ab46..c5ded6ed64e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -50,6 +50,7 @@ public class ClassDescriptorResolver { TypeParameterDescriptor typeParameterDescriptor = TypeParameterDescriptor.createForFurtherModification( descriptor, annotationResolver.createAnnotationStubs(typeParameter.getModifierList()), + true, typeParameter.getVariance(), JetPsiUtil.safeName(typeParameter.getName()), index @@ -313,6 +314,7 @@ public class ClassDescriptorResolver { TypeParameterDescriptor typeParameterDescriptor = TypeParameterDescriptor.createForFurtherModification( containingDescriptor, annotationResolver.createAnnotationStubs(typeParameter.getModifierList()), + true, typeParameter.getVariance(), JetPsiUtil.safeName(typeParameter.getName()), index diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java index 6f52c4737ce..c59fd03c47e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java @@ -103,66 +103,10 @@ public class OverrideResolver { } // Check if everything that must be overridden, actually is - - // Everything from supertypes - Map implementedWithDelegationBy = Maps.newHashMap(); - Set inheritedFunctions = Sets.newLinkedHashSet(); - for (JetType supertype : classDescriptor.getSupertypes()) { - for (DeclarationDescriptor descriptor : supertype.getMemberScope().getAllDescriptors()) { - if (descriptor instanceof CallableMemberDescriptor) { - CallableMemberDescriptor memberDescriptor = (CallableMemberDescriptor) descriptor; - inheritedFunctions.add(memberDescriptor); - } - } - } - - // Only those actually inherited - Set filteredMembers = OverridingUtil.filterOverrides(inheritedFunctions); - - // Group members with "the same" signature - Multimap factoredMembers = CommonSuppliers.newLinkedHashSetHashSetMultimap(); - for (CallableMemberDescriptor one : filteredMembers) { - if (factoredMembers.values().contains(one)) continue; - for (CallableMemberDescriptor another : filteredMembers) { -// if (one == another) continue; - factoredMembers.put(one, one); - if (OverridingUtil.isOverridableBy(one, another).isSuccess() - || OverridingUtil.isOverridableBy(another, one).isSuccess()) { - factoredMembers.put(one, another); - } - } - } - // More than one implementation or no implementations at all Set abstractNoImpl = Sets.newLinkedHashSet(); Set manyImpl = Sets.newLinkedHashSet(); - for (CallableMemberDescriptor key : factoredMembers.keySet()) { - Collection mutuallyOverridable = factoredMembers.get(key); - - int implementationCount = 0; - for (CallableMemberDescriptor member : mutuallyOverridable) { - if (member.getModality() != Modality.ABSTRACT) { - implementationCount++; - } - } - - if (implementationCount == 0) { - abstractNoImpl.addAll(mutuallyOverridable); - } - else if (implementationCount > 1) { - manyImpl.addAll(mutuallyOverridable); - } - } - - // Members actually present (declared) in the class - Set actuallyOverridden = Sets.newHashSet(implementedWithDelegationBy.keySet()); - for (CallableMemberDescriptor member : classDescriptor.getCallableMembers()) { - actuallyOverridden.addAll(member.getOverriddenDescriptors()); - } - - // Those to be overridden that are actually not - abstractNoImpl.removeAll(actuallyOverridden); - manyImpl.removeAll(actuallyOverridden); + collectMissingImplementations(classDescriptor, abstractNoImpl, manyImpl); PsiElement nameIdentifier = klass; if (klass instanceof JetClass) { @@ -216,6 +160,70 @@ public class OverrideResolver { // } } + public static void collectMissingImplementations(MutableClassDescriptor classDescriptor, Set abstractNoImpl, Set manyImpl) { + // Everything from supertypes + Map implementedWithDelegationBy = Maps.newHashMap(); + Multimap factoredMembers = collectSuperMethods(classDescriptor); + + for (CallableMemberDescriptor key : factoredMembers.keySet()) { + Collection mutuallyOverridable = factoredMembers.get(key); + + int implementationCount = 0; + for (CallableMemberDescriptor member : mutuallyOverridable) { + if (member.getModality() != Modality.ABSTRACT) { + implementationCount++; + } + } + + if (implementationCount == 0) { + abstractNoImpl.addAll(mutuallyOverridable); + } + else if (implementationCount > 1) { + manyImpl.addAll(mutuallyOverridable); + } + } + + // Members actually present (declared) in the class + Set actuallyOverridden = Sets.newHashSet(implementedWithDelegationBy.keySet()); + for (CallableMemberDescriptor member : classDescriptor.getCallableMembers()) { + actuallyOverridden.addAll(member.getOverriddenDescriptors()); + } + + // Those to be overridden that are actually not + abstractNoImpl.removeAll(actuallyOverridden); + manyImpl.removeAll(actuallyOverridden); + } + + public static Multimap collectSuperMethods(MutableClassDescriptor classDescriptor) { + Set inheritedFunctions = Sets.newLinkedHashSet(); + for (JetType supertype : classDescriptor.getSupertypes()) { + for (DeclarationDescriptor descriptor : supertype.getMemberScope().getAllDescriptors()) { + if (descriptor instanceof CallableMemberDescriptor) { + CallableMemberDescriptor memberDescriptor = (CallableMemberDescriptor) descriptor; + inheritedFunctions.add(memberDescriptor); + } + } + } + + // Only those actually inherited + Set filteredMembers = OverridingUtil.filterOverrides(inheritedFunctions); + + // Group members with "the same" signature + Multimap factoredMembers = CommonSuppliers.newLinkedHashSetHashSetMultimap(); + for (CallableMemberDescriptor one : filteredMembers) { + if (factoredMembers.values().contains(one)) continue; + for (CallableMemberDescriptor another : filteredMembers) { +// if (one == another) continue; + factoredMembers.put(one, one); + if (OverridingUtil.isOverridableBy(one, another).isSuccess() + || OverridingUtil.isOverridableBy(another, one).isSuccess()) { + factoredMembers.put(one, another); + } + } + } + return factoredMembers; + } + private void checkOverride(CallableMemberDescriptor declared) { JetNamedDeclaration member = (JetNamedDeclaration) context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, declared); if (member == null) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java index 3d29ab80a1d..b31f101c402 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java @@ -40,6 +40,7 @@ public class DescriptorSubstitutor { TypeParameterDescriptor substituted = TypeParameterDescriptor.createForFurtherModification( newContainingDeclaration, descriptor.getAnnotations(), + descriptor.isReified(), descriptor.getVariance(), descriptor.getName(), descriptor.getIndex()); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index e637d05de72..c76c5c797ca 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -116,7 +116,7 @@ public class JetStandardClasses { parameters.add(TypeParameterDescriptor.createWithDefaultBound( classDescriptor, Collections.emptyList(), - Variance.OUT_VARIANCE, "T" + j, j)); + true, Variance.OUT_VARIANCE, "T" + j, j)); } TUPLE[i] = classDescriptor.initialize( true, @@ -159,7 +159,7 @@ public class JetStandardClasses { parameters.add(0, TypeParameterDescriptor.createWithDefaultBound( receiverFunction, Collections.emptyList(), - Variance.IN_VARIANCE, "T", 0)); + true, Variance.IN_VARIANCE, "T", 0)); RECEIVER_FUNCTION[i] = receiverFunction.initialize( false, parameters, @@ -174,12 +174,12 @@ public class JetStandardClasses { parameters.add(TypeParameterDescriptor.createWithDefaultBound( function, Collections.emptyList(), - Variance.IN_VARIANCE, "P" + j, j + 1)); + true, Variance.IN_VARIANCE, "P" + j, j + 1)); } parameters.add(TypeParameterDescriptor.createWithDefaultBound( function, Collections.emptyList(), - Variance.OUT_VARIANCE, "R", parameterCount + 1)); + true, Variance.OUT_VARIANCE, "R", parameterCount + 1)); return parameters; } diff --git a/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java b/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java index a9bddbb2c7a..b744988a594 100644 --- a/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java +++ b/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java @@ -311,6 +311,9 @@ public class DescriptorRenderer implements Renderer { } protected void renderTypeParameter(TypeParameterDescriptor descriptor, StringBuilder builder) { + if (!descriptor.isReified()) { + builder.append(renderKeyword("erased")).append(" "); + } renderName(descriptor, builder); if (!descriptor.getUpperBounds().isEmpty()) { JetType bound = descriptor.getUpperBounds().iterator().next(); diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 48949cbb3f0..a14c23ddbed 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -54,5 +54,7 @@ + + diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/DescriptorClassMember.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/DescriptorClassMember.java new file mode 100644 index 00000000000..f22e8db19d5 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/DescriptorClassMember.java @@ -0,0 +1,57 @@ +package org.jetbrains.jet.plugin.codeInsight; + +import com.intellij.codeInsight.generation.ClassMember; +import com.intellij.codeInsight.generation.MemberChooserObject; +import com.intellij.ui.SimpleColoredComponent; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.resolve.DescriptorRenderer; + +import javax.swing.*; + +/** + * @author yole + */ +public class DescriptorClassMember implements ClassMember { + private final DeclarationDescriptor myDescriptor; + + public DescriptorClassMember(DeclarationDescriptor descriptor) { + myDescriptor = descriptor; + } + + @Override + public MemberChooserObject getParentNodeDelegate() { + final DeclarationDescriptor parent = myDescriptor.getContainingDeclaration(); + return new DescriptorClassMember(parent); + } + + @Override + public void renderTreeNode(SimpleColoredComponent component, JTree tree) { + component.append(getText()); + } + + @Override + public String getText() { + return DescriptorRenderer.TEXT.render(myDescriptor); + } + + public DeclarationDescriptor getDescriptor() { + return myDescriptor; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DescriptorClassMember that = (DescriptorClassMember) o; + + if (myDescriptor != null ? !myDescriptor.equals(that.myDescriptor) : that.myDescriptor != null) return false; + + return true; + } + + @Override + public int hashCode() { + return myDescriptor != null ? myDescriptor.hashCode() : 0; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/ImplementMethodsHandler.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/ImplementMethodsHandler.java new file mode 100644 index 00000000000..abf648878ea --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/ImplementMethodsHandler.java @@ -0,0 +1,22 @@ +package org.jetbrains.jet.plugin.codeInsight; + +import com.google.common.collect.Sets; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.resolve.OverrideResolver; + +import java.util.Set; + +/** + * @author yole + */ +public class ImplementMethodsHandler extends OverrideImplementMethodsHandler { + protected Set collectMethodsToGenerate(MutableClassDescriptor descriptor) { + Set missingImplementations = Sets.newLinkedHashSet(); + OverrideResolver.collectMissingImplementations(descriptor, missingImplementations, missingImplementations); + return missingImplementations; + } + + protected String getChooserTitle() { + return "Implement Members"; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java new file mode 100644 index 00000000000..ddc4aa1dfe4 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideImplementMethodsHandler.java @@ -0,0 +1,183 @@ +package org.jetbrains.jet.plugin.codeInsight; + +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.ide.util.MemberChooser; +import com.intellij.lang.LanguageCodeInsightActionHandler; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * @author yole + */ +public abstract class OverrideImplementMethodsHandler implements LanguageCodeInsightActionHandler { + public static List membersFromDescriptors(Set missingImplementations) { + List members = new ArrayList(); + for (CallableMemberDescriptor memberDescriptor : missingImplementations) { + members.add(new DescriptorClassMember(memberDescriptor)); + } + return members; + } + + public Set collectMethodsToGenerate(JetClassOrObject classOrObject) { + BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) classOrObject.getContainingFile()); + final DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject); + if (descriptor instanceof MutableClassDescriptor) { + return collectMethodsToGenerate((MutableClassDescriptor) descriptor); + } + return Collections.emptySet(); + } + + protected abstract Set collectMethodsToGenerate(MutableClassDescriptor descriptor); + + public static void generateMethods(Project project, Editor editor, JetClassOrObject classOrObject, List selectedElements) { + final JetClassBody body = classOrObject.getBody(); + if (body == null) { + return; + } + for (DescriptorClassMember selectedElement : selectedElements) { + final DeclarationDescriptor descriptor = selectedElement.getDescriptor(); + if (descriptor instanceof FunctionDescriptor) { + JetElement target = overrideFunction(project, (FunctionDescriptor) descriptor); + body.addBefore(target, body.getRBrace()); + } + else if (descriptor instanceof PropertyDescriptor) { + JetElement target = overrideProperty(project, (PropertyDescriptor) descriptor); + body.addBefore(target, body.getRBrace()); + } + } + } + + private static JetElement overrideProperty(Project project, PropertyDescriptor descriptor) { + StringBuilder bodyBuilder = new StringBuilder("override "); + if (descriptor.isVar()) { + bodyBuilder.append("var "); + } + else { + bodyBuilder.append("val "); + } + bodyBuilder.append(descriptor.getName()).append(": ").append(descriptor.getOutType()); + String initializer = defaultInitializer(descriptor.getOutType(), JetStandardLibrary.getJetStandardLibrary(project)); + if (initializer != null) { + bodyBuilder.append("=").append(initializer); + } + else { + bodyBuilder.append("= ?"); + } + return JetPsiFactory.createProperty(project, bodyBuilder.toString()); + } + + private static JetElement overrideFunction(Project project, FunctionDescriptor descriptor) { + StringBuilder bodyBuilder = new StringBuilder("override fun "); + bodyBuilder.append(descriptor.getName()); + bodyBuilder.append("("); + boolean first = true; + for (ValueParameterDescriptor parameterDescriptor : descriptor.getValueParameters()) { + if (!first) { + bodyBuilder.append(","); + } + first = false; + bodyBuilder.append(parameterDescriptor.getName()); + bodyBuilder.append(": "); + bodyBuilder.append(parameterDescriptor.getOutType().toString()); + } + bodyBuilder.append(")"); + final JetType returnType = descriptor.getReturnType(); + final JetStandardLibrary stdlib = JetStandardLibrary.getJetStandardLibrary(project); + if (!returnType.equals(stdlib.getTuple0Type())) { + bodyBuilder.append(": ").append(returnType.toString()); + } + bodyBuilder.append("{"); + final String initializer = defaultInitializer(returnType, stdlib); + if (initializer != null) { + bodyBuilder.append("return " ).append(initializer); + } + bodyBuilder.append("}"); + return JetPsiFactory.createFunction(project, bodyBuilder.toString()); + } + + private static String defaultInitializer(JetType returnType, JetStandardLibrary stdlib) { + if (returnType.isNullable()) { + return "null"; + } + else if (returnType.equals(stdlib.getIntType()) || returnType.equals(stdlib.getLongType()) || + returnType.equals(stdlib.getShortType()) || returnType.equals(stdlib.getByteType()) || + returnType.equals(stdlib.getFloatType()) || returnType.equals(stdlib.getDoubleType())) { + return "0"; + } + else if (returnType.equals(stdlib.getBooleanType())) { + return "false"; + } + return null; + } + + private MemberChooser showOverrideImplementChooser(Project project, + DescriptorClassMember[] members) { + final MemberChooser chooser = new MemberChooser(members, true, true, project); + chooser.setTitle(getChooserTitle()); + chooser.show(); + if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null; + return chooser; + } + + protected abstract String getChooserTitle(); + + @Override + public boolean isValidFor(Editor editor, PsiFile file) { + if (!(file instanceof JetFile)) { + return false; + } + final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); + final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class); + return classOrObject != null; + } + + @Override + public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) { + final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); + final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class); + Set missingImplementations = collectMethodsToGenerate(classOrObject); + if (missingImplementations.isEmpty()) { + HintManager.getInstance().showErrorHint(editor, "No methods to implement have been found"); + return; + } + List members = membersFromDescriptors(missingImplementations); + final MemberChooser chooser = showOverrideImplementChooser(project, + members.toArray(new DescriptorClassMember[members.size()])); + if (chooser == null) { + return; + } + + final List selectedElements = chooser.getSelectedElements(); + if (selectedElements == null || selectedElements.isEmpty()) return; + + new WriteCommandAction(project, file) { + protected void run(final Result result) throws Throwable { + generateMethods(project, editor, classOrObject, selectedElements); + } + }.execute(); + + } + + @Override + public boolean startInWriteAction() { + return false; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideMethodsHandler.java b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideMethodsHandler.java new file mode 100644 index 00000000000..2cb0be4bb09 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/OverrideMethodsHandler.java @@ -0,0 +1,31 @@ +package org.jetbrains.jet.plugin.codeInsight; + +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; +import org.jetbrains.jet.lang.descriptors.MutableClassDescriptor; +import org.jetbrains.jet.lang.resolve.OverrideResolver; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author yole + */ +public class OverrideMethodsHandler extends OverrideImplementMethodsHandler { + protected Set collectMethodsToGenerate(MutableClassDescriptor descriptor) { + final Set superMethods = OverrideResolver.collectSuperMethods(descriptor).keySet(); + for (CallableMemberDescriptor member : descriptor.getCallableMembers()) { + superMethods.removeAll(member.getOverriddenDescriptors()); + } + Set result = new HashSet(); + for (CallableMemberDescriptor superMethod : superMethods) { + if (superMethod.getModality().isOverridable()) { + result.add(superMethod); + } + } + return result; + } + + protected String getChooserTitle() { + return "Override Members"; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/formatter/JetBlock.java b/idea/src/org/jetbrains/jet/plugin/formatter/JetBlock.java index ff2ae3cadf1..ae847276ba7 100644 --- a/idea/src/org/jetbrains/jet/plugin/formatter/JetBlock.java +++ b/idea/src/org/jetbrains/jet/plugin/formatter/JetBlock.java @@ -24,6 +24,7 @@ public class JetBlock implements ASTBlock { private Indent myIndent; private Wrap myWrap; private CodeStyleSettings mySettings; + private final SpacingBuilder mySpacingBuilder; private List mySubBlocks; private static final TokenSet CODE_BLOCKS = TokenSet.create( @@ -35,12 +36,14 @@ public class JetBlock implements ASTBlock { JetNodeTypes.THEN, JetNodeTypes.ELSE); - public JetBlock(ASTNode node, Alignment alignment, Indent indent, Wrap wrap, CodeStyleSettings settings) { + public JetBlock(ASTNode node, Alignment alignment, Indent indent, Wrap wrap, CodeStyleSettings settings, + SpacingBuilder spacingBuilder) { myNode = node; myAlignment = alignment; myIndent = indent; myWrap = wrap; mySettings = settings; + mySpacingBuilder = spacingBuilder; } @Override @@ -110,7 +113,7 @@ public class JetBlock implements ASTBlock { childIndent = Indent.getNormalIndent(); } - return new JetBlock(child, childAlignment, childIndent, wrap, mySettings); + return new JetBlock(child, childAlignment, childIndent, wrap, mySettings, mySpacingBuilder); } private static Indent indentIfNotBrace(ASTNode child) { @@ -120,8 +123,8 @@ public class JetBlock implements ASTBlock { } @Override - public Spacing getSpacing(Block block, Block block1) { - return null; + public Spacing getSpacing(Block child1, Block child2) { + return mySpacingBuilder.getSpacing(this, child1, child2); } @NotNull diff --git a/idea/src/org/jetbrains/jet/plugin/formatter/JetFormattingModelBuilder.java b/idea/src/org/jetbrains/jet/plugin/formatter/JetFormattingModelBuilder.java index 852e8a05a30..e4a3f0d4901 100644 --- a/idea/src/org/jetbrains/jet/plugin/formatter/JetFormattingModelBuilder.java +++ b/idea/src/org/jetbrains/jet/plugin/formatter/JetFormattingModelBuilder.java @@ -1,15 +1,17 @@ package org.jetbrains.jet.plugin.formatter; -import com.intellij.formatting.FormattingModel; -import com.intellij.formatting.FormattingModelBuilder; -import com.intellij.formatting.FormattingModelProvider; -import com.intellij.formatting.Indent; +import com.intellij.formatting.*; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetNodeTypes; +import org.jetbrains.jet.lexer.JetTokens; + +import static org.jetbrains.jet.JetNodeTypes.*; +import static org.jetbrains.jet.lexer.JetTokens.*; /** * @author yole @@ -18,10 +20,22 @@ public class JetFormattingModelBuilder implements FormattingModelBuilder { @NotNull @Override public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) { - final JetBlock block = new JetBlock(element.getNode(), null, Indent.getNoneIndent(), null, settings); + final JetBlock block = new JetBlock(element.getNode(), null, Indent.getNoneIndent(), null, settings, + createSpacingBuilder(settings)); return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings); } + private static SpacingBuilder createSpacingBuilder(CodeStyleSettings settings) { + return new SpacingBuilder(settings) + .before(COMMA).spaceIf(settings.SPACE_BEFORE_COMMA) + .after(COMMA).spaceIf(settings.SPACE_AFTER_COMMA) + .around(EQ).spaceIf(settings.SPACE_AROUND_ASSIGNMENT_OPERATORS) + .beforeInside(BLOCK, FUN).spaceIf(settings.SPACE_BEFORE_METHOD_LBRACE) + .afterInside(LBRACE, BLOCK).lineBreakInCode() + .beforeInside(RBRACE, CLASS_BODY).lineBreakInCode() + .beforeInside(RBRACE, BLOCK).lineBreakInCode(); + } + @Override public TextRange getRangeAffectingIndent(PsiFile psiFile, int i, ASTNode astNode) { return null; diff --git a/idea/testData/codeInsight/overrideImplement/genericMethod.kt b/idea/testData/codeInsight/overrideImplement/genericMethod.kt new file mode 100644 index 00000000000..3c7e6d8de5b --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/genericMethod.kt @@ -0,0 +1,7 @@ +trait G { + fun foo(t : T) : T +} + +class GC() : G { + +} diff --git a/idea/testData/codeInsight/overrideImplement/genericMethod.kt.after b/idea/testData/codeInsight/overrideImplement/genericMethod.kt.after new file mode 100644 index 00000000000..f55e765fc4e --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/genericMethod.kt.after @@ -0,0 +1,10 @@ +trait G { + fun foo(t : T) : T +} + +class GC() : G { + + override fun foo(t: Int): Int { + return 0 + } +} diff --git a/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Impl.kt b/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Impl.kt new file mode 100644 index 00000000000..14c331bd84c --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Impl.kt @@ -0,0 +1,5 @@ +import foo.Intf + +class Impl(): Intf { + +} diff --git a/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Impl.kt.after b/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Impl.kt.after new file mode 100644 index 00000000000..f0e3fb14e49 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Impl.kt.after @@ -0,0 +1,8 @@ +import foo.Intf + +class Impl(): Intf { + + override fun getFooBar(): String? { + return null + } +} diff --git a/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Intf.java b/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Intf.java new file mode 100644 index 00000000000..65891b1db74 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/javaInterfaceMethod/foo/Intf.java @@ -0,0 +1,5 @@ +package foo; + +interface Intf { + String getFooBar(); +} diff --git a/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt b/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt new file mode 100644 index 00000000000..14c331bd84c --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt @@ -0,0 +1,5 @@ +import foo.Intf + +class Impl(): Intf { + +} diff --git a/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt.after b/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt.after new file mode 100644 index 00000000000..a20e435f471 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt.after @@ -0,0 +1,7 @@ +import foo.Intf + +class Impl(): Intf { + + override fun fooBar(i: Int, s: Array?, foo: Any?) { + } +} diff --git a/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Intf.java b/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Intf.java new file mode 100644 index 00000000000..04c1e8f6dfa --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/javaParameters/foo/Intf.java @@ -0,0 +1,5 @@ +package foo; + +interface Intf { + void fooBar(int i, String[] s, Object foo); +} diff --git a/idea/testData/codeInsight/overrideImplement/property.kt b/idea/testData/codeInsight/overrideImplement/property.kt new file mode 100644 index 00000000000..0f08af46b60 --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/property.kt @@ -0,0 +1,7 @@ +trait T { + val v : Int +} + +class GC() : T { + +} diff --git a/idea/testData/codeInsight/overrideImplement/property.kt.after b/idea/testData/codeInsight/overrideImplement/property.kt.after new file mode 100644 index 00000000000..bfff67330db --- /dev/null +++ b/idea/testData/codeInsight/overrideImplement/property.kt.after @@ -0,0 +1,8 @@ +trait T { + val v : Int +} + +class GC() : T { + + override val v: Int = 0 +} diff --git a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java index c99444a9fb9..2072055fa59 100644 --- a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTest.java @@ -6,7 +6,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.JetTestCaseBuilder; -import org.jetbrains.jet.plugin.quickfix.PluginTestCaseBase; +import org.jetbrains.jet.plugin.PluginTestCaseBase; import java.io.File; diff --git a/idea/tests/org/jetbrains/jet/plugin/JetLightProjectDescriptor.java b/idea/tests/org/jetbrains/jet/plugin/JetLightProjectDescriptor.java new file mode 100644 index 00000000000..9fe80a43672 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/JetLightProjectDescriptor.java @@ -0,0 +1,33 @@ +package org.jetbrains.jet.plugin; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.StdModuleTypes; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.testFramework.LightProjectDescriptor; + +/** + * @author yole + */ +public class JetLightProjectDescriptor implements LightProjectDescriptor { + private JetLightProjectDescriptor() { + } + + public static final JetLightProjectDescriptor INSTANCE = new JetLightProjectDescriptor(); + + @Override + public ModuleType getModuleType() { + return StdModuleTypes.JAVA; + } + + @Override + public Sdk getSdk() { + return PluginTestCaseBase.jdkFromIdeaHome(); + } + + @Override + public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) { + } +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/PluginTestCaseBase.java b/idea/tests/org/jetbrains/jet/plugin/PluginTestCaseBase.java similarity index 86% rename from idea/tests/org/jetbrains/jet/plugin/quickfix/PluginTestCaseBase.java rename to idea/tests/org/jetbrains/jet/plugin/PluginTestCaseBase.java index 7a14d6e797c..5aaf520fc18 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/PluginTestCaseBase.java +++ b/idea/tests/org/jetbrains/jet/plugin/PluginTestCaseBase.java @@ -1,4 +1,4 @@ -package org.jetbrains.jet.plugin.quickfix; +package org.jetbrains.jet.plugin; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.JavaSdkImpl; @@ -8,6 +8,9 @@ import org.jetbrains.jet.JetTestCaseBuilder; * @author yole */ public class PluginTestCaseBase { + private PluginTestCaseBase() { + } + public static String getTestDataPathBase() { return JetTestCaseBuilder.getHomeDirectory() + "/idea/testData"; } diff --git a/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java b/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java new file mode 100644 index 00000000000..8d528827964 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/codeInsight/OverrideImplementTest.java @@ -0,0 +1,75 @@ +package org.jetbrains.jet.plugin.codeInsight; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.plugin.JetLightProjectDescriptor; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.util.Set; + +/** + * @author yole + */ +public class OverrideImplementTest extends LightCodeInsightFixtureTestCase { + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JetLightProjectDescriptor.INSTANCE; + } + + @Override + public void setUp() throws Exception { + super.setUp(); + myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/codeInsight/overrideImplement"); + } + + public void testJavaInterfaceMethod() { + doDirectoryTest(); + } + + public void testJavaParameters() { + doDirectoryTest(); + } + + public void testGenericMethod() { + doFileTest(); + } + + public void testProperty() { + doFileTest(); + } + + private void doFileTest() { + myFixture.configureByFile(getTestName(true) + ".kt"); + doImplement(); + myFixture.checkResultByFile(getTestName(true) + ".kt.after"); + } + + private void doDirectoryTest() { + myFixture.copyDirectoryToProject(getTestName(true), ""); + myFixture.configureFromTempProjectFile("foo/Impl.kt"); + doImplement(); + myFixture.checkResultByFile(getTestName(true) + "/foo/Impl.kt.after"); + } + + private void doImplement() { + final PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset()); + final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class); + final Set descriptors = new ImplementMethodsHandler().collectMethodsToGenerate(classOrObject); + assertEquals(1, descriptors.size()); + new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) { + @Override + protected void run(Result result) throws Throwable { + ImplementMethodsHandler.generateMethods(myFixture.getProject(), myFixture.getEditor(), classOrObject, + ImplementMethodsHandler.membersFromDescriptors(descriptors)); + } + }.execute(); + } +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixTest.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixTest.java index 2e0da1ea44c..7b8521fb419 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/JetQuickFixTest.java @@ -7,6 +7,7 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.JetTestCaseBuilder; +import org.jetbrains.jet.plugin.PluginTestCaseBase; import java.io.File; import java.io.FilenameFilter; diff --git a/stdlib/ktSrc/ModuleBuilder.kt b/stdlib/ktSrc/ModuleBuilder.kt index bee397cbb56..ca94ba92ea1 100644 --- a/stdlib/ktSrc/ModuleBuilder.kt +++ b/stdlib/ktSrc/ModuleBuilder.kt @@ -35,7 +35,7 @@ class JarBuilder(val parent: ModuleBuilder) { } } -class ModuleBuilder(val name: String): IModuleBuilder { +open class ModuleBuilder(val name: String): IModuleBuilder { val sourceFiles: ArrayList = ArrayList() val classpathRoots: ArrayList = ArrayList() var _jarName: String? = null @@ -66,4 +66,11 @@ class ModuleBuilder(val name: String): IModuleBuilder { override fun getModuleName(): String? = name override fun getJarName(): String? = _jarName -} \ No newline at end of file +} + +class ModuleBuilder2(name: String): ModuleBuilder(name) { + + +} +} +