New diagnostic for non-local return annotation

This commit is contained in:
Michael Bogdanov
2014-06-10 11:34:06 +04:00
parent 8092717da4
commit da01a11137
26 changed files with 689 additions and 91 deletions
@@ -594,6 +594,7 @@ public interface Errors {
//Inline and inlinable parameters //Inline and inlinable parameters
DiagnosticFactory2<JetElement, DeclarationDescriptor, DeclarationDescriptor> INVISIBLE_MEMBER_FROM_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT); DiagnosticFactory2<JetElement, DeclarationDescriptor, DeclarationDescriptor> INVISIBLE_MEMBER_FROM_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT);
DiagnosticFactory3<JetElement, JetElement, DeclarationDescriptor, DeclarationDescriptor> NON_LOCAL_RETURN_NOT_ALLOWED = DiagnosticFactory3.create(ERROR, CALL_ELEMENT);
DiagnosticFactory2<JetElement, JetNamedDeclaration, DeclarationDescriptor> NOT_YET_SUPPORTED_IN_INLINE = DiagnosticFactory2.create(ERROR); DiagnosticFactory2<JetElement, JetNamedDeclaration, DeclarationDescriptor> NOT_YET_SUPPORTED_IN_INLINE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<JetFunction, DeclarationDescriptor> NOTHING_TO_INLINE = DiagnosticFactory1.create(WARNING, NAMED_ELEMENT); DiagnosticFactory1<JetFunction, DeclarationDescriptor> NOTHING_TO_INLINE = DiagnosticFactory1.create(WARNING, NAMED_ELEMENT);
DiagnosticFactory2<JetElement, JetExpression, DeclarationDescriptor> USAGE_IS_NOT_INLINABLE = DiagnosticFactory2.create(ERROR); DiagnosticFactory2<JetElement, JetExpression, DeclarationDescriptor> USAGE_IS_NOT_INLINABLE = DiagnosticFactory2.create(ERROR);
@@ -515,6 +515,8 @@ public class DefaultErrorMessages {
MAP.put(USAGE_IS_NOT_INLINABLE, "Illegal usage of inline-parameter ''{0}'' in ''{1}''. Annotate the parameter with [noinline]", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(USAGE_IS_NOT_INLINABLE, "Illegal usage of inline-parameter ''{0}'' in ''{1}''. Annotate the parameter with [noinline]", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES);
MAP.put(NULLABLE_INLINE_PARAMETER, "Inline-parameter ''{0}'' of ''{1}'' must not be nullable. Annotate the parameter with [noinline] or make not nullable", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(NULLABLE_INLINE_PARAMETER, "Inline-parameter ''{0}'' of ''{1}'' must not be nullable. Annotate the parameter with [noinline] or make not nullable", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES);
MAP.put(RECURSION_IN_INLINE, "Inline-function ''{1}'' can't be recursive", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(RECURSION_IN_INLINE, "Inline-function ''{1}'' can't be recursive", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES);
//Inline non Locals
MAP.put(NON_LOCAL_RETURN_NOT_ALLOWED, "Can''t inline ''{0}'' here: it may contain non-local returns. Annotate parameter declaration ''{0}'' with ''inlineOptions(ONLY_LOCAL_RETURNS)''", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
MAP.setImmutable(); MAP.setImmutable();
@@ -884,4 +884,37 @@ public class JetPsiUtil {
return element instanceof JetSimpleNameExpression && return element instanceof JetSimpleNameExpression &&
((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.LABEL_IDENTIFIER; ((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.LABEL_IDENTIFIER;
} }
@Nullable
public static JetExpression getParentCallIfPresent(@NotNull JetExpression expression) {
PsiElement parent = expression.getParent();
while (parent != null) {
if (parent instanceof JetBinaryExpression ||
parent instanceof JetUnaryExpression ||
parent instanceof JetLabeledExpression ||
parent instanceof JetDotQualifiedExpression ||
parent instanceof JetCallExpression ||
parent instanceof JetArrayAccessExpression ||
parent instanceof JetMultiDeclaration) {
if (parent instanceof JetLabeledExpression) {
parent = parent.getParent();
continue;
}
//check that it's in inlineable call would be in resolve call of parent
return (JetExpression) parent;
}
else if (parent instanceof JetParenthesizedExpression || parent instanceof JetBinaryExpressionWithTypeRHS) {
parent = parent.getParent();
}
else if (parent instanceof JetValueArgument || parent instanceof JetValueArgumentList) {
parent = parent.getParent();
}
else {
return null;
}
}
return null;
}
} }
@@ -328,17 +328,18 @@ public class BindingContextUtils {
} }
@NotNull @NotNull
public static Pair<FunctionDescriptor, PsiElement> getLambdaContainingFunction( public static Pair<FunctionDescriptor, PsiElement> getContainingFunctionSkipFunctionLiterals(
@NotNull FunctionDescriptor lambdaDescriptor, @NotNull BindingContext context,
@NotNull BindingContext context @Nullable DeclarationDescriptor startDescriptor,
boolean strict
) { ) {
FunctionDescriptor containingFunctionDescriptor = lambdaDescriptor; FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(startDescriptor, FunctionDescriptor.class, strict);
PsiElement containingFunction; PsiElement containingFunction = containingFunctionDescriptor != null ? callableDescriptorToDeclaration(context, containingFunctionDescriptor) : null;
do { while (containingFunction instanceof JetFunctionLiteral) {
containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class); containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class);
containingFunction = containingFunctionDescriptor != null ? callableDescriptorToDeclaration(context, containingFunction = containingFunctionDescriptor != null ? callableDescriptorToDeclaration(context,
containingFunctionDescriptor) : null; containingFunctionDescriptor) : null;
} while (containingFunction instanceof JetFunctionLiteral); }
return new Pair<FunctionDescriptor, PsiElement>(containingFunctionDescriptor, containingFunction); return new Pair<FunctionDescriptor, PsiElement>(containingFunctionDescriptor, containingFunction);
} }
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
public class InlineDescriptorUtils {
public static boolean checkNonLocalReturnUsage(@NotNull DeclarationDescriptor fromFunction, @NotNull JetExpression startExpression, @NotNull BindingTrace trace) {
PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, JetClassOrObject.class, JetDeclarationWithBody.class);
if (containingFunction == null) {
return false;
}
DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction);
if (containingFunctionDescriptor == null) {
return false;
}
BindingContext bindingContext = trace.getBindingContext();
while (containingFunction instanceof JetFunctionLiteral && fromFunction != containingFunctionDescriptor) {
//JetFunctionLiteralExpression
containingFunction = containingFunction.getParent();
boolean isInlinedLambda = false;
JetExpression call = JetPsiUtil.getParentCallIfPresent((JetFunctionLiteralExpression) containingFunction);
if (call != null) {
//TODO: ask sveta
ResolvedCall<?> resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, JetPsiUtil.getCalleeExpressionIfAny(call));
CallableDescriptor resultingDescriptor = resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
if (resultingDescriptor instanceof SimpleFunctionDescriptor) {
isInlinedLambda = ((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline();
}
}
if (!isInlinedLambda) {
return false;
}
containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
containingFunction = containingFunctionDescriptor != null
? BindingContextUtils.descriptorToDeclaration(bindingContext, containingFunctionDescriptor)
: null;
}
return fromFunction == containingFunctionDescriptor;
}
@Nullable
public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
DeclarationDescriptor currentDescriptor = strict ? descriptor.getContainingDeclaration() : descriptor;
while (currentDescriptor != null) {
if (currentDescriptor instanceof FunctionDescriptor || currentDescriptor instanceof ClassDescriptor) {
return currentDescriptor;
}
currentDescriptor = currentDescriptor.getContainingDeclaration();
}
return null;
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.calls; package org.jetbrains.jet.lang.resolve.calls;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
@@ -24,11 +25,9 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.InlineDescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext; import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument; import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
@@ -39,6 +38,7 @@ import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
public class InlineCallResolverExtension implements CallResolverExtension { public class InlineCallResolverExtension implements CallResolverExtension {
@@ -82,69 +82,65 @@ public class InlineCallResolverExtension implements CallResolverExtension {
checkCallWithReceiver(context, targetDescriptor, resolvedCall.getReceiverArgument(), expression); checkCallWithReceiver(context, targetDescriptor, resolvedCall.getReceiverArgument(), expression);
if (inlinableParameters.contains(targetDescriptor)) { if (inlinableParameters.contains(targetDescriptor)) {
if (!couldAccessVariable(expression)) { if (!isInsideCall(expression)) {
context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor)); context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor));
} }
} }
for (ResolvedValueArgument value : resolvedCall.getValueArguments().values()) { for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
ResolvedValueArgument value = entry.getValue();
ValueParameterDescriptor valueDescriptor = entry.getKey();
if (!(value instanceof DefaultValueArgument)) { if (!(value instanceof DefaultValueArgument)) {
for (ValueArgument argument : value.getArguments()) { for (ValueArgument argument : value.getArguments()) {
checkValueParameter(context, targetDescriptor, argument, value instanceof VarargValueArgument); checkValueParameter(context, targetDescriptor, argument, valueDescriptor);
} }
} }
} }
checkVisibility(targetDescriptor, expression, context); checkVisibility(targetDescriptor, expression, context);
checkRecursion(targetDescriptor, expression, context); checkRecursion(context, targetDescriptor, expression);
} }
private static boolean couldAccessVariable(JetExpression expression) { private static boolean isInsideCall(JetExpression expression) {
PsiElement parent = expression.getParent(); JetElement parent = JetPsiUtil.getParentCallIfPresent(expression);
while (parent != null) { if (parent instanceof JetBinaryExpression) {
if (parent instanceof JetValueArgument || JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
parent instanceof JetBinaryExpression || if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
parent instanceof JetUnaryExpression || //assignment
parent instanceof JetLabeledExpression ||
parent instanceof JetDotQualifiedExpression ||
parent instanceof JetCallExpression ||
parent instanceof JetArrayAccessExpression ||
parent instanceof JetMultiDeclaration) {
if (parent instanceof JetLabeledExpression) {
parent = parent.getParent();
continue;
}
else if (parent instanceof JetBinaryExpression) {
JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
//assignment
return false;
}
}
//check that it's in inlineable call would be in resolve call of parent
return true;
}
else if (parent instanceof JetParenthesizedExpression || parent instanceof JetBinaryExpressionWithTypeRHS) {
parent = parent.getParent();
}
else {
return false; return false;
} }
} }
return false;
return parent != null;
} }
private void checkValueParameter(BasicCallResolutionContext context, CallableDescriptor targetDescriptor, ValueArgument argument, boolean isVararg) {
JetExpression jetExpression = argument.getArgumentExpression();
if (jetExpression == null) { private void checkValueParameter(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor targetDescriptor,
@NotNull ValueArgument targetArgument,
@NotNull ValueParameterDescriptor targetParameterDescriptor
) {
JetExpression argumentExpression = targetArgument.getArgumentExpression();
if (argumentExpression == null) {
return; return;
} }
CallableDescriptor varDescriptor = getDescriptor(context, jetExpression); CallableDescriptor argumentCallee = getCalleeDescriptor(context, argumentExpression, false);
if (varDescriptor != null && inlinableParameters.contains(varDescriptor)) { if (argumentCallee != null && inlinableParameters.contains(argumentCallee)) {
checkFunctionCall(context, targetDescriptor, jetExpression, isVararg); boolean isTargetInlineFunction = targetDescriptor instanceof SimpleFunctionDescriptor &&
((SimpleFunctionDescriptor) targetDescriptor).getInlineStrategy().isInline();
if (!isTargetInlineFunction || !isInlinableParameter(targetParameterDescriptor)) {
context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(argumentExpression, argumentExpression, descriptor));
} else {
if (allowsNonLocalReturns(argumentCallee) && !allowsNonLocalReturns(targetParameterDescriptor)) {
context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(argumentExpression, argumentExpression, argumentCallee, descriptor));
} else {
checkNonLocalReturn(context, argumentCallee, argumentExpression);
}
}
} }
} }
@@ -160,7 +156,7 @@ public class InlineCallResolverExtension implements CallResolverExtension {
JetExpression receiverExpression = null; JetExpression receiverExpression = null;
if (receiver instanceof ExpressionReceiver) { if (receiver instanceof ExpressionReceiver) {
receiverExpression = ((ExpressionReceiver) receiver).getExpression(); receiverExpression = ((ExpressionReceiver) receiver).getExpression();
varDescriptor = getDescriptor(context, receiverExpression); varDescriptor = getCalleeDescriptor(context, receiverExpression, true);
} }
else if (receiver instanceof ExtensionReceiver) { else if (receiver instanceof ExtensionReceiver) {
ExtensionReceiver extensionReceiver = (ExtensionReceiver) receiver; ExtensionReceiver extensionReceiver = (ExtensionReceiver) receiver;
@@ -174,35 +170,42 @@ public class InlineCallResolverExtension implements CallResolverExtension {
if (inlinableParameters.contains(varDescriptor)) { if (inlinableParameters.contains(varDescriptor)) {
//check that it's invoke or inlinable extension //check that it's invoke or inlinable extension
checkFunctionCall(context, targetDescriptor, receiverExpression, false); checkLambdaInvokeOrExtensionCall(context, varDescriptor, targetDescriptor, receiverExpression);
} }
} }
@Nullable @Nullable
private static CallableDescriptor getDescriptor( private static CallableDescriptor getCalleeDescriptor(
@NotNull BasicCallResolutionContext context, @NotNull BasicCallResolutionContext context,
@NotNull JetExpression expression @NotNull JetExpression expression,
boolean unwrapVariableAsFunction
) { ) {
// todo ask sveta
ResolvedCall<?> thisCall = context.trace.get(BindingContext.RESOLVED_CALL, expression); ResolvedCall<?> thisCall = context.trace.get(BindingContext.RESOLVED_CALL, expression);
if (unwrapVariableAsFunction && thisCall instanceof VariableAsFunctionResolvedCall) {
return ((VariableAsFunctionResolvedCall) thisCall).getVariableCall().getResultingDescriptor();
}
return thisCall != null ? thisCall.getResultingDescriptor() : null; return thisCall != null ? thisCall.getResultingDescriptor() : null;
} }
private void checkFunctionCall( private void checkLambdaInvokeOrExtensionCall(
BasicCallResolutionContext context, @NotNull BasicCallResolutionContext context,
CallableDescriptor targetDescriptor, @NotNull CallableDescriptor lambdaDescriptor,
JetExpression receiverExpresssion, @NotNull CallableDescriptor callDescriptor,
boolean isVararg @NotNull JetExpression receiverExpresssion
) { ) {
boolean inlinableCall = isInvokeOrInlineExtension(targetDescriptor); boolean inlinableCall = isInvokeOrInlineExtension(callDescriptor);
if (!inlinableCall || isVararg) { if (!inlinableCall) {
context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(receiverExpresssion, receiverExpresssion, descriptor)); context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(receiverExpresssion, receiverExpresssion, descriptor));
} else {
checkNonLocalReturn(context, lambdaDescriptor, receiverExpresssion);
} }
} }
public void checkRecursion( public void checkRecursion(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor targetDescriptor, @NotNull CallableDescriptor targetDescriptor,
@NotNull JetElement expression, @NotNull JetElement expression
@NotNull BasicCallResolutionContext context
) { ) {
if (targetDescriptor.getOriginal() == descriptor) { if (targetDescriptor.getOriginal() == descriptor) {
context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor)); context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
@@ -248,4 +251,34 @@ public class InlineCallResolverExtension implements CallResolverExtension {
} }
return true; return true;
} }
private void checkNonLocalReturn(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor inlinableParameterDescriptor,
@NotNull JetExpression parameterUsage
) {
if (!allowsNonLocalReturns(inlinableParameterDescriptor)) return;
if (!InlineDescriptorUtils.checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) {
context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage, inlinableParameterDescriptor, descriptor));
}
}
@Nullable
public static PsiElement getDeclaration(JetExpression expression) {
do {
expression = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class);
} while (expression instanceof JetMultiDeclaration || expression instanceof JetProperty);
return expression;
}
private static boolean allowsNonLocalReturns(CallableDescriptor lambdaDescriptor) {
if (lambdaDescriptor instanceof ValueParameterDescriptor) {
if (InlineUtil.hasOnlyLocalReturn((ValueParameterDescriptor) lambdaDescriptor)) {
//annotated
return false;
}
}
return true;
}
} }
@@ -22,16 +22,10 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.DescriptorResolver;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.model.MutableDataFlowInfoForArguments; import org.jetbrains.jet.lang.resolve.calls.model.MutableDataFlowInfoForArguments;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
@@ -451,27 +445,24 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
// In a default value for parameter // In a default value for parameter
context.trace.report(RETURN_NOT_ALLOWED.on(expression)); context.trace.report(RETURN_NOT_ALLOWED.on(expression));
} }
assert parentDeclaration != null;
DeclarationDescriptor declarationDescriptor = context.trace.get(DECLARATION_TO_DESCRIPTOR, parentDeclaration);
FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(declarationDescriptor, FunctionDescriptor.class, false);
if (expression.getTargetLabel() == null) { if (expression.getTargetLabel() == null) {
if (containingFunctionDescriptor != null) { assert parentDeclaration != null;
PsiElement containingFunction = BindingContextUtils.callableDescriptorToDeclaration(context.trace.getBindingContext(), containingFunctionDescriptor); DeclarationDescriptor declarationDescriptor = context.trace.get(DECLARATION_TO_DESCRIPTOR, parentDeclaration);
assert containingFunction != null; Pair<FunctionDescriptor, PsiElement> containingFunInfo =
if (containingFunction instanceof JetFunctionLiteral) { BindingContextUtils.getContainingFunctionSkipFunctionLiterals(context.trace.getBindingContext(), declarationDescriptor,
Pair<FunctionDescriptor, PsiElement> result = BindingContextUtils false);
.getLambdaContainingFunction(containingFunctionDescriptor, context.trace.getBindingContext()); FunctionDescriptor containingFunctionDescriptor = containingFunInfo.getFirst();
containingFunctionDescriptor = result.getFirst();
containingFunction = result.getSecond();
if (containingFunctionDescriptor != null) {
if (!InlineDescriptorUtils.checkNonLocalReturnUsage(containingFunctionDescriptor, expression, context.trace) ||
containingFunctionDescriptor instanceof ConstructorDescriptor) {
// Unqualified, in a function literal // Unqualified, in a function literal
context.trace.report(RETURN_NOT_ALLOWED.on(expression)); context.trace.report(RETURN_NOT_ALLOWED.on(expression));
resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE);
} }
if (containingFunctionDescriptor != null) {
expectedType = getFunctionExpectedReturnType(containingFunctionDescriptor, (JetElement) containingFunction); expectedType = getFunctionExpectedReturnType(containingFunctionDescriptor, (JetElement) containingFunInfo.getSecond());
}
} }
else { else {
// Outside a function // Outside a function
@@ -483,7 +474,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
SimpleFunctionDescriptor functionDescriptor = context.trace.get(FUNCTION, labelTargetElement); SimpleFunctionDescriptor functionDescriptor = context.trace.get(FUNCTION, labelTargetElement);
if (functionDescriptor != null) { if (functionDescriptor != null) {
expectedType = getFunctionExpectedReturnType(functionDescriptor, labelTargetElement); expectedType = getFunctionExpectedReturnType(functionDescriptor, labelTargetElement);
if (functionDescriptor != containingFunctionDescriptor) { if (!InlineDescriptorUtils.checkNonLocalReturnUsage(functionDescriptor, expression, context.trace)) {
// Qualified, non-local // Qualified, non-local
context.trace.report(RETURN_NOT_ALLOWED.on(expression)); context.trace.report(RETURN_NOT_ALLOWED.on(expression));
resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE); resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE);
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE // !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -ONLY_LOCAL_RETURN
fun <T, U> Function1<T, U>.minusAssign(p: Function1<T, U>) {} fun <T, U> Function1<T, U>.minusAssign(p: Function1<T, U>) {}
@@ -0,0 +1,7 @@
inline fun <R> onlyLocal(p: () -> R) {
inlineAll(<!USAGE_IS_NOT_INLINABLE!>p<!>)
}
<!NOTHING_TO_INLINE!>inline fun <R> inlineAll(noinline p: () -> R)<!> {
p()
}
@@ -0,0 +1,24 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
val s = object {
val z = p()
fun a() {
p()
}
}
}
inline fun <R> inlineFun(p: () -> R) {
val s = object {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}
}
@@ -0,0 +1,49 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
val s = object {
val z = p();
{
doCall {
p()
}
}
fun a() {
doCall {
p()
}
p()
}
}
}
inline fun <R> inlineFun(p: () -> R) {
val s = object {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>();
{
doCall {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}
fun a() {
doCall {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}
}
inline fun <R> doCall(p: () -> R) {
p()
}
@@ -0,0 +1,43 @@
fun inlineCallExplicitError(): String {
inlineFun @lamba {
if (true) {
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@lamba 2<!>
}
1
}
return "x"
}
fun inlineCall(): String {
inlineFun @lamba {
(): Int ->
if (true) {
return@lamba 2
}
1
}
return "x"
}
inline fun inlineFun(s: () -> Int) {
s()
}
fun noInlineCall(): String {
noInline @lambda {
(): Int ->
if (true) {
return@lambda 2
}
1
}
return "x"
}
fun noInline(s: () -> Int) {
s()
}
@@ -0,0 +1,9 @@
import kotlin.InlineOption.*
inline fun <R> onlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
inlineAll(p)
}
inline fun <R> inlineAll(p: () -> R) {
p()
}
@@ -0,0 +1,17 @@
import kotlin.InlineOption.*
inline fun <R> inlineFunWithAnnotation(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) {
inlineFun {
p()
}
}
inline fun <R> inlineFun2(p: () -> R) {
inlineFun {
p()
}
}
inline fun <R> inlineFun(p: () -> R) {
p()
}
@@ -0,0 +1,31 @@
fun inlineCall(): String {
inlineFun {
if (true) {
return@inlineCall ""
}
1
}
return "x"
}
inline fun inlineFun(s: ()->Int) {
s()
}
fun noInlineCall(): String {
noInline {
if (true) {
<!RETURN_NOT_ALLOWED!>return@noInlineCall ""<!>
}
1
}
return "x"
}
fun noInline(s: ()->Int) {
s()
}
@@ -0,0 +1,16 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun a() {
val z = p()
}<!>
a()
}
inline fun <R> inlineFun(p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}<!>
a()
}
@@ -0,0 +1,43 @@
import Kind.EXT_RETURN
import Kind.GLOBAL_RETURN
enum class Kind {
LOCAL
EXT_RETURN
GLOBAL_RETURN
}
class Internal(val value: String)
class External(val value: String)
class Global(val value: String)
fun test1(intKind: Kind, extKind: Kind): Global {
var externalResult = doCall @ext {
() : External ->
val internalResult = doCall @int {
() : Internal ->
if (intKind == Kind.LOCAL) {
return@test1 Global("internal to global")
} else if (intKind == EXT_RETURN) {
return@ext External("internal to external")
}
return@int Internal("internal to local")
}
if (extKind == GLOBAL_RETURN || extKind == EXT_RETURN) {
return Global("external to global")
}
External(internalResult.value + " to local");
}
return Global(externalResult.value + " to exit")
}
public inline fun <R> doCall(block: ()-> R) : R {
return block()
}
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
{
p()
}()
}
inline fun <R> inlineFun(p: () -> R) {
{
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}()
}
@@ -0,0 +1,24 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A {
val z = p()
fun a() {
p()
}
}<!>
}
inline fun <R> inlineFun(p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}<!>
}
@@ -0,0 +1,24 @@
inline fun <R> doCall(p: () -> R) {
p()
}
inline fun <R> doCallInt(p: () -> R): R {
return p()
}
class A {
var result: Int = doCallInt { <!RETURN_NOT_ALLOWED!>return this<!> };
var field: Int
get() {
doCall { return 1 }
return 2
}
set(v: Int) {
doCall {
result = v / 2
return
}
result = v
}
}
@@ -0,0 +1,9 @@
import kotlin.InlineOption.*
inline fun <R> toOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) {
p()
}
inline fun <R> inlineAll(p: () -> R) {
toOnlyLocal(<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>)
}
@@ -4464,7 +4464,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
} }
@TestMetadata("compiler/testData/diagnostics/tests/inline") @TestMetadata("compiler/testData/diagnostics/tests/inline")
@InnerTestClasses({Inline.BinaryExpressions.class, Inline.NonPublicMember.class, Inline.Regressions.class, Inline.UnaryExpressions.class}) @InnerTestClasses({Inline.BinaryExpressions.class, Inline.NonLocalReturns.class, Inline.NonPublicMember.class, Inline.Regressions.class, Inline.UnaryExpressions.class})
public static class Inline extends AbstractJetDiagnosticsTest { public static class Inline extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInInline() throws Exception { public void testAllFilesPresentInInline() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/inline"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/inline"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -4500,6 +4500,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/inline/extensionOnFunction.kt"); doTest("compiler/testData/diagnostics/tests/inline/extensionOnFunction.kt");
} }
@TestMetadata("fromInlineToNoInline.kt")
public void testFromInlineToNoInline() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/fromInlineToNoInline.kt");
}
@TestMetadata("functions.kt") @TestMetadata("functions.kt")
public void testFunctions() throws Exception { public void testFunctions() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/functions.kt"); doTest("compiler/testData/diagnostics/tests/inline/functions.kt");
@@ -4628,6 +4633,74 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
} }
@TestMetadata("compiler/testData/diagnostics/tests/inline/nonLocalReturns")
public static class NonLocalReturns extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInNonLocalReturns() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/inline/nonLocalReturns"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("anonymousObjects.kt")
public void testAnonymousObjects() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/anonymousObjects.kt");
}
@TestMetadata("anonymousObjectsNested.kt")
public void testAnonymousObjectsNested() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/anonymousObjectsNested.kt");
}
@TestMetadata("explicitReturnType.kt")
public void testExplicitReturnType() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/explicitReturnType.kt");
}
@TestMetadata("fromOnlyLocal.kt")
public void testFromOnlyLocal() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/fromOnlyLocal.kt");
}
@TestMetadata("inlineLambda.kt")
public void testInlineLambda() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/inlineLambda.kt");
}
@TestMetadata("labeledReturn.kt")
public void testLabeledReturn() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/labeledReturn.kt");
}
@TestMetadata("localFun.kt")
public void testLocalFun() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/localFun.kt");
}
@TestMetadata("nestedNonLocals.kt")
public void testNestedNonLocals() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/nestedNonLocals.kt");
}
@TestMetadata("noInlineLambda.kt")
public void testNoInlineLambda() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/noInlineLambda.kt");
}
@TestMetadata("nonInlinedClass.kt")
public void testNonInlinedClass() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/nonInlinedClass.kt");
}
@TestMetadata("propertyAccessorsAndConstructor.kt")
public void testPropertyAccessorsAndConstructor() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/propertyAccessorsAndConstructor.kt");
}
@TestMetadata("toOnlyLocal.kt")
public void testToOnlyLocal() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/toOnlyLocal.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/inline/nonPublicMember") @TestMetadata("compiler/testData/diagnostics/tests/inline/nonPublicMember")
public static class NonPublicMember extends AbstractJetDiagnosticsTest { public static class NonPublicMember extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInNonPublicMember() throws Exception { public void testAllFilesPresentInNonPublicMember() throws Exception {
@@ -4706,6 +4779,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
TestSuite suite = new TestSuite("Inline"); TestSuite suite = new TestSuite("Inline");
suite.addTestSuite(Inline.class); suite.addTestSuite(Inline.class);
suite.addTestSuite(BinaryExpressions.class); suite.addTestSuite(BinaryExpressions.class);
suite.addTestSuite(NonLocalReturns.class);
suite.addTestSuite(NonPublicMember.class); suite.addTestSuite(NonPublicMember.class);
suite.addTestSuite(Regressions.class); suite.addTestSuite(Regressions.class);
suite.addTestSuite(UnaryExpressions.class); suite.addTestSuite(UnaryExpressions.class);
+8
View File
@@ -24,3 +24,11 @@ public enum class InlineStrategy {
AS_FUNCTION AS_FUNCTION
IN_PLACE IN_PLACE
} }
public annotation class inlineOptions(vararg val value: InlineOption)
public enum class InlineOption {
LOCAL_CONTINUE_AND_BREAK
ONLY_LOCAL_RETURN
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.types.lang;
public enum InlineOption {
LOCAL_CONTINUE_AND_BREAK,
ONLY_LOCAL_RETURN
}
@@ -23,9 +23,12 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations; import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.EnumValue; import org.jetbrains.jet.lang.resolve.constants.EnumValue;
import java.util.List;
public class InlineUtil { public class InlineUtil {
public static boolean hasNoinlineAnnotation(@NotNull CallableDescriptor valueParameterDescriptor) { public static boolean hasNoinlineAnnotation(@NotNull CallableDescriptor valueParameterDescriptor) {
@@ -68,6 +71,40 @@ public class InlineUtil {
return null; return null;
} }
public static boolean hasOnlyLocalContinueAndBreak(@NotNull ValueParameterDescriptor descriptor) {
return hasInlineOption(descriptor, InlineOption.LOCAL_CONTINUE_AND_BREAK);
}
public static boolean hasOnlyLocalReturn(@NotNull ValueParameterDescriptor descriptor) {
return hasInlineOption(descriptor, InlineOption.ONLY_LOCAL_RETURN);
}
private static boolean hasInlineOption(@NotNull ValueParameterDescriptor descriptor, @NotNull InlineOption option) {
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
ClassDescriptor annotationClass = builtIns.getInlineOptionsClassAnnotation();
AnnotationDescriptor optionsAnnotation = getAnnotation(descriptor.getAnnotations(), annotationClass);
if (optionsAnnotation != null) {
ValueParameterDescriptor parameterDescriptor = annotationClass.getConstructors().iterator().next().getValueParameters().get(0);
CompileTimeConstant<?> argument = optionsAnnotation.getValueArgument(parameterDescriptor);
if (argument == null) {
return false;
} else {
if (argument instanceof ArrayValue) {
List<CompileTimeConstant<?>> values = ((ArrayValue) argument).getValue();
for (CompileTimeConstant<?> value : values) {
if (value instanceof EnumValue) {
if (((EnumValue) value).getValue().getName().asString().equals(option.name())) {
return true;
}
}
}
}
}
}
return false;
}
} }
@@ -307,6 +307,11 @@ public class KotlinBuiltIns {
return getBuiltInClassByName("inline"); return getBuiltInClassByName("inline");
} }
@NotNull
public ClassDescriptor getInlineOptionsClassAnnotation() {
return getBuiltInClassByName("inlineOptions");
}
@NotNull @NotNull
public ClassDescriptor getTailRecursiveAnnotationClass() { public ClassDescriptor getTailRecursiveAnnotationClass() {
return getBuiltInClassByName("tailRecursive"); return getBuiltInClassByName("tailRecursive");