KT-456 No check for obligatory return in getters
This commit is contained in:
@@ -548,7 +548,7 @@ public class JetControlFlowProcessor {
|
|||||||
if (subroutine instanceof JetFunctionLiteralExpression) {
|
if (subroutine instanceof JetFunctionLiteralExpression) {
|
||||||
subroutine = ((JetFunctionLiteralExpression) subroutine).getFunctionLiteral();
|
subroutine = ((JetFunctionLiteralExpression) subroutine).getFunctionLiteral();
|
||||||
}
|
}
|
||||||
if (subroutine instanceof JetFunction) {
|
if (subroutine instanceof JetFunction || subroutine instanceof JetPropertyAccessor || subroutine instanceof JetConstructor) {
|
||||||
if (returnedExpression == null) {
|
if (returnedExpression == null) {
|
||||||
builder.returnNoValue(expression, subroutine);
|
builder.returnNoValue(expression, subroutine);
|
||||||
}
|
}
|
||||||
@@ -560,6 +560,9 @@ public class JetControlFlowProcessor {
|
|||||||
if (labelElement != null) {
|
if (labelElement != null) {
|
||||||
trace.report(NOT_A_RETURN_LABEL.on(expression, expression.getLabelName()));
|
trace.report(NOT_A_RETURN_LABEL.on(expression, expression.getLabelName()));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
trace.report(RETURN_NOT_ALLOWED.on(expression));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,14 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
|
|||||||
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||||
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
||||||
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||||
|
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author svtk
|
* @author svtk
|
||||||
*/
|
*/
|
||||||
@@ -67,7 +71,7 @@ public class JetFlowInformationProvider {
|
|||||||
wrappedTrace.close();
|
wrappedTrace.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collectReturnExpressions(@NotNull JetElement subroutine, @NotNull final Collection<JetExpression> returnedExpressions) {
|
private void collectReturnExpressions(@NotNull JetElement subroutine, @NotNull final Collection<JetExpression> returnedExpressions) {
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
@@ -120,19 +124,64 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collectUnreachableExpressions(@NotNull JetElement subroutine, @NotNull Collection<JetElement> unreachableElements) {
|
public void checkDefiniteReturn(@NotNull JetDeclarationWithBody function, final @NotNull JetType expectedReturnType) {
|
||||||
|
assert function instanceof JetDeclaration;
|
||||||
|
|
||||||
|
JetExpression bodyExpression = function.getBodyExpression();
|
||||||
|
if (bodyExpression == null) return;
|
||||||
|
|
||||||
|
List<JetExpression> returnedExpressions = Lists.newArrayList();
|
||||||
|
collectReturnExpressions(function.asElement(), returnedExpressions);
|
||||||
|
|
||||||
|
boolean nothingReturned = returnedExpressions.isEmpty();
|
||||||
|
|
||||||
|
returnedExpressions.remove(function); // This will be the only "expression" if the body is empty
|
||||||
|
|
||||||
|
if (expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) {
|
||||||
|
trace.report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType));
|
||||||
|
}
|
||||||
|
final boolean blockBody = function.hasBlockBody();
|
||||||
|
|
||||||
|
final Set<JetElement> rootUnreachableElements = collectUnreachableCode(function.asElement());
|
||||||
|
for (JetElement element : rootUnreachableElements) {
|
||||||
|
trace.report(UNREACHABLE_CODE.on(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JetExpression returnedExpression : returnedExpressions) {
|
||||||
|
returnedExpression.accept(new JetVisitorVoid() {
|
||||||
|
@Override
|
||||||
|
public void visitReturnExpression(JetReturnExpression expression) {
|
||||||
|
if (!blockBody) {
|
||||||
|
trace.report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitExpression(JetExpression expression) {
|
||||||
|
if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) {
|
||||||
|
trace.report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<JetElement> collectUnreachableCode(@NotNull JetElement subroutine) {
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
|
Collection<JetElement> unreachableElements = Lists.newArrayList();
|
||||||
for (Instruction deadInstruction : pseudocode.getDeadInstructions()) {
|
for (Instruction deadInstruction : pseudocode.getDeadInstructions()) {
|
||||||
if (deadInstruction instanceof JetElementInstruction &&
|
if (deadInstruction instanceof JetElementInstruction &&
|
||||||
!(deadInstruction instanceof ReadUnitValueInstruction)) {
|
!(deadInstruction instanceof ReadUnitValueInstruction)) {
|
||||||
unreachableElements.add(((JetElementInstruction) deadInstruction).getElement());
|
unreachableElements.add(((JetElementInstruction) deadInstruction).getElement());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
|
||||||
|
return JetPsiUtil.findRootExpressions(unreachableElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean inAnonymousInitializers, final boolean analyzeLocalDeclaration) {
|
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean inAnonymousInitializers, final boolean inLocalDeclaration) {
|
||||||
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
@@ -202,7 +251,7 @@ public class JetFlowInformationProvider {
|
|||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!analyzeLocalDeclaration && !isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
|
if (!inLocalDeclaration && !isInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
|
||||||
varWithUninitializedErrorGenerated.add(variableDescriptor);
|
varWithUninitializedErrorGenerated.add(variableDescriptor);
|
||||||
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
|
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
|
||||||
}
|
}
|
||||||
@@ -225,7 +274,7 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
JetExpression expression = (JetExpression) element;
|
JetExpression expression = (JetExpression) element;
|
||||||
if (!analyzeLocalDeclaration && hasInitializer && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
|
if (!inLocalDeclaration && hasInitializer && !variableDescriptor.isVar() && !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
|
||||||
PsiElement psiElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
PsiElement psiElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
||||||
JetProperty property = psiElement instanceof JetProperty ? (JetProperty) psiElement : null;
|
JetProperty property = psiElement instanceof JetProperty ? (JetProperty) psiElement : null;
|
||||||
varWithValReassignErrorGenerated.add(variableDescriptor);
|
varWithValReassignErrorGenerated.add(variableDescriptor);
|
||||||
@@ -254,7 +303,7 @@ public class JetFlowInformationProvider {
|
|||||||
for (Instruction instruction : pseudocode.getInstructions()) {
|
for (Instruction instruction : pseudocode.getInstructions()) {
|
||||||
if (instruction instanceof LocalDeclarationInstruction) {
|
if (instruction instanceof LocalDeclarationInstruction) {
|
||||||
JetElement element = ((LocalDeclarationInstruction) instruction).getElement();
|
JetElement element = ((LocalDeclarationInstruction) instruction).getElement();
|
||||||
markUninitializedVariables(element, false, analyzeLocalDeclaration);
|
markUninitializedVariables(element, false, inLocalDeclaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-6
@@ -45,12 +45,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enterSubroutine(@NotNull JetDeclaration subroutine) {
|
public void enterSubroutine(@NotNull JetDeclaration subroutine) {
|
||||||
if (builder != null) {
|
pushBuilder(subroutine, subroutine);
|
||||||
pushBuilder(subroutine, builder.getCurrentSubroutine());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pushBuilder(subroutine, subroutine);
|
|
||||||
}
|
|
||||||
assert builder != null;
|
assert builder != null;
|
||||||
builder.enterSubroutine(subroutine);
|
builder.enterSubroutine(subroutine);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,11 +281,11 @@ public class BodyResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resolveSecondaryConstructorBodies() {
|
private void resolveSecondaryConstructorBodies() {
|
||||||
for (Map.Entry<JetDeclaration, ConstructorDescriptor> entry : this.context.getConstructors().entrySet()) {
|
for (Map.Entry<JetConstructor, ConstructorDescriptor> entry : this.context.getConstructors().entrySet()) {
|
||||||
JetDeclaration declaration = entry.getKey();
|
JetConstructor constructor = entry.getKey();
|
||||||
ConstructorDescriptor descriptor = entry.getValue();
|
ConstructorDescriptor descriptor = entry.getValue();
|
||||||
|
|
||||||
resolveSecondaryConstructorBody((JetConstructor) declaration, descriptor, ((MutableClassDescriptor) descriptor.getContainingDeclaration()).getScopeForMemberResolution());
|
resolveSecondaryConstructorBody(constructor, descriptor, ((MutableClassDescriptor) descriptor.getContainingDeclaration()).getScopeForMemberResolution());
|
||||||
|
|
||||||
assert descriptor.getReturnType() != null;
|
assert descriptor.getReturnType() != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
package org.jetbrains.jet.lang.resolve;
|
package org.jetbrains.jet.lang.resolve;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
|
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
|
||||||
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
|
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
|
||||||
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
import org.jetbrains.jet.lang.types.JetStandardClasses;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
|
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
|
||||||
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,130 +18,70 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
|||||||
*/
|
*/
|
||||||
public class ControlFlowAnalyzer {
|
public class ControlFlowAnalyzer {
|
||||||
private TopDownAnalysisContext context;
|
private TopDownAnalysisContext context;
|
||||||
private ExpressionTypingServices typeInferrerServices;
|
|
||||||
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
|
||||||
private final boolean analyzeLocalDeclaration;
|
private final boolean inLocalDeclaration;
|
||||||
|
|
||||||
public ControlFlowAnalyzer(TopDownAnalysisContext context, JetControlFlowDataTraceFactory flowDataTraceFactory, boolean analyzeLocalDeclaration) {
|
public ControlFlowAnalyzer(TopDownAnalysisContext context, JetControlFlowDataTraceFactory flowDataTraceFactory, boolean inLocalDeclaration) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.flowDataTraceFactory = flowDataTraceFactory;
|
this.flowDataTraceFactory = flowDataTraceFactory;
|
||||||
this.analyzeLocalDeclaration = analyzeLocalDeclaration;
|
this.inLocalDeclaration = inLocalDeclaration;
|
||||||
this.typeInferrerServices = context.getSemanticServices().getTypeInferrerServices(context.getTrace());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process() {
|
public void process() {
|
||||||
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
|
for (JetClass aClass : context.getClasses().keySet()) {
|
||||||
JetClass aClass = entry.getKey();
|
|
||||||
MutableClassDescriptor classDescriptor = entry.getValue();
|
|
||||||
|
|
||||||
if (!context.completeAnalysisNeeded(aClass)) continue;
|
if (!context.completeAnalysisNeeded(aClass)) continue;
|
||||||
|
checkClassOrObject(aClass);
|
||||||
checkClassOrObject(aClass, classDescriptor);
|
|
||||||
}
|
}
|
||||||
|
for (JetObjectDeclaration objectDeclaration : context.getObjects().keySet()) {
|
||||||
for (Map.Entry<JetObjectDeclaration, MutableClassDescriptor> entry : context.getObjects().entrySet()) {
|
|
||||||
JetObjectDeclaration objectDeclaration = entry.getKey();
|
|
||||||
MutableClassDescriptor objectDescriptor = entry.getValue();
|
|
||||||
|
|
||||||
if (!context.completeAnalysisNeeded(objectDeclaration)) continue;
|
if (!context.completeAnalysisNeeded(objectDeclaration)) continue;
|
||||||
|
checkClassOrObject(objectDeclaration);
|
||||||
checkClassOrObject(objectDeclaration, objectDescriptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Map.Entry<JetNamedFunction, FunctionDescriptorImpl> entry : context.getFunctions().entrySet()) {
|
for (Map.Entry<JetNamedFunction, FunctionDescriptorImpl> entry : context.getFunctions().entrySet()) {
|
||||||
JetNamedFunction function = entry.getKey();
|
JetNamedFunction function = entry.getKey();
|
||||||
FunctionDescriptorImpl functionDescriptor = entry.getValue();
|
FunctionDescriptorImpl functionDescriptor = entry.getValue();
|
||||||
|
|
||||||
if (!context.completeAnalysisNeeded(function)) continue;
|
if (!context.completeAnalysisNeeded(function)) continue;
|
||||||
|
|
||||||
final JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType()
|
final JetType expectedReturnType = !function.hasBlockBody() && !function.hasDeclaredReturnType()
|
||||||
? NO_EXPECTED_TYPE
|
? NO_EXPECTED_TYPE
|
||||||
: functionDescriptor.getReturnType();
|
: functionDescriptor.getReturnType();
|
||||||
checkFunction(function, functionDescriptor, expectedReturnType);
|
checkFunction(function, expectedReturnType);
|
||||||
}
|
}
|
||||||
|
for (JetConstructor constructor : this.context.getConstructors().keySet()) {
|
||||||
for (Map.Entry<JetDeclaration, ConstructorDescriptor> entry : this.context.getConstructors().entrySet()) {
|
|
||||||
JetDeclaration declaration = entry.getKey();
|
|
||||||
assert declaration instanceof JetConstructor;
|
|
||||||
JetConstructor constructor = (JetConstructor) declaration;
|
|
||||||
ConstructorDescriptor descriptor = entry.getValue();
|
|
||||||
|
|
||||||
if (!context.completeAnalysisNeeded(constructor)) continue;
|
if (!context.completeAnalysisNeeded(constructor)) continue;
|
||||||
checkFunction(constructor, descriptor, JetStandardClasses.getUnitType());
|
checkFunction(constructor, JetStandardClasses.getUnitType());
|
||||||
}
|
|
||||||
|
|
||||||
for (JetProperty property : context.getProperties().keySet()) {
|
|
||||||
if (!context.completeAnalysisNeeded(property)) continue;
|
|
||||||
checkProperty(property);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkClassOrObject(JetClassOrObject klass, MutableClassDescriptor classDescriptor) {
|
private void checkClassOrObject(JetClassOrObject klass) {
|
||||||
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) klass, (JetExpression) klass, flowDataTraceFactory, context.getTrace());
|
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) klass, (JetExpression) klass, flowDataTraceFactory, context.getTrace());
|
||||||
flowInformationProvider.markUninitializedVariables((JetElement) klass, true, analyzeLocalDeclaration);
|
flowInformationProvider.markUninitializedVariables((JetElement) klass, true, inLocalDeclaration);
|
||||||
|
|
||||||
|
List<JetDeclaration> declarations = klass.getDeclarations();
|
||||||
|
for (JetDeclaration declaration : declarations) {
|
||||||
|
if (declaration instanceof JetProperty) {
|
||||||
|
JetProperty property = (JetProperty) declaration;
|
||||||
|
DeclarationDescriptor descriptor = context.getTrace().get(BindingContext.DECLARATION_TO_DESCRIPTOR, property);
|
||||||
|
assert descriptor instanceof PropertyDescriptor;
|
||||||
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||||
|
for (JetPropertyAccessor accessor : property.getAccessors()) {
|
||||||
|
PropertyAccessorDescriptor accessorDescriptor = accessor.isGetter()
|
||||||
|
? propertyDescriptor.getGetter()
|
||||||
|
: propertyDescriptor.getSetter();
|
||||||
|
assert accessorDescriptor != null;
|
||||||
|
flowInformationProvider.checkDefiniteReturn(accessor, accessorDescriptor.getReturnType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFunction(JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, final @NotNull JetType expectedReturnType) {
|
private void checkFunction(JetDeclarationWithBody function, final @NotNull JetType expectedReturnType) {
|
||||||
assert function instanceof JetDeclaration;
|
assert function instanceof JetDeclaration;
|
||||||
|
|
||||||
JetExpression bodyExpression = function.getBodyExpression();
|
JetExpression bodyExpression = function.getBodyExpression();
|
||||||
if (bodyExpression == null) return;
|
if (bodyExpression == null) return;
|
||||||
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) function, bodyExpression, flowDataTraceFactory, context.getTrace());
|
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) function, bodyExpression, flowDataTraceFactory, context.getTrace());
|
||||||
|
|
||||||
final boolean blockBody = function.hasBlockBody();
|
flowInformationProvider.checkDefiniteReturn(function, expectedReturnType);
|
||||||
List<JetElement> unreachableElements = Lists.newArrayList();
|
|
||||||
flowInformationProvider.collectUnreachableExpressions(function.asElement(), unreachableElements);
|
|
||||||
|
|
||||||
// This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
|
flowInformationProvider.markUninitializedVariables(function.asElement(), false, inLocalDeclaration);
|
||||||
final Set<JetElement> rootUnreachableElements = JetPsiUtil.findRootExpressions(unreachableElements);
|
|
||||||
|
|
||||||
for (JetElement element : rootUnreachableElements) {
|
|
||||||
context.getTrace().report(UNREACHABLE_CODE.on(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<JetExpression> returnedExpressions = Lists.newArrayList();
|
|
||||||
flowInformationProvider.collectReturnExpressions(function.asElement(), returnedExpressions);
|
|
||||||
|
|
||||||
boolean nothingReturned = returnedExpressions.isEmpty();
|
|
||||||
|
|
||||||
returnedExpressions.remove(function); // This will be the only "expression" if the body is empty
|
|
||||||
|
|
||||||
final JetScope scope = this.context.getDeclaringScopes().get(function);
|
|
||||||
|
|
||||||
if (expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && returnedExpressions.isEmpty() && !nothingReturned) {
|
|
||||||
context.getTrace().report(RETURN_TYPE_MISMATCH.on(bodyExpression, expectedReturnType));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (JetExpression returnedExpression : returnedExpressions) {
|
|
||||||
returnedExpression.accept(new JetVisitorVoid() {
|
|
||||||
@Override
|
|
||||||
public void visitReturnExpression(JetReturnExpression expression) {
|
|
||||||
if (!blockBody) {
|
|
||||||
context.getTrace().report(RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY.on(expression));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitExpression(JetExpression expression) {
|
|
||||||
if (blockBody && expectedReturnType != NO_EXPECTED_TYPE && !JetStandardClasses.isUnit(expectedReturnType) && !rootUnreachableElements.contains(expression)) {
|
|
||||||
JetType type = typeInferrerServices.getType(scope, expression, expectedReturnType);
|
|
||||||
if (type == null || !JetStandardClasses.isNothing(type)) {
|
|
||||||
context.getTrace().report(NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.on(expression));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
flowInformationProvider.markUninitializedVariables(function.asElement(), false, analyzeLocalDeclaration);
|
|
||||||
if (((JetDeclaration) function).hasModifier(JetTokens.INLINE_KEYWORD)) {
|
|
||||||
//inline functions after M1
|
|
||||||
// flowInformationProvider.markNotOnlyInvokedFunctionVariables(function.asElement(), functionDescriptor.getValueParameters());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkProperty(JetProperty property) {
|
|
||||||
JetExpression initializer = property.getInitializer();
|
|
||||||
if (initializer == null) return;
|
|
||||||
new JetFlowInformationProvider(property, initializer, flowDataTraceFactory, context.getTrace());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import java.util.Set;
|
|||||||
private final Map<JetDeclaration, JetScope> declaringScopes = Maps.newHashMap();
|
private final Map<JetDeclaration, JetScope> declaringScopes = Maps.newHashMap();
|
||||||
|
|
||||||
private final Map<JetNamedFunction, FunctionDescriptorImpl> functions = Maps.newLinkedHashMap();
|
private final Map<JetNamedFunction, FunctionDescriptorImpl> functions = Maps.newLinkedHashMap();
|
||||||
private final Map<JetDeclaration, ConstructorDescriptor> constructors = Maps.newLinkedHashMap();
|
private final Map<JetConstructor, ConstructorDescriptor> constructors = Maps.newLinkedHashMap();
|
||||||
private final Map<JetProperty, PropertyDescriptor> properties = Maps.newLinkedHashMap();
|
private final Map<JetProperty, PropertyDescriptor> properties = Maps.newLinkedHashMap();
|
||||||
private final Set<PropertyDescriptor> primaryConstructorParameterProperties = Sets.newHashSet();
|
private final Set<PropertyDescriptor> primaryConstructorParameterProperties = Sets.newHashSet();
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ import java.util.Set;
|
|||||||
return primaryConstructorParameterProperties;
|
return primaryConstructorParameterProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<JetDeclaration, ConstructorDescriptor> getConstructors() {
|
public Map<JetConstructor, ConstructorDescriptor> getConstructors() {
|
||||||
return constructors;
|
return constructors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
if (functionTypeExpected) {
|
if (functionTypeExpected) {
|
||||||
returnType = JetStandardClasses.getReturnType(expectedType);
|
returnType = JetStandardClasses.getReturnType(expectedType);
|
||||||
}
|
}
|
||||||
returnType = context.getServices().getBlockReturnedType(functionInnerScope, functionLiteral.getBodyExpression(), CoercionStrategy.COERCION_TO_UNIT, context.replaceExpectedType(returnType));
|
returnType = context.getServices().getBlockReturnedType(functionInnerScope, functionLiteral.getBodyExpression(), CoercionStrategy.COERCION_TO_UNIT, context.replaceExpectedType(returnType).replaceExpectedReturnType(returnType));
|
||||||
}
|
}
|
||||||
JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("<return type>") : returnType;
|
JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("<return type>") : returnType;
|
||||||
functionDescriptor.setReturnType(safeReturnType);
|
functionDescriptor.setReturnType(safeReturnType);
|
||||||
|
|||||||
@@ -55,16 +55,3 @@ error:
|
|||||||
sink:
|
sink:
|
||||||
<SINK> NEXT:[] PREV:[<END>]
|
<SINK> NEXT:[] PREV:[<END>]
|
||||||
=====================
|
=====================
|
||||||
== k ==
|
|
||||||
val k = 34
|
|
||||||
---------------------
|
|
||||||
l0:
|
|
||||||
<START> NEXT:[r(34)] PREV:[]
|
|
||||||
r(34) NEXT:[<END>] PREV:[<START>]
|
|
||||||
l1:
|
|
||||||
<END> NEXT:[<SINK>] PREV:[r(34)]
|
|
||||||
error:
|
|
||||||
<ERROR> NEXT:[] PREV:[]
|
|
||||||
sink:
|
|
||||||
<SINK> NEXT:[] PREV:[<END>]
|
|
||||||
=====================
|
|
||||||
|
|||||||
@@ -387,16 +387,3 @@ error:
|
|||||||
sink:
|
sink:
|
||||||
<SINK> NEXT:[] PREV:[<END>]
|
<SINK> NEXT:[] PREV:[<END>]
|
||||||
=====================
|
=====================
|
||||||
== a ==
|
|
||||||
val a: Int = 1
|
|
||||||
---------------------
|
|
||||||
l0:
|
|
||||||
<START> NEXT:[r(1)] PREV:[]
|
|
||||||
r(1) NEXT:[<END>] PREV:[<START>]
|
|
||||||
l1:
|
|
||||||
<END> NEXT:[<SINK>] PREV:[r(1)]
|
|
||||||
error:
|
|
||||||
<ERROR> NEXT:[] PREV:[]
|
|
||||||
sink:
|
|
||||||
<SINK> NEXT:[] PREV:[<END>]
|
|
||||||
=====================
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ fun unitEmpty() : Unit {}
|
|||||||
fun unitEmptyReturn() : Unit {return}
|
fun unitEmptyReturn() : Unit {return}
|
||||||
fun unitIntReturn() : Unit {return <!TYPE_MISMATCH!>1<!>}
|
fun unitIntReturn() : Unit {return <!TYPE_MISMATCH!>1<!>}
|
||||||
fun unitUnitReturn() : Unit {return ()}
|
fun unitUnitReturn() : Unit {return ()}
|
||||||
fun test1() : Any = {<!RETURN_NOT_ALLOWED, RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY!>return<!>}
|
fun test1() : Any = {return}
|
||||||
fun test2() : Any = @a {return@a 1}
|
fun test2() : Any = @a {return@a 1}
|
||||||
fun test3() : Any { <!RETURN_TYPE_MISMATCH!>return<!> }
|
fun test3() : Any { <!RETURN_TYPE_MISMATCH!>return<!> }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//KT-456 No check for obligatory return in getters
|
||||||
|
|
||||||
|
namespace kt456
|
||||||
|
|
||||||
|
class A() {
|
||||||
|
val i: Int
|
||||||
|
get() : Int <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{ //no error
|
||||||
|
}<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
//more tests
|
||||||
|
class B() {
|
||||||
|
val i: Int
|
||||||
|
get() <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{ //no error
|
||||||
|
}<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
class C() {
|
||||||
|
val i : Int
|
||||||
|
get() : Int {
|
||||||
|
try {
|
||||||
|
doSmth()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>doSmth()<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doSmth() {}
|
||||||
@@ -5,7 +5,7 @@ fun unitEmpty() : Unit {}
|
|||||||
fun unitEmptyReturn() : Unit {return}
|
fun unitEmptyReturn() : Unit {return}
|
||||||
fun unitIntReturn() : Unit {return <error>1</error>}
|
fun unitIntReturn() : Unit {return <error>1</error>}
|
||||||
fun unitUnitReturn() : Unit {return ()}
|
fun unitUnitReturn() : Unit {return ()}
|
||||||
fun test1() : Any = {<error>return</error>}
|
fun test1() : Any = {return}
|
||||||
fun test2() : Any = @a {return@a 1}
|
fun test2() : Any = @a {return@a 1}
|
||||||
fun test3() : Any { <error>return</error> }
|
fun test3() : Any { <error>return</error> }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user