Control-Flow Analysis: Reuse pseudo-value information for unused expression analysis

This commit is contained in:
Alexey Sedunov
2014-07-25 14:52:15 +04:00
parent 2a19016d58
commit 9cbcabffa4
55 changed files with 239 additions and 190 deletions
@@ -31,6 +31,7 @@ import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableInitState;
import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState; import org.jetbrains.jet.lang.cfg.PseudocodeVariablesData.VariableUseState;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue; import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode; import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodePackage;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil; import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeUtil;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor;
@@ -50,6 +51,7 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
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.*; import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind; import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
@@ -121,10 +123,10 @@ public class JetFlowInformationProvider {
markUnusedVariables(); markUnusedVariables();
markUnusedLiteralsInBlock();
markStatements(); markStatements();
markUnusedExpressions();
markWhenWithoutElse(); markWhenWithoutElse();
} }
@@ -666,36 +668,28 @@ public class JetFlowInformationProvider {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// "Unused literals" in block // "Unused expressions" in block
public void markUnusedLiteralsInBlock() { public void markUnusedExpressions() {
final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap(); final Map<Instruction, DiagnosticFactory<?>> reportedDiagnosticMap = Maps.newHashMap();
PseudocodeTraverserPackage.traverse( PseudocodeTraverserPackage.traverse(
pseudocode, FORWARD, new FunctionVoid1<Instruction>() { pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1<Instruction>() {
@Override @Override
public void execute(@NotNull Instruction instruction) { public void execute(@NotNull Instruction instruction) {
if (!(instruction instanceof ReadValueInstruction || instruction instanceof MagicInstruction)) return; if (!(instruction instanceof JetElementInstruction)) return;
JetElement element = ((JetElementInstruction) instruction).getElement(); JetElement element = ((JetElementInstruction)instruction).getElement();
if (!(element instanceof JetFunctionLiteralExpression if (!(element instanceof JetExpression)) return;
|| element instanceof JetConstantExpression
|| element instanceof JetStringTemplateExpression
|| element instanceof JetSimpleNameExpression)) return;
if (!(element instanceof JetStringTemplateExpression || instruction instanceof ReadValueInstruction)) return; if (BindingContextUtilPackage.isUsedAsStatement((JetExpression) element, trace.getBindingContext())
&& PseudocodePackage.getSideEffectFree(instruction)) {
VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap); VariableContext ctxt = new VariableContext(instruction, reportedDiagnosticMap);
report(
PsiElement parent = element.getParent(); element instanceof JetFunctionLiteralExpression
if (parent instanceof JetBlockExpression) { ? Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element)
if (!JetPsiUtil.isImplicitlyUsed(element)) { : Errors.UNUSED_EXPRESSION.on(element),
if (element instanceof JetFunctionLiteralExpression) { ctxt
report(Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element), ctxt); );
}
else {
report(Errors.UNUSED_EXPRESSION.on(element), ctxt);
}
}
} }
} }
} }
@@ -64,4 +64,6 @@ public interface Pseudocode {
@NotNull @NotNull
List<? extends Instruction> getUsages(@Nullable PseudoValue value); List<? extends Instruction> getUsages(@Nullable PseudoValue value);
boolean isSideEffectFree(@NotNull Instruction instruction);
} }
@@ -18,13 +18,16 @@ package org.jetbrains.jet.lang.cfg.pseudocode;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.intellij.util.containers.BidirectionalMap; import com.intellij.util.containers.BidirectionalMap;
import jet.runtime.typeinfo.JetValueParameter;
import kotlin.Function0; import kotlin.Function0;
import kotlin.Function1;
import kotlin.KotlinPackage; import kotlin.KotlinPackage;
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.cfg.Label; import org.jetbrains.jet.lang.cfg.Label;
import org.jetbrains.jet.lang.cfg.LoopInfo; import org.jetbrains.jet.lang.cfg.LoopInfo;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.InstructionWithValue;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MergeInstruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MergeInstruction;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction; import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction;
@@ -95,6 +98,7 @@ public class PseudocodeImpl implements Pseudocode {
private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap(); private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap();
private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap(); private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap();
private final Set<Instruction> sideEffectFree = Sets.newHashSet();
private Pseudocode parent = null; private Pseudocode parent = null;
private Set<LocalFunctionDeclarationInstruction> localDeclarations = null; private Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
@@ -239,6 +243,9 @@ public class PseudocodeImpl implements Pseudocode {
addValueUsage(mergedValue, instruction); addValueUsage(mergedValue, instruction);
} }
} }
if (PseudocodePackage.calcSideEffectFree(instruction)) {
sideEffectFree.add(instruction);
}
} }
/*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) { /*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) {
@@ -283,6 +290,11 @@ public class PseudocodeImpl implements Pseudocode {
return result != null ? result : Collections.<Instruction>emptyList(); return result != null ? result : Collections.<Instruction>emptyList();
} }
@Override
public boolean isSideEffectFree(@NotNull Instruction instruction) {
return sideEffectFree.contains(instruction);
}
/*package*/ void bindElementToValue(@NotNull JetElement element, @NotNull PseudoValue value) { /*package*/ void bindElementToValue(@NotNull JetElement element, @NotNull PseudoValue value) {
elementsToValues.put(element, value); elementsToValues.put(element, value);
} }
@@ -131,25 +131,25 @@ public class MagicInstruction(
} }
} }
public enum class MagicKind { public enum class MagicKind(val sideEffectFree: Boolean = false) {
// builtin operations // builtin operations
STRING_TEMPLATE STRING_TEMPLATE: MagicKind(true)
AND AND: MagicKind(true)
OR OR: MagicKind(true)
NOT_NULL_ASSERTION NOT_NULL_ASSERTION: MagicKind()
EQUALS_IN_WHEN_CONDITION EQUALS_IN_WHEN_CONDITION: MagicKind()
IS IS: MagicKind()
CAST CAST: MagicKind()
CALLABLE_REFERENCE CALLABLE_REFERENCE: MagicKind(true)
// implicit operations // implicit operations
LOOP_RANGE_ITERATION LOOP_RANGE_ITERATION: MagicKind()
IMPLICIT_RECEIVER IMPLICIT_RECEIVER: MagicKind()
VALUE_CONSUMER VALUE_CONSUMER: MagicKind()
// unrecognized operations // unrecognized operations
UNRESOLVED_CALL UNRESOLVED_CALL: MagicKind()
UNSUPPORTED_ELEMENT UNSUPPORTED_ELEMENT: MagicKind()
UNRECOGNIZED_WRITE_RHS UNRECOGNIZED_WRITE_RHS: MagicKind()
FAKE_INITIALIZER FAKE_INITIALIZER: MagicKind()
} }
// Merges values produced by alternative control-flow paths (such as 'if' branches) // Merges values produced by alternative control-flow paths (such as 'if' branches)
@@ -16,7 +16,6 @@
package org.jetbrains.jet.lang.cfg.pseudocode package org.jetbrains.jet.lang.cfg.pseudocode
import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.* import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.* import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.* import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*
@@ -31,7 +30,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.jet.lang.resolve.OverridingUtil import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor
fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? { fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? {
val callableDescriptor = resolvedCall.getResultingDescriptor() val callableDescriptor = resolvedCall.getResultingDescriptor()
@@ -135,3 +134,31 @@ public fun Instruction.getPrimaryDeclarationDescriptorIfAny(bindingContext: Bind
else -> PseudocodeUtil.extractVariableDescriptorIfAny(this, false, bindingContext) else -> PseudocodeUtil.extractVariableDescriptorIfAny(this, false, bindingContext)
} }
} }
public val Instruction.sideEffectFree: Boolean
get() = owner.isSideEffectFree(this)
private fun Instruction.calcSideEffectFree(): Boolean {
if (this !is InstructionWithValue) return false
if (!inputValues.all { it.createdAt?.sideEffectFree ?: false }) return false
return when (this) {
is ReadValueInstruction -> target.let {
when (it) {
is AccessTarget.Call -> when (it.resolvedCall.getResultingDescriptor()) {
is LocalVariableDescriptor, is ValueParameterDescriptor, is ReceiverParameterDescriptor -> true
else -> false
}
else -> when (element) {
is JetConstantExpression, is JetFunctionLiteralExpression, is JetStringTemplateExpression -> true
else -> false
}
}
}
is MagicInstruction -> kind.sideEffectFree
else -> false
}
}
@@ -282,29 +282,6 @@ public class JetPsiUtil {
return null; return null;
} }
public static boolean isImplicitlyUsed(@NotNull JetElement element) {
PsiElement parent = element.getParent();
if (!(parent instanceof JetBlockExpression)) return true;
JetBlockExpression block = (JetBlockExpression) parent;
List<JetElement> statements = block.getStatements();
if (statements.get(statements.size() - 1) == element) {
JetExpression expression = getDirectParentOfTypeForBlock(block, JetIfExpression.class);
if (expression == null) {
expression = getDirectParentOfTypeForBlock(block, JetWhenExpression.class);
}
if (expression == null) {
expression = getDirectParentOfTypeForBlock(block, JetFunctionLiteral.class);
}
if (expression == null) {
expression = getDirectParentOfTypeForBlock(block, JetTryExpression.class);
}
if (expression != null) {
return isImplicitlyUsed(expression);
}
}
return false;
}
public static void deleteClass(@NotNull JetClassOrObject clazz) { public static void deleteClass(@NotNull JetClassOrObject clazz) {
CheckUtil.checkWritable(clazz); CheckUtil.checkWritable(clazz);
JetFile file = clazz.getContainingJetFile(); JetFile file = clazz.getContainingJetFile();
@@ -265,7 +265,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
else { else {
facade.getTypeInfo(body, context.replaceScope(context.scope)); facade.getTypeInfo(body, context.replaceScope(context.scope));
} }
context.trace.report(UNUSED_FUNCTION_LITERAL.on(function));
} }
else if (body != null) { else if (body != null) {
WritableScope writableScope = newWritableScopeImpl(context, "do..while body scope"); WritableScope writableScope = newWritableScopeImpl(context, "do..while body scope");
@@ -25,7 +25,7 @@ fun f(): Unit {
<!TYPE_MISMATCH!>x<!> in 1..2 <!TYPE_MISMATCH!>x<!> in 1..2
val y : Boolean? = true val y : Boolean? = true
false || <!TYPE_MISMATCH!>y<!> <!UNUSED_EXPRESSION!>false || <!TYPE_MISMATCH!>y<!><!>
<!TYPE_MISMATCH!>y<!> && true <!UNUSED_EXPRESSION!><!TYPE_MISMATCH!>y<!> && true<!>
<!TYPE_MISMATCH!>y<!> && <!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!> <!UNUSED_EXPRESSION!><!TYPE_MISMATCH!>y<!> && <!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!><!>
} }
@@ -47,7 +47,7 @@ fun blockReturnValueTypeMatch() : Int {return 1}
fun blockReturnValueTypeMismatchUnit() : Int {return <!TYPE_MISMATCH!>Unit<!>} fun blockReturnValueTypeMismatchUnit() : Int {return <!TYPE_MISMATCH!>Unit<!>}
fun blockAndAndMismatch() : Int { fun blockAndAndMismatch() : Int {
true && false <!UNUSED_EXPRESSION!>true && false<!>
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockAndAndMismatch1() : Int { fun blockAndAndMismatch1() : Int {
return <!TYPE_MISMATCH!>true && false<!> return <!TYPE_MISMATCH!>true && false<!>
@@ -57,7 +57,7 @@ fun blockAndAndMismatch2() : Int {
} }
fun blockAndAndMismatch3() : Int { fun blockAndAndMismatch3() : Int {
true || false <!UNUSED_EXPRESSION!>true || false<!>
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockAndAndMismatch4() : Int { fun blockAndAndMismatch4() : Int {
return <!TYPE_MISMATCH!>true || false<!> return <!TYPE_MISMATCH!>true || false<!>
@@ -91,18 +91,18 @@ fun blockReturnValueTypeMatch6() : Int {
} }
fun blockReturnValueTypeMatch7() : Int { fun blockReturnValueTypeMatch7() : Int {
if (1 > 2) if (1 > 2)
1.0 <!UNUSED_EXPRESSION!>1.0<!>
else 2.0 else <!UNUSED_EXPRESSION!>2.0<!>
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch8() : Int { fun blockReturnValueTypeMatch8() : Int {
if (1 > 2) if (1 > 2)
1.0 <!UNUSED_EXPRESSION!>1.0<!>
else 2.0 else <!UNUSED_EXPRESSION!>2.0<!>
return 1 return 1
} }
fun blockReturnValueTypeMatch9() : Int { fun blockReturnValueTypeMatch9() : Int {
if (1 > 2) if (1 > 2)
1.0 <!UNUSED_EXPRESSION!>1.0<!>
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch10() : Int { fun blockReturnValueTypeMatch10() : Int {
return <!TYPE_MISMATCH!>if (1 > 2) return <!TYPE_MISMATCH!>if (1 > 2)
@@ -110,7 +110,7 @@ fun blockReturnValueTypeMatch10() : Int {
} }
fun blockReturnValueTypeMatch11() : Int { fun blockReturnValueTypeMatch11() : Int {
if (1 > 2) if (1 > 2)
else 1.0 else <!UNUSED_EXPRESSION!>1.0<!>
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!> <!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
fun blockReturnValueTypeMatch12() : Int { fun blockReturnValueTypeMatch12() : Int {
if (1 > 2) if (1 > 2)
@@ -12,11 +12,11 @@ fun demo() {
<!UNUSED_EXPRESSION!>"$.$.asdf$\t"<!> <!UNUSED_EXPRESSION!>"$.$.asdf$\t"<!>
<!UNUSED_EXPRESSION!>"asd\$"<!> <!UNUSED_EXPRESSION!>"asd\$"<!>
<!UNUSED_EXPRESSION!>"asd$a<!ILLEGAL_ESCAPE!>\x<!>"<!> <!UNUSED_EXPRESSION!>"asd$a<!ILLEGAL_ESCAPE!>\x<!>"<!>
<!UNUSED_EXPRESSION!>"asd$a$asd$ $<!UNRESOLVED_REFERENCE!>xxx<!>"<!> "asd$a$asd$ $<!UNRESOLVED_REFERENCE!>xxx<!>"
<!UNUSED_EXPRESSION!>"fosdfasdo${1 + bar + 100}}sdsdfgdsfsdf"<!> "fosdfasdo${1 + bar + 100}}sdsdfgdsfsdf"
<!UNUSED_EXPRESSION!>"foo${bar + map {foo}}sdfsdf"<!> "foo${bar + map {foo}}sdfsdf"
<!UNUSED_EXPRESSION!>"foo${bar + map { "foo" }}sdfsdf"<!> "foo${bar + map { "foo" }}sdfsdf"
<!UNUSED_EXPRESSION!>"foo${bar + map { "foo${bar + map {
"foo$sdf${ buzz{}}" }}sdfsdf"<!> "foo$sdf${ buzz{}}" }}sdfsdf"
<!UNUSED_EXPRESSION!>"a<!ILLEGAL_ESCAPE!>\u<!> <!ILLEGAL_ESCAPE!>\u<!>0 <!ILLEGAL_ESCAPE!>\u<!>00 <!ILLEGAL_ESCAPE!>\u<!>000 \u0000 \u0AaA <!ILLEGAL_ESCAPE!>\u<!>0AAz.length( ) + \u0022b"<!> <!UNUSED_EXPRESSION!>"a<!ILLEGAL_ESCAPE!>\u<!> <!ILLEGAL_ESCAPE!>\u<!>0 <!ILLEGAL_ESCAPE!>\u<!>00 <!ILLEGAL_ESCAPE!>\u<!>000 \u0000 \u0AaA <!ILLEGAL_ESCAPE!>\u<!>0AAz.length( ) + \u0022b"<!>
} }
@@ -3,7 +3,7 @@ fun testIf() {
} }
fun testIf1(b: Boolean) { fun testIf1(b: Boolean) {
if (b) todo() else 1 if (b) todo() else <!UNUSED_EXPRESSION!>1<!>
bar() bar()
} }
@@ -5,7 +5,7 @@ package kt1075
fun foo(b: String) { fun foo(b: String) {
if (<!TYPE_MISMATCH!>b<!> in 1..10) {} //type mismatch if (<!TYPE_MISMATCH!>b<!> in 1..10) {} //type mismatch
when (b) { when (b) {
<!TYPE_MISMATCH_IN_RANGE!>in<!> 1..10 -> 1 //no type mismatch, but it should be here <!TYPE_MISMATCH_IN_RANGE!>in<!> 1..10 -> <!UNUSED_EXPRESSION!>1<!> //no type mismatch, but it should be here
else -> 2 else -> <!UNUSED_EXPRESSION!>2<!>
} }
} }
@@ -15,7 +15,7 @@ fun foo() : Int {
fun fff(): Int { fun fff(): Int {
var d = 3 var d = 3
when(d) { when(d) {
4 -> 21 4 -> <!UNUSED_EXPRESSION!>21<!>
return 2 -> <!UNREACHABLE_CODE!>return 47<!> return 2 -> <!UNREACHABLE_CODE!>return 47<!>
<!UNREACHABLE_CODE!>bar() -> 45<!> <!UNREACHABLE_CODE!>bar() -> 45<!>
<!UNREACHABLE_CODE!>444 -> true<!> <!UNREACHABLE_CODE!>444 -> true<!>
@@ -21,7 +21,7 @@ fun test1(t : Pair<Int, Int>) : Int {
} }
//more tests //more tests
fun t1(x: Int) = when(x) { fun t1(x: Int) = when(<!UNUSED_EXPRESSION!>x<!>) {
else -> 1 else -> 1
} }
@@ -4,7 +4,7 @@ class BinOp(val operator : String) : Expr
fun test(e : Expr) { fun test(e : Expr) {
if (e is BinOp) { if (e is BinOp) {
when (<!DEBUG_INFO_AUTOCAST!>e<!>.operator) { when (<!DEBUG_INFO_AUTOCAST!>e<!>.operator) {
else -> 0 else -> <!UNUSED_EXPRESSION!>0<!>
} }
} }
} }
@@ -2,13 +2,10 @@
fun foo(s: String?) { fun foo(s: String?) {
when { when {
s == null -> 1 s == null -> <!UNUSED_EXPRESSION!>1<!>
<!DEBUG_INFO_AUTOCAST!>s<!>.foo() -> 2 <!DEBUG_INFO_AUTOCAST!>s<!>.foo() -> <!UNUSED_EXPRESSION!>2<!>
else -> 3 else -> <!UNUSED_EXPRESSION!>3<!>
} }
} }
fun String.foo() = true fun String.foo() = true
@@ -71,5 +71,5 @@ import outer.*
val c = Command() val c = Command()
c<!UNNECESSARY_SAFE_CALL!>?.<!>equals2(null) c<!UNNECESSARY_SAFE_CALL!>?.<!>equals2(null)
if (command == null) 1 if (command == null) <!UNUSED_EXPRESSION!>1<!>
} }
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
package d package d
trait A<T> trait A<T>
@@ -1,3 +1,3 @@
fun test(a: Any) { fun test(a: Any) {
when (a)<!SYNTAX!><!> when (<!UNUSED_EXPRESSION!>a<!>)<!SYNTAX!><!>
} }
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
package d package d
fun foo(a : IntArray) { fun foo(a : IntArray) {
@@ -2,21 +2,21 @@ inline fun inlineFunWithInvoke(s: (p: Int) -> Unit) {
(s)(11) (s)(11)
(s).invoke(11) (s).invoke(11)
(s) invoke 11 (s) invoke 11
(<!USAGE_IS_NOT_INLINABLE!>s<!>) (<!USAGE_IS_NOT_INLINABLE, UNUSED_EXPRESSION!>s<!>)
} }
inline fun Function1<Int, Unit>.inlineExt() { inline fun Function1<Int, Unit>.inlineExt() {
(this).invoke(11) (this).invoke(11)
(this) invoke 11 (this) invoke 11
(this)(11) (this)(11)
(<!USAGE_IS_NOT_INLINABLE!>this<!>) (<!USAGE_IS_NOT_INLINABLE, UNUSED_EXPRESSION!>this<!>)
} }
inline fun inlineFunWithInvoke2(s: (p: Int) -> Unit) { inline fun inlineFunWithInvoke2(s: (p: Int) -> Unit) {
(((s)))(11) (((s)))(11)
(((s))).invoke(11) (((s))).invoke(11)
(((s))) invoke 11 (((s))) invoke 11
(((<!USAGE_IS_NOT_INLINABLE!>s<!>))) (((<!USAGE_IS_NOT_INLINABLE, UNUSED_EXPRESSION!>s<!>)))
} }
inline fun propagation(s: (p: Int) -> Unit) { inline fun propagation(s: (p: Int) -> Unit) {
@@ -51,7 +51,7 @@ inline fun Function1<Int, Unit>.inlineExt() {
this invoke 11 this invoke 11
this(11) this(11)
<!USAGE_IS_NOT_INLINABLE!>this<!> <!USAGE_IS_NOT_INLINABLE, UNUSED_EXPRESSION!>this<!>
11 11
} }
@@ -2,7 +2,7 @@ inline fun inlineFunWithInvoke(s: (p: Int) -> Unit) {
(s: (p: Int) -> Unit)(11) (s: (p: Int) -> Unit)(11)
(s: (p: Int) -> Unit).invoke(11) (s: (p: Int) -> Unit).invoke(11)
(s: (p: Int) -> Unit) invoke 11 (s: (p: Int) -> Unit) invoke 11
(<!USAGE_IS_NOT_INLINABLE!>s<!>) (<!USAGE_IS_NOT_INLINABLE, UNUSED_EXPRESSION!>s<!>)
} }
inline fun Function1<Int, Unit>.inlineExt() { inline fun Function1<Int, Unit>.inlineExt() {
@@ -1,7 +1,7 @@
//FILE: foo.kt //FILE: foo.kt
fun main(args: Array<String>) { fun main(args: Array<String>) {
val c: Type val c: Type
when (<!UNINITIALIZED_VARIABLE!>c<!>) { when (<!UNINITIALIZED_VARIABLE, UNUSED_EXPRESSION!>c<!>) {
} }
} }
@@ -1,5 +1,5 @@
fun nonlocals(b : Boolean) { fun nonlocals(b : Boolean) {
@a{(): Int -> @a<!UNUSED_FUNCTION_LITERAL!>{(): Int ->
fun foo() { fun foo() {
if (b) { if (b) {
<!RETURN_NOT_ALLOWED!>return@a 1<!> // The label must be resolved, but an error should be reported for a non-local return <!RETURN_NOT_ALLOWED!>return@a 1<!> // The label must be resolved, but an error should be reported for a non-local return
@@ -7,6 +7,5 @@ fun nonlocals(b : Boolean) {
} }
return@a 5 return@a 5
} }<!>
} }
@@ -15,11 +15,11 @@ fun B.b() {
} }
fun test() { fun test() {
@b { B.() -> @b <!UNUSED_FUNCTION_LITERAL!>{ B.() ->
object : A { object : A {
override fun foo() { override fun foo() {
this@b.bar() this@b.bar()
} }
} }
} }<!>
} }
@@ -6,5 +6,5 @@ fun Any.equals(other : Any?) : Boolean = this === other
fun main(args: Array<String>) { fun main(args: Array<String>) {
val command = parse("") val command = parse("")
if (command == null) 1 // error on this line, but must be OK if (command == null) <!UNUSED_EXPRESSION!>1<!> // error on this line, but must be OK
} }
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
fun <T> T.mustBe(t : T) { fun <T> T.mustBe(t : T) {
<!UNUSED_EXPRESSION!>"$this must be$<!SYNTAX!>as<!>$t"<!> "$this must be$<!SYNTAX!>as<!>$t"
} }
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
fun foo(i: Int) { fun foo(i: Int) {
<!FUNCTION_EXPECTED!>i<!>() <!FUNCTION_EXPECTED!>i<!>()
<!FUNCTION_EXPECTED!>1<!>() <!FUNCTION_EXPECTED!>1<!>()
@@ -1,9 +1,9 @@
// FILE: f.kt // FILE: f.kt
class A() { class A() {
fun foo() : Unit { fun foo() : Unit {
this@A <!UNUSED_EXPRESSION!>this@A<!>
this<!UNRESOLVED_REFERENCE!>@a<!> this<!UNRESOLVED_REFERENCE!>@a<!>
this <!UNUSED_EXPRESSION!>this<!>
} }
val x = this@A.foo() val x = this@A.foo()
@@ -5,7 +5,7 @@ fun notAnExpression() {
if (<!SUPER_IS_NOT_AN_EXPRESSION!>super<!>) {} else {} // not an expression if (<!SUPER_IS_NOT_AN_EXPRESSION!>super<!>) {} else {} // not an expression
val <!UNUSED_VARIABLE!>x<!> = <!SUPER_IS_NOT_AN_EXPRESSION!>super<!> // not an expression val <!UNUSED_VARIABLE!>x<!> = <!SUPER_IS_NOT_AN_EXPRESSION!>super<!> // not an expression
when (1) { when (1) {
<!SUPER_IS_NOT_AN_EXPRESSION!>super<!> -> 1 // not an expression <!SUPER_IS_NOT_AN_EXPRESSION!>super<!> -> <!UNUSED_EXPRESSION!>1<!> // not an expression
else -> {} else -> {}
} }
@@ -4,15 +4,15 @@ fun foo() : Int {
val s = "" val s = ""
val x = 1 val x = 1
when (x) { when (x) {
is <!INCOMPATIBLE_TYPES!>String<!> -> 1 is <!INCOMPATIBLE_TYPES!>String<!> -> <!UNUSED_EXPRESSION!>1<!>
!is Int -> 1 !is Int -> <!UNUSED_EXPRESSION!>1<!>
is Any<!USELESS_NULLABLE_CHECK!>?<!> -> 1 is Any<!USELESS_NULLABLE_CHECK!>?<!> -> <!UNUSED_EXPRESSION!>1<!>
<!INCOMPATIBLE_TYPES!>s<!> -> 1 <!INCOMPATIBLE_TYPES!>s<!> -> <!UNUSED_EXPRESSION!>1<!>
1 -> 1 1 -> <!UNUSED_EXPRESSION!>1<!>
1 + <!UNRESOLVED_REFERENCE!>a<!> -> 1 1 + <!UNRESOLVED_REFERENCE!>a<!> -> <!UNUSED_EXPRESSION!>1<!>
in 1..<!UNRESOLVED_REFERENCE!>a<!> -> 1 in 1..<!UNRESOLVED_REFERENCE!>a<!> -> <!UNUSED_EXPRESSION!>1<!>
!in 1..<!UNRESOLVED_REFERENCE!>a<!> -> 1 !in 1..<!UNRESOLVED_REFERENCE!>a<!> -> <!UNUSED_EXPRESSION!>1<!>
else -> 1 else -> <!UNUSED_EXPRESSION!>1<!>
} }
return 0 return 0
@@ -25,20 +25,20 @@ fun test() {
val s = ""; val s = "";
when (x) { when (x) {
<!INCOMPATIBLE_TYPES!>s<!> -> 1 <!INCOMPATIBLE_TYPES!>s<!> -> <!UNUSED_EXPRESSION!>1<!>
<!INCOMPATIBLE_TYPES!>""<!> -> 1 <!INCOMPATIBLE_TYPES!>""<!> -> <!UNUSED_EXPRESSION!>1<!>
x -> 1 x -> <!UNUSED_EXPRESSION!>1<!>
1 -> 1 1 -> <!UNUSED_EXPRESSION!>1<!>
} }
val z = 1 val z = 1
when (z) { when (z) {
<!ELSE_MISPLACED_IN_WHEN!>else<!> -> 1 <!ELSE_MISPLACED_IN_WHEN!>else<!> -> <!UNUSED_EXPRESSION!>1<!>
<!UNREACHABLE_CODE!>1 -> 2<!> <!UNREACHABLE_CODE!>1 -> 2<!>
} }
when (z) { when (<!UNUSED_EXPRESSION!>z<!>) {
else -> 1 else -> <!UNUSED_EXPRESSION!>1<!>
} }
} }
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
trait A trait A
abstract class B abstract class B
annotation class C annotation class C
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
fun foo(x: Int, <!UNUSED_PARAMETER!>y<!>: Any) = x fun foo(x: Int, <!UNUSED_PARAMETER!>y<!>: Any) = x
fun foo(<!UNUSED_PARAMETER!>x<!>: Any, y: Int) = y fun foo(<!UNUSED_PARAMETER!>x<!>: Any, y: Int) = y
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
// FILE: a.kt // FILE: a.kt
package first package first
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
class A { class A {
fun Int.extInt() = 42 fun Int.extInt() = 42
fun A.extA(x: String) = x fun A.extA(x: String) = x
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
import A.Inner import A.Inner
class A { class A {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
import kotlin.reflect.KMemberFunction0 import kotlin.reflect.KMemberFunction0
class A { class A {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
import kotlin.reflect.KMemberFunction0 import kotlin.reflect.KMemberFunction0
class A { class A {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
import kotlin.reflect.KMemberFunction0 import kotlin.reflect.KMemberFunction0
class A { class A {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
import kotlin.reflect.KFunction0 import kotlin.reflect.KFunction0
class A { class A {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
import kotlin.reflect.KFunction0 import kotlin.reflect.KFunction0
class A { class A {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
fun bar() = 42 fun bar() = 42
fun main() { fun main() {
@@ -1,3 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
class A class A
fun main() { fun main() {
@@ -0,0 +1,23 @@
fun foo() {
}
class A {
fun foo() {
}
class B
}
fun A.bar() {
}
fun test() {
<!UNUSED_EXPRESSION!>::foo<!>
<!UNUSED_EXPRESSION!>::A<!>
<!UNUSED_EXPRESSION!>A::B<!>
<!UNUSED_EXPRESSION!>A::foo<!>
<!UNUSED_EXPRESSION!>A::bar<!>
}
@@ -111,6 +111,11 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/testsWithStdLib/callableReference"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/testsWithStdLib/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("unused.kt")
public void testUnused() throws Exception {
doTest("compiler/testData/diagnostics/testsWithStdLib/callableReference/unused.kt");
}
@TestMetadata("compiler/testData/diagnostics/testsWithStdLib/callableReference/function") @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/callableReference/function")
public static class Function extends AbstractJetDiagnosticsTestWithStdLib { public static class Function extends AbstractJetDiagnosticsTestWithStdLib {
@TestMetadata("abstractClassConstructors.kt") @TestMetadata("abstractClassConstructors.kt")
@@ -25,7 +25,7 @@ fun f(): Unit {
<error>x</error> in 1..2 <error>x</error> in 1..2
val y : Boolean? = true val y : Boolean? = true
false || <error>y</error> <warning>false || <error>y</error></warning>
<error>y</error> && true <warning><error>y</error> && true</warning>
<error>y</error> && <error>1</error> <warning><error>y</error> && <error>1</error></warning>
} }
+1 -1
View File
@@ -66,5 +66,5 @@ fun Int.foo() = this
val c = Command() val c = Command()
c<warning>?.</warning>equals2(null) c<warning>?.</warning>equals2(null)
if (command == null) 1 if (command == null) <warning>1</warning>
} }
+8 -8
View File
@@ -42,7 +42,7 @@ fun blockReturnValueTypeMatch() : Int {return 1}
fun blockReturnValueTypeMismatchUnit() : Int {return <error>Unit</error>} fun blockReturnValueTypeMismatchUnit() : Int {return <error>Unit</error>}
fun blockAndAndMismatch() : Int { fun blockAndAndMismatch() : Int {
true && false <warning>true && false</warning>
<error>}</error> <error>}</error>
fun blockAndAndMismatch1() : Int { fun blockAndAndMismatch1() : Int {
return <error>true && false</error> return <error>true && false</error>
@@ -52,7 +52,7 @@ fun blockAndAndMismatch2() : Int {
} }
fun blockAndAndMismatch3() : Int { fun blockAndAndMismatch3() : Int {
true || false <warning>true || false</warning>
<error>}</error> <error>}</error>
fun blockAndAndMismatch4() : Int { fun blockAndAndMismatch4() : Int {
return <error>true || false</error> return <error>true || false</error>
@@ -86,18 +86,18 @@ fun blockReturnValueTypeMatch6() : Int {
} }
fun blockReturnValueTypeMatch7() : Int { fun blockReturnValueTypeMatch7() : Int {
if (1 > 2) if (1 > 2)
1.0 <warning>1.0</warning>
else 2.0 else <warning>2.0</warning>
<error>}</error> <error>}</error>
fun blockReturnValueTypeMatch8() : Int { fun blockReturnValueTypeMatch8() : Int {
if (1 > 2) if (1 > 2)
1.0 <warning>1.0</warning>
else 2.0 else <warning>2.0</warning>
return 1 return 1
} }
fun blockReturnValueTypeMatch9() : Int { fun blockReturnValueTypeMatch9() : Int {
if (1 > 2) if (1 > 2)
1.0 <warning>1.0</warning>
<error>}</error> <error>}</error>
fun blockReturnValueTypeMatch10() : Int { fun blockReturnValueTypeMatch10() : Int {
return <error>if (1 > 2) return <error>if (1 > 2)
@@ -105,7 +105,7 @@ fun blockReturnValueTypeMatch10() : Int {
} }
fun blockReturnValueTypeMatch11() : Int { fun blockReturnValueTypeMatch11() : Int {
if (1 > 2) if (1 > 2)
else 1.0 else <warning>1.0</warning>
<error>}</error> <error>}</error>
fun blockReturnValueTypeMatch12() : Int { fun blockReturnValueTypeMatch12() : Int {
if (1 > 2) if (1 > 2)
+2 -2
View File
@@ -6,9 +6,9 @@ class Dup {
class A() { class A() {
fun foo() : Unit { fun foo() : Unit {
this@A <warning>this@A</warning>
this<error>@a</error> this<error>@a</error>
this <warning>this</warning>
} }
val x = this@A.foo() val x = this@A.foo()
+17 -17
View File
@@ -4,15 +4,15 @@ fun foo() : Int {
val s = "" val s = ""
val x = 1 val x = 1
when (x) { when (x) {
is <error>String</error> -> 1 is <error>String</error> -> <warning>1</warning>
!is Int -> 1 !is Int -> <warning>1</warning>
is Any<warning>?</warning> -> 1 is Any<warning>?</warning> -> <warning>1</warning>
<error>s</error> -> 1 <error>s</error> -> <warning>1</warning>
1 -> 1 1 -> <warning>1</warning>
1 + <error>a</error> -> 1 1 + <error>a</error> -> <warning>1</warning>
in 1..<error>a</error> -> 1 in 1..<error>a</error> -> <warning>1</warning>
!in 1..<error>a</error> -> 1 !in 1..<error>a</error> -> <warning>1</warning>
else -> 1 else -> <warning>1</warning>
} }
return 0 return 0
@@ -25,21 +25,21 @@ fun test() {
val s = ""; val s = "";
when (x) { when (x) {
<error>s</error> -> 1 <error>s</error> -> <warning>1</warning>
<error>""</error> -> 1 <error>""</error> -> <warning>1</warning>
x -> 1 x -> <warning>1</warning>
1 -> 1 1 -> <warning>1</warning>
else -> 1 else -> <warning>1</warning>
} }
val z = 1 val z = 1
when (z) { when (z) {
<error>else</error> -> 1 <error>else</error> -> <warning>1</warning>
<warning>1 -> 2</warning> <warning>1 -> 2</warning>
} }
when (z) { when (<warning>z</warning>) {
else -> 1 else -> <warning>1</warning>
} }
} }
+1 -1
View File
@@ -6,5 +6,5 @@ fun Any.equals(other : Any?) : Boolean = this === other
fun main(args: Array<String>) { fun main(args: Array<String>) {
val command = parse("") val command = parse("")
if (command == null) 1 // error on this line, but must be OK if (command == null) <warning>1</warning> // error on this line, but must be OK
} }
@@ -1,8 +1,7 @@
// "Suppress 'UNUSED_EXPRESSION' for statement " "true" // "Suppress 'UNUSED_EXPRESSION' for statement " "true"
fun foo() { fun foo() {
val a = 1
[suppress("UNUSED_EXPRESSION")] [suppress("UNUSED_EXPRESSION")]
a a
} }
val a = 1
@@ -1,7 +1,6 @@
// "Suppress 'UNUSED_EXPRESSION' for statement " "true" // "Suppress 'UNUSED_EXPRESSION' for statement " "true"
fun foo() { fun foo() {
val a = 1
a a
} }
val a = 1