New diagnostic for non-local return annotation
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
+2
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+92
-59
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-23
@@ -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<!>()
|
||||||
|
}
|
||||||
|
}<!>
|
||||||
|
}
|
||||||
+24
@@ -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);
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user