Added error 'public member should specify type' and quick fix
This commit is contained in:
@@ -123,6 +123,8 @@ public interface Errors {
|
||||
|
||||
DiagnosticWithParameterFactory<JetNamedDeclaration, JetClass> NON_FINAL_MEMBER_IN_FINAL_CLASS = DiagnosticWithParameterFactory.create(ERROR, "Non final member in a final class", DiagnosticParameters.CLASS);
|
||||
|
||||
DiagnosticWithParameterFactory<JetNamedDeclaration, JetType> PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE = DiagnosticWithParameterFactory.create(ERROR, "Public member should specify a type", DiagnosticParameters.TYPE);
|
||||
|
||||
SimpleDiagnosticFactory PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT = SimpleDiagnosticFactory.create(ERROR, "Projections are not allowed on type arguments of functions and properties"); // TODO : better positioning
|
||||
SimpleDiagnosticFactory SUPERTYPE_NOT_INITIALIZED = SimpleDiagnosticFactory.create(ERROR, "This type has a constructor, and thus must be initialized here");
|
||||
SimpleDiagnosticFactory SECONDARY_CONSTRUCTOR_BUT_NO_PRIMARY = SimpleDiagnosticFactory.create(ERROR, "A secondary constructor may appear only in a class that has a primary constructor");
|
||||
|
||||
@@ -481,17 +481,7 @@ public class ClassDescriptorResolver {
|
||||
|
||||
JetType type = getVariableType(scopeWithTypeParameters, property, true);
|
||||
|
||||
boolean hasBody = property.getInitializer() != null;
|
||||
if (!hasBody) {
|
||||
JetPropertyAccessor getter = property.getGetter();
|
||||
if (getter != null && getter.getBodyExpression() != null) {
|
||||
hasBody = true;
|
||||
}
|
||||
JetPropertyAccessor setter = property.getSetter();
|
||||
if (!hasBody && setter != null && setter.getBodyExpression() != null) {
|
||||
hasBody = true;
|
||||
}
|
||||
}
|
||||
boolean hasBody = hasBody(property);
|
||||
Modality defaultModality = getDefaultModality(containingDeclaration, hasBody);
|
||||
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
|
||||
containingDeclaration,
|
||||
@@ -513,6 +503,21 @@ public class ClassDescriptorResolver {
|
||||
return propertyDescriptor;
|
||||
}
|
||||
|
||||
/*package*/ static boolean hasBody(JetProperty property) {
|
||||
boolean hasBody = property.getInitializer() != null;
|
||||
if (!hasBody) {
|
||||
JetPropertyAccessor getter = property.getGetter();
|
||||
if (getter != null && getter.getBodyExpression() != null) {
|
||||
hasBody = true;
|
||||
}
|
||||
JetPropertyAccessor setter = property.getSetter();
|
||||
if (!hasBody && setter != null && setter.getBodyExpression() != null) {
|
||||
hasBody = true;
|
||||
}
|
||||
}
|
||||
return hasBody;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JetType getVariableType(@NotNull final JetScope scope, @NotNull final JetProperty property, boolean allowDeferred) {
|
||||
// TODO : receiver?
|
||||
|
||||
@@ -137,6 +137,29 @@ public class DeclarationsChecker {
|
||||
checkPropertyAbstractness(property, propertyDescriptor, classDescriptor);
|
||||
checkPropertyInitializer(property, propertyDescriptor, classDescriptor);
|
||||
checkAccessors(property, propertyDescriptor);
|
||||
checkDeclaredTypeInPublicMember(property, propertyDescriptor);
|
||||
}
|
||||
|
||||
private void checkDeclaredTypeInPublicMember(JetNamedDeclaration member, CallableMemberDescriptor memberDescriptor) {
|
||||
PsiElement nameIdentifier = member.getNameIdentifier();
|
||||
boolean hasDeferredType;
|
||||
if (member instanceof JetProperty) {
|
||||
hasDeferredType = ((JetProperty) member).getPropertyTypeRef() == null && ClassDescriptorResolver.hasBody((JetProperty) member);
|
||||
}
|
||||
else {
|
||||
assert member instanceof JetFunction;
|
||||
JetFunction function = (JetFunction) member;
|
||||
hasDeferredType = function.getReturnTypeRef() == null && function.getBodyExpression() != null && !function.hasBlockBody();
|
||||
}
|
||||
if ((memberDescriptor.getVisibility() == Visibility.PUBLIC || memberDescriptor.getVisibility() == Visibility.PROTECTED) &&
|
||||
hasDeferredType && nameIdentifier != null) {
|
||||
|
||||
JetType returnType = memberDescriptor.getReturnType();
|
||||
if (returnType instanceof DeferredType) {
|
||||
returnType = ((DeferredType) returnType).getActualType();
|
||||
}
|
||||
context.getTrace().report(PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE.on(member, nameIdentifier, returnType));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPropertyAbstractness(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {
|
||||
@@ -232,6 +255,7 @@ public class DeclarationsChecker {
|
||||
JetModifierList modifierList = function.getModifierList();
|
||||
ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
|
||||
boolean hasAbstractModifier = abstractNode != null;
|
||||
checkDeclaredTypeInPublicMember(function, functionDescriptor);
|
||||
if (containingDescriptor instanceof ClassDescriptor) {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) containingDescriptor;
|
||||
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.jetbrains.jet.plugin.quickfix;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters;
|
||||
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters;
|
||||
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
/**
|
||||
* @author svtk
|
||||
*/
|
||||
public class AddReturnTypeFix extends JetIntentionAction<JetNamedDeclaration> {
|
||||
private JetType type;
|
||||
|
||||
public AddReturnTypeFix(@NotNull JetNamedDeclaration element, JetType type) {
|
||||
super(element);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return "Add return type declaration";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Add return type declaration";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
PsiElement newElement;
|
||||
if (element instanceof JetProperty) {
|
||||
newElement = addPropertyType(project, (JetProperty) element, type);
|
||||
}
|
||||
else {
|
||||
assert element instanceof JetFunction;
|
||||
newElement = addFunctionType(project, (JetFunction) element, type);
|
||||
}
|
||||
ImportClassHelper.perform(type, element, newElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static JetProperty addPropertyType(Project project, JetProperty property, JetType type) {
|
||||
JetProperty newProperty = (JetProperty) property.copy();
|
||||
JetTypeReference typeReference = JetPsiFactory.createType(project, type.toString());
|
||||
Pair<PsiElement, PsiElement> colon = JetPsiFactory.createColon(project);
|
||||
PsiElement nameIdentifier = newProperty.getNameIdentifier();
|
||||
addTypeReference(newProperty, typeReference, colon, nameIdentifier);
|
||||
return newProperty;
|
||||
}
|
||||
|
||||
public static JetFunction addFunctionType(Project project, JetFunction function, JetType type) {
|
||||
JetFunction newFunction = (JetFunction) function.copy();
|
||||
JetTypeReference typeReference = JetPsiFactory.createType(project, type.toString());
|
||||
Pair<PsiElement, PsiElement> colon = JetPsiFactory.createColon(project);
|
||||
JetParameterList valueParameterList = newFunction.getValueParameterList();
|
||||
addTypeReference(newFunction, typeReference, colon, valueParameterList);
|
||||
return newFunction;
|
||||
}
|
||||
|
||||
private static void addTypeReference(JetNamedDeclaration element, JetTypeReference typeReference, Pair<PsiElement, PsiElement> colon, PsiElement anchor) {
|
||||
assert anchor != null;
|
||||
element.addAfter(typeReference, anchor);
|
||||
element.addRangeAfter(colon.getFirst(), colon.getSecond(), anchor);
|
||||
}
|
||||
|
||||
public static JetIntentionActionFactory<JetNamedDeclaration> createFactory() {
|
||||
return new JetIntentionActionFactory<JetNamedDeclaration>() {
|
||||
@Override
|
||||
public JetIntentionAction<JetNamedDeclaration> createAction(DiagnosticWithPsiElement diagnostic) {
|
||||
assert diagnostic.getPsiElement() instanceof JetNamedDeclaration;
|
||||
DiagnosticWithParameters<PsiElement> diagnosticWithParameters = assertAndCastToDiagnosticWithParameters(diagnostic, DiagnosticParameters.TYPE);
|
||||
JetType type = diagnosticWithParameters.getParameter(DiagnosticParameters.TYPE);
|
||||
return new AddReturnTypeFix((JetNamedDeclaration) diagnostic.getPsiElement(), type);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -103,5 +103,7 @@ public class QuickFixes {
|
||||
add(Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, removeModifierFactory);
|
||||
add(Errors.REDUNDANT_MODIFIER_IN_GETTER, removeRedundantModifierFactory);
|
||||
add(Errors.ILLEGAL_MODIFIER, removeModifierFactory);
|
||||
|
||||
add(Errors.PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE, AddReturnTypeFix.createFactory());
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package org.jetbrains.jet.plugin.quickfix;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
@@ -97,7 +96,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
|
||||
newElement.deleteChildRange(nextSibling, initializer);
|
||||
|
||||
if (newElement.getPropertyTypeRef() == null && type != null) {
|
||||
newElement = addPropertyType(project, newElement, type);
|
||||
newElement = AddReturnTypeFix.addPropertyType(project, newElement, type);
|
||||
needImport = true;
|
||||
}
|
||||
}
|
||||
@@ -108,17 +107,6 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
|
||||
}
|
||||
}
|
||||
|
||||
public static JetProperty addPropertyType(Project project, JetProperty property, JetType type) {
|
||||
JetProperty newProperty = (JetProperty) property.copy();
|
||||
JetTypeReference typeReference = JetPsiFactory.createType(project, type.toString());
|
||||
Pair<PsiElement, PsiElement> colon = JetPsiFactory.createColon(project);
|
||||
PsiElement nameIdentifier = newProperty.getNameIdentifier();
|
||||
assert nameIdentifier != null;
|
||||
newProperty.addAfter(typeReference, nameIdentifier);
|
||||
newProperty.addRangeAfter(colon.getFirst(), colon.getSecond(), nameIdentifier);
|
||||
return newProperty;
|
||||
}
|
||||
|
||||
public static JetIntentionActionFactory<JetProperty> createFactory() {
|
||||
return new JetIntentionActionFactory<JetProperty>() {
|
||||
@Override
|
||||
|
||||
@@ -164,3 +164,8 @@ fun f(): Int {
|
||||
}
|
||||
|
||||
fun f(): Int = if (1 < 2) 1 else returnNothing()
|
||||
|
||||
public fun <!PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE!>f<!>() = 1
|
||||
class B() {
|
||||
protected fun <!PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE!>f<!>() = "ss"
|
||||
}
|
||||
@@ -25,4 +25,5 @@ class Test() {
|
||||
<!UNRESOLVED_REFERENCE!>$b<!> = <!UNRESOLVED_REFERENCE!>$a<!>
|
||||
a = <!UNRESOLVED_REFERENCE!>$b<!>
|
||||
}
|
||||
public val <!PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE!>i<!> = 1
|
||||
}
|
||||
@@ -3,7 +3,7 @@ class Outer() {
|
||||
val outer: Outer get() = this@Outer
|
||||
}
|
||||
|
||||
public val x = Inner()
|
||||
public val x : Inner = Inner()
|
||||
}
|
||||
|
||||
fun box() : String {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
open class Base() {
|
||||
public val plain = 239
|
||||
val plain = 239
|
||||
public val read : Int
|
||||
get() = 239
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Outer() {
|
||||
public val s = "xyzzy"
|
||||
val s = "xyzzy"
|
||||
|
||||
open class InnerBase(public val name: String) {
|
||||
}
|
||||
@@ -7,7 +7,7 @@ class Outer() {
|
||||
class InnerDerived(): InnerBase(s) {
|
||||
}
|
||||
|
||||
public val x = InnerDerived()
|
||||
val x = InnerDerived()
|
||||
}
|
||||
|
||||
fun box() : String {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "true"
|
||||
namespace a
|
||||
|
||||
class A() {
|
||||
protected fun <caret>foo() : Int = 1
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "true"
|
||||
namespace a
|
||||
|
||||
class A() {
|
||||
public fun <caret>foo() : String = "a"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "true"
|
||||
namespace a
|
||||
|
||||
import java.util.List
|
||||
|
||||
public val <caret>l : List<Int>? = java.util.Collections.emptyList<Int>()
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "false"
|
||||
namespace a
|
||||
|
||||
class A() {
|
||||
internal protected fun <caret>foo() = 1
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "true"
|
||||
namespace a
|
||||
|
||||
class A() {
|
||||
protected fun <caret>foo() = 1
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "false"
|
||||
namespace a
|
||||
|
||||
class A() {
|
||||
public fun <caret>foo()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Add return type declaration" "true"
|
||||
namespace a
|
||||
|
||||
class A() {
|
||||
public fun <caret>foo() = "a"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Add return type declaration" "true"
|
||||
namespace a
|
||||
|
||||
public val <caret>l = java.util.Collections.emptyList<Int>()
|
||||
@@ -34,7 +34,7 @@ public class PropertyGenTest extends CodegenTestCase {
|
||||
}
|
||||
|
||||
public void testPublicVar() throws Exception {
|
||||
loadText("class PublicVar() { public var foo = 0; }");
|
||||
loadText("class PublicVar() { public var foo : Int = 0; }");
|
||||
final Class aClass = loadImplementationClass(generateClassesInFile(), "PublicVar");
|
||||
final Object instance = aClass.newInstance();
|
||||
Method setter = findMethodByName(aClass, "setFoo");
|
||||
@@ -44,7 +44,7 @@ public class PropertyGenTest extends CodegenTestCase {
|
||||
}
|
||||
|
||||
public void testAccessorsInInterface() {
|
||||
loadText("class AccessorsInInterface() { public var foo = 0; }");
|
||||
loadText("class AccessorsInInterface() { public var foo : Int = 0; }");
|
||||
final Class aClass = loadClass("AccessorsInInterface", generateClassesInFile());
|
||||
assertNotNull(findMethodByName(aClass, "getFoo"));
|
||||
assertNotNull(findMethodByName(aClass, "setFoo"));
|
||||
@@ -108,7 +108,7 @@ public class PropertyGenTest extends CodegenTestCase {
|
||||
}
|
||||
|
||||
public void testInitializersForNamespaceProperties() throws Exception {
|
||||
loadText("public val x = System.currentTimeMillis()");
|
||||
loadText("val x = System.currentTimeMillis()");
|
||||
final Method method = generateFunction("getX");
|
||||
assertIsCurrentTime((Long) method.invoke(null));
|
||||
}
|
||||
@@ -136,7 +136,7 @@ public class PropertyGenTest extends CodegenTestCase {
|
||||
}
|
||||
|
||||
public void testKt160() throws Exception {
|
||||
loadText("public val s = java.lang.Double.toString(1.0)");
|
||||
loadText("internal val s = java.lang.Double.toString(1.0)");
|
||||
final Method method = generateFunction("getS");
|
||||
assertEquals(method.invoke(null), "1.0");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.jetbrains.jet.plugin.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import org.jetbrains.jet.JetTestCaseBase;
|
||||
|
||||
/**
|
||||
@@ -21,5 +22,10 @@ public class TypeAdditionFixTests extends LightQuickFixTestCase {
|
||||
protected String getTestDataPath() {
|
||||
return JetTestCaseBase.getTestDataPathBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return JetTestCaseBase.jdkFromIdeaHome();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user