KT-1175 Mark unused literals

This commit is contained in:
svtk
2012-01-27 18:28:54 +04:00
parent ca1abed3a9
commit 11ad28812d
27 changed files with 183 additions and 89 deletions
@@ -82,7 +82,7 @@ public class JetControlFlowProcessor {
}
@Override
public void visitWhenConditionExpression(JetWhenConditionWithExpression condition) {
public void visitWhenConditionWithExpression(JetWhenConditionWithExpression condition) {
JetExpressionPattern pattern = condition.getPattern();
if (pattern != null) {
pattern.accept(patternVisitor);
@@ -641,6 +641,39 @@ public class JetFlowInformationProvider {
}
}
////////////////////////////////////////////////////////////////////////////////
// "Unused literals" in block
public void markUnusedLiteralsInBlock(@NotNull JetElement subroutine) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
JetControlFlowGraphTraverser<Void> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, true);
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
@Override
public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
if (!(instruction instanceof ReadValueInstruction)) return;
JetElement element = ((ReadValueInstruction) instruction).getElement();
if (!(element instanceof JetFunctionLiteralExpression
|| element instanceof JetConstantExpression
|| element instanceof JetStringTemplateExpression
|| element instanceof JetSimpleNameExpression)) {
return;
}
PsiElement parent = element.getParent();
if (parent instanceof JetBlockExpression) {
if (!JetPsiUtil.isImplicitlyUsed(element)) {
if (element instanceof JetFunctionLiteralExpression) {
trace.report(Errors.UNUSED_FUNCTION_LITERAL.on((JetFunctionLiteralExpression) element));
}
else {
trace.report(Errors.UNUSED_EXPRESSION.on(element));
}
}
}
}
});
}
////////////////////////////////////////////////////////////////////////////////
// Util methods 7
@@ -167,6 +167,8 @@ public interface Errors {
return argument.getText();
}
};
SimpleDiagnosticFactory UNUSED_EXPRESSION = SimpleDiagnosticFactory.create(WARNING, "The expression is unused");
SimplePsiElementOnlyDiagnosticFactory<JetFunctionLiteralExpression> UNUSED_FUNCTION_LITERAL = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "The function literal is unused. If you mean block, you can use 'run { ... }'");
PsiElementOnlyDiagnosticFactory1<JetExpression, DeclarationDescriptor> VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME);
PsiElementOnlyDiagnosticFactory1<JetExpression, DeclarationDescriptor> INITIALIZATION_BEFORE_DECLARATION = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable cannot be initialized before declaration", NAME);
@@ -1,12 +1,14 @@
package org.jetbrains.jet.lang.psi;
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.lexer.JetTokens;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
@@ -85,11 +87,12 @@ public class JetPsiUtil {
if (quoted.startsWith("`") && quoted.endsWith("`") && quoted.length() >= 2) {
return quoted.substring(1, quoted.length() - 1);
} else {
}
else {
return quoted;
}
}
@NotNull
public static String unquoteIdentifierOrFieldReference(@NotNull String quoted) {
if (quoted.indexOf('`') < 0) {
@@ -98,7 +101,8 @@ public class JetPsiUtil {
if (quoted.startsWith("$")) {
return "$" + unquoteIdentifier(quoted.substring(1));
} else {
}
else {
return unquoteIdentifier(quoted);
}
}
@@ -167,4 +171,51 @@ public class JetPsiUtil {
}
return false;
}
@Nullable
public static <T extends PsiElement> T getDirectParentOfTypeForBlock(@NotNull JetBlockExpression block, @NotNull Class<T> aClass) {
T parent = PsiTreeUtil.getParentOfType(block, aClass);
if (parent instanceof JetIfExpression) {
JetIfExpression ifExpression = (JetIfExpression) parent;
if (ifExpression.getElse() == block || ifExpression.getThen() == block) {
return parent;
}
}
if (parent instanceof JetWhenExpression) {
JetWhenExpression whenExpression = (JetWhenExpression) parent;
for (JetWhenEntry whenEntry : whenExpression.getEntries()) {
if (whenEntry.getExpression() == block) {
return parent;
}
}
}
if (parent instanceof JetFunctionLiteral) {
JetFunctionLiteral functionLiteral = (JetFunctionLiteral) parent;
if (functionLiteral.getBodyExpression() == block) {
return parent;
}
}
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) {
return isImplicitlyUsed(expression);
}
}
return false;
}
}
@@ -358,7 +358,7 @@ public class JetVisitorVoid extends PsiElementVisitor {
visitJetElement(condition);
}
public void visitWhenConditionExpression(JetWhenConditionWithExpression condition) {
public void visitWhenConditionWithExpression(JetWhenConditionWithExpression condition) {
visitJetElement(condition);
}
@@ -20,7 +20,7 @@ public class JetWhenConditionWithExpression extends JetWhenCondition {
@Override
public void accept(@NotNull JetVisitorVoid visitor) {
visitor.visitWhenConditionExpression(this);
visitor.visitWhenConditionWithExpression(this);
}
@Override
@@ -81,5 +81,7 @@ public class ControlFlowAnalyzer {
flowInformationProvider.markUninitializedVariables(function.asElement(), context.isDeclaredLocally());
flowInformationProvider.markUnusedVariables(function.asElement());
flowInformationProvider.markUnusedLiteralsInBlock(function.asElement());
}
}
@@ -143,7 +143,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
}
@Override
public void visitWhenConditionExpression(JetWhenConditionWithExpression condition) {
public void visitWhenConditionWithExpression(JetWhenConditionWithExpression condition) {
JetPattern pattern = condition.getPattern();
if (pattern != null) {
newDataFlowInfo[0] = checkPatternType(pattern, subjectType, subjectExpression == null, scopeToExtend, context, subjectVariables);
@@ -36,7 +36,7 @@ fun intShortInfer() = 1
fun intShort() : Int = 1
//fun intBlockInfer() {1}
fun intBlock() : Int {return 1}
fun intBlock1() : Int {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>1<!>}
fun intBlock1() : Int {<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, UNUSED_EXPRESSION!>1<!>}
fun intString(): Int = <!TYPE_MISMATCH!>"s"<!>
fun intFunctionLiteral(): Int = <!TYPE_MISMATCH!>{ 10 }<!>
@@ -10,28 +10,28 @@ fun ff() {
}
fun test() {
'a'
'\n'
'\t'
'\b'
'\r'
'\"'
'\''
'\\'
'\$'
<!ERROR_COMPILE_TIME_VALUE!>'\x'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\123'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\ra'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\000'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\000'<!>
'\u0000'
'\u000a'
'\u000A'
<!ERROR_COMPILE_TIME_VALUE!>'\u'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\u0'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\u00'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\u000'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\u000z'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\\u000'<!>
<!ERROR_COMPILE_TIME_VALUE!>'\'<!>
<!UNUSED_EXPRESSION!>'a'<!>
<!UNUSED_EXPRESSION!>'\n'<!>
<!UNUSED_EXPRESSION!>'\t'<!>
<!UNUSED_EXPRESSION!>'\b'<!>
<!UNUSED_EXPRESSION!>'\r'<!>
<!UNUSED_EXPRESSION!>'\"'<!>
<!UNUSED_EXPRESSION!>'\''<!>
<!UNUSED_EXPRESSION!>'\\'<!>
<!UNUSED_EXPRESSION!>'\$'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\x'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\123'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\ra'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\000'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\000'<!>
<!UNUSED_EXPRESSION!>'\u0000'<!>
<!UNUSED_EXPRESSION!>'\u000a'<!>
<!UNUSED_EXPRESSION!>'\u000A'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\u'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\u0'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\u00'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\u000'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\u000z'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\\u000'<!>
<!ERROR_COMPILE_TIME_VALUE, UNUSED_EXPRESSION!>'\'<!>
}
@@ -7,16 +7,16 @@ fun demo() {
fun buzz(f : () -> Any?) : Int = 1
val sdf = 1
val foo = 3;
"$abc"
"$"
"$.$.asdf$\t"
"asd\$"
"asd$a<!ILLEGAL_ESCAPE_SEQUENCE!>\x<!>"
"asd$a$asd$ $<!UNRESOLVED_REFERENCE!>xxx<!>"
"fosdfasdo${1 + bar + 100}}sdsdfgdsfsdf"
"foo${bar + map {foo}}sdfsdf"
"foo${bar + map { "foo" }}sdfsdf"
"foo${bar + map {
"foo$sdf${ buzz{}}" }}sdfsdf"
"a<!ILLEGAL_ESCAPE_SEQUENCE!>\u<!> <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>0 <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>00 <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>000 \u0000 \u0AaA <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>0AAz.length( ) + \u0022b"
<!UNUSED_EXPRESSION!>"$abc"<!>
<!UNUSED_EXPRESSION!>"$"<!>
<!UNUSED_EXPRESSION!>"$.$.asdf$\t"<!>
<!UNUSED_EXPRESSION!>"asd\$"<!>
<!UNUSED_EXPRESSION!>"asd$a<!ILLEGAL_ESCAPE_SEQUENCE!>\x<!>"<!>
<!UNUSED_EXPRESSION!>"asd$a$asd$ $<!UNRESOLVED_REFERENCE!>xxx<!>"<!>
<!UNUSED_EXPRESSION!>"fosdfasdo${1 + bar + 100}}sdsdfgdsfsdf"<!>
<!UNUSED_EXPRESSION!>"foo${bar + map {foo}}sdfsdf"<!>
<!UNUSED_EXPRESSION!>"foo${bar + map { "foo" }}sdfsdf"<!>
<!UNUSED_EXPRESSION!>"foo${bar + map {
"foo$sdf${ buzz{}}" }}sdfsdf"<!>
<!UNUSED_EXPRESSION!>"a<!ILLEGAL_ESCAPE_SEQUENCE!>\u<!> <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>0 <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>00 <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>000 \u0000 \u0AaA <!ILLEGAL_ESCAPE_SEQUENCE!>\u<!>0AAz.length( ) + \u0022b"<!>
}
@@ -110,7 +110,7 @@ fun t7() : Int {
<!UNREACHABLE_CODE!>2<!>
}
catch (e : Any) {
2
<!UNUSED_EXPRESSION!>2<!>
}
return 1 // this is OK, like in Java
}
@@ -145,8 +145,8 @@ fun failtest(<!UNUSED_PARAMETER!>a<!> : Int) : Int {
}
fun foo(a : Nothing) : Unit {
1
a
<!UNUSED_EXPRESSION!>1<!>
<!UNUSED_EXPRESSION!>a<!>
<!UNREACHABLE_CODE!>2<!>
}
@@ -62,7 +62,7 @@ fun testCoercionToUnit() {
when(i) {
is 1 -> {
val d = 34
"1"
<!UNUSED_EXPRESSION!>"1"<!>
doSmth(d)
}
@@ -22,11 +22,11 @@ fun A.plus(<!UNUSED_PARAMETER!>a<!> : Any) {
1.foo()
true.<!NONE_APPLICABLE!>foo<!>()
1
<!UNUSED_EXPRESSION!>1<!>
}
fun A.plus(<!UNUSED_PARAMETER!>a<!> : Int) {
1
<!UNUSED_EXPRESSION!>1<!>
}
fun <T> T.minus(<!UNUSED_PARAMETER!>t<!> : T) : Int = 1
@@ -73,4 +73,4 @@ import outer.*
c<!UNNECESSARY_SAFE_CALL!>?.<!>equals2(null)
if (command == null) 1
}
}
@@ -116,12 +116,12 @@ fun f13(a : A?) {
}
if (!(a is val c is B) || !(a is val x is C)) {
<!UNRESOLVED_REFERENCE!>x<!>
<!UNRESOLVED_REFERENCE!>c<!>
<!UNRESOLVED_REFERENCE, UNUSED_EXPRESSION!>x<!>
<!UNRESOLVED_REFERENCE, UNUSED_EXPRESSION!>c<!>
}
else {
<!UNRESOLVED_REFERENCE!>x<!>
<!UNRESOLVED_REFERENCE!>c<!>
<!UNRESOLVED_REFERENCE, UNUSED_EXPRESSION!>x<!>
<!UNRESOLVED_REFERENCE, UNUSED_EXPRESSION!>c<!>
}
if (!(a is val c is B) || !(a is val c is C)) {
@@ -1,16 +1,16 @@
// KT-306 Ambiguity when different this's have same-looking functions
fun test() {
{Foo.() ->
<!UNUSED_FUNCTION_LITERAL!>{Foo.() ->
bar();
{Barr.() ->
this.bar()
bar()
}
}
{Barr.() ->
}<!>
<!UNUSED_FUNCTION_LITERAL!>{Barr.() ->
bar()
}
}<!>
}
class Foo {
@@ -19,4 +19,4 @@ class Foo {
class Barr {
fun bar() {}
}
}
@@ -1,7 +1,7 @@
package kt402
fun getTypeChecker() : (Any)->Boolean {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>{ (a : Any) -> a is <!UNRESOLVED_REFERENCE!>T<!> }<!> // reports unsupported
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY, UNUSED_FUNCTION_LITERAL!>{ (a : Any) -> a is <!UNRESOLVED_REFERENCE!>T<!> }<!> // reports unsupported
}
fun f() : (Any) -> Boolean {
return { (a : Any) -> a is String }
@@ -7,5 +7,5 @@ fun aaa() =
fun bbb() {
aaa()
1 // Stupid error: unreachable code
<!UNUSED_EXPRESSION!>1<!> // Stupid error: unreachable code
}
@@ -1,3 +1,3 @@
fun <T> T.mustBe(t : T) {
"$this must be$<!SYNTAX!>as<!>$t"
<!UNUSED_EXPRESSION!>"$this must be$<!SYNTAX!>as<!>$t"<!>
}
@@ -1,7 +1,7 @@
fun f(<!UNUSED_PARAMETER!>i<!>: Int) {
for (j in 1..100) {
{
<!UNUSED_FUNCTION_LITERAL!>{
var <!NAME_SHADOWING!>i<!> = 12
}
}<!>
}
}
@@ -1,7 +1,7 @@
fun ff(): Int {
var i = 1
{
<!UNUSED_FUNCTION_LITERAL!>{
val <!NAME_SHADOWING!>i<!> = 2
}
}<!>
return i
}
}
@@ -1,5 +1,5 @@
fun f(): Int {
var i = 17
{ (): Unit -> var <!NAME_SHADOWING!>i<!> = 18 }
<!UNUSED_FUNCTION_LITERAL!>{ (): Unit -> var <!NAME_SHADOWING!>i<!> = 18 }<!>
return i
}
}
@@ -1,5 +1,5 @@
fun ff(): Int {
var i = 1
{ (i: Int) -> i }
<!UNUSED_FUNCTION_LITERAL!>{ (i: Int) -> i }<!>
return i
}
}
+2 -2
View File
@@ -18,11 +18,11 @@ fun A.plus(<warning>a</warning> : Any) {
1.foo()
true.<error>foo</error>()
1
<warning>1</warning>
}
fun A.plus(<warning>a</warning> : Int) {
1
<warning>1</warning>
}
fun <T> T.minus(<warning>t</warning> : T) : Int = 1
+14 -12
View File
@@ -7,15 +7,17 @@ fun demo() {
fun buzz(f : () -> Any?) : Int = 1
val sdf = 1
val foo = 3;
"$abc"
"$"
"$.$.asdf$\t"
"asd\$"
"asd$a<error>\x</error>"
"asd$a$asd$ $<error>xxx</error>"
"fosdfasdo${1 + bar + 100}}sdsdfgdsfsdf"
"foo${bar + map {foo}}sdfsdf"
"foo${bar + map { "foo" }}sdfsdf"
"foo${bar + map {
"foo$sdf${ buzz{}}" }}sdfsdf"
}
use("$abc")
use("$")
use("$.$.asdf$\t")
use("asd\$")
use("asd$a<error>\x</error>")
use("asd$a$asd$ $<error>xxx</error>")
use("fosdfasdo${1 + bar + 100}}sdsdfgdsfsdf")
use("foo${bar + map {foo}}sdfsdf")
use("foo${bar + map { "foo" }}sdfsdf")
use("foo${bar + map {
"foo$sdf${ buzz{}}" }}sdfsdf")
}
fun use(<warning>s</warning>: String) {}
+3 -3
View File
@@ -108,7 +108,7 @@ fun t7() : Int {
<error>2</error>
}
catch (e : Any) {
2
<warning>2</warning>
}
return 1 // this is OK, like in Java
}
@@ -143,8 +143,8 @@ fun failtest(<warning>a</warning> : Int) : Int {
}
fun foo(a : Nothing) : Unit {
1
a
<warning>1</warning>
<warning>a</warning>
<error>2</error>
}
+4
View File
@@ -65,3 +65,7 @@ Add iterated elements to java.util.TreeSet
*/
inline fun <T> Iterator<T>.toTreeSet() = to(TreeSet<T>())
/*
Run function f
*/
inline fun <T> run(f: () -> T) = f()