Support for !! (analog of sure()/npe())

This commit is contained in:
Andrey Breslav
2012-03-22 12:36:40 +01:00
parent 4a3d0133f6
commit 7341a82b0b
10 changed files with 449 additions and 375 deletions
@@ -322,6 +322,7 @@ public interface Errors {
DiagnosticFactory<JetSimpleNameExpression> AMBIGUOUS_LABEL = DiagnosticFactory.create(ERROR, "Ambiguous label");
DiagnosticFactory1<PsiElement, String> UNSUPPORTED = DiagnosticFactory1.create(ERROR, "Unsupported [{0}]");
DiagnosticFactory1<PsiElement, JetType> UNNECESSARY_SAFE_CALL = DiagnosticFactory1.create(WARNING, "Unnecessary safe call on a non-null receiver of type {0}");
DiagnosticFactory1<PsiElement, JetType> UNNECESSARY_NOT_NULL_ASSERTION = DiagnosticFactory1.create(WARNING, "Unnecessary non-null assertion (!!) on a non-null receiver of type {0}");
DiagnosticFactory2<JetSimpleNameExpression, JetTypeConstraint, JetTypeParameterListOwner> NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER = DiagnosticFactory2.create(ERROR, "{0} does not refer to a type parameter of {1}", new Renderer<JetTypeConstraint>() {
@NotNull
@Override
@@ -53,7 +53,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
FUN_KEYWORD, FOR_KEYWORD, NULL_KEYWORD,
TRUE_KEYWORD, FALSE_KEYWORD, IS_KEYWORD, THROW_KEYWORD, RETURN_KEYWORD, BREAK_KEYWORD,
CONTINUE_KEYWORD, OBJECT_KEYWORD, IF_KEYWORD, TRY_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD,
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS,
WHEN_KEYWORD, RBRACKET, RBRACE, RPAR, PLUSPLUS, MINUSMINUS, EXCLEXCL,
// MUL,
PLUS, MINUS, EXCL, DIV, PERC, LTEQ,
// TODO GTEQ, foo<bar, baz>=x
@@ -126,7 +126,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
@SuppressWarnings({"UnusedDeclaration"})
private enum Precedence {
POSTFIX(PLUSPLUS, MINUSMINUS,
POSTFIX(PLUSPLUS, MINUSMINUS, EXCLEXCL,
// HASH,
DOT, SAFE_ACCESS), // typeArguments? valueArguments : typeArguments : arrayAccess
@@ -235,7 +235,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
opSet.removeAll(usedSet);
assert false : opSet;
}
assert usedSet.size() == opSet.size();
assert usedSet.size() == opSet.size() : "Either some operations are unused, or something that's not an operation is used";
usedSet.removeAll(opSet);
@@ -350,7 +350,7 @@ public class JetExpressionParsing extends AbstractJetParsing {
* atomicExpression postfixUnaryOperation?
*
* postfixUnaryOperation
* : "++" : "--"
* : "++" : "--" : "!!"
* : typeArguments? valueArguments (getEntryPoint? functionLiteral)
* : typeArguments (getEntryPoint? functionLiteral)
* : arrayAccess
@@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.resolve.calls.CallMaker;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResultsUtil;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
import org.jetbrains.jet.lang.resolve.constants.*;
import org.jetbrains.jet.lang.resolve.constants.StringValue;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
@@ -661,7 +662,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
public JetType visitUnaryExpression(JetUnaryExpression expression, ExpressionTypingContext context, boolean isStatement) {
JetExpression baseExpression = expression.getBaseExpression();
if (baseExpression == null) return null;
JetSimpleNameExpression operationSign = expression.getOperationReference();
// If it's a labeled expression
if (JetTokens.LABELS.contains(operationSign.getReferencedNameElementType())) {
String referencedName = operationSign.getReferencedName();
referencedName = referencedName == null ? " <?>" : referencedName;
@@ -672,12 +676,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.labelResolver.exitLabeledElement(baseExpression);
return DataFlowUtils.checkType(type, expression, context);
}
IElementType operationType = operationSign.getReferencedNameElementType();
String name = OperatorConventions.UNARY_OPERATION_NAMES.get(operationType);
if (name == null) {
context.trace.report(UNSUPPORTED.on(operationSign, "visitUnaryExpression"));
return null;
}
// Type check the base expression
TemporaryBindingTrace temporaryTrace = TemporaryBindingTrace.create(context.trace);
BindingTrace initialTrace = context.trace;
context = context.replaceBindingTrace(temporaryTrace);
@@ -687,6 +689,28 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return null;
}
// Special case for expr!!
if (operationType == JetTokens.EXCLEXCL) {
JetType result;
if (isKnownToBeNotNull(baseExpression, context)) {
temporaryTrace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, type));
result = type;
}
else {
result = TypeUtils.makeNotNullable(type);
}
temporaryTrace.commit();
return DataFlowUtils.checkType(result, expression, context);
}
// Conventions for unary operations
String name = OperatorConventions.UNARY_OPERATION_NAMES.get(operationType);
if (name == null) {
context.trace.report(UNSUPPORTED.on(operationSign, "visitUnaryExpression"));
return null;
}
// a[i]++/-- takes special treatment because it is actually let j = i, arr = a in arr.set(j, a.get(j).inc())
if ((operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) && baseExpression instanceof JetArrayAccessExpression) {
JetExpression stubExpression = ExpressionTypingUtils.createStubExpressionOfNecessaryType(baseExpression.getProject(), type, context.trace);
resolveArrayAccessSetMethod((JetArrayAccessExpression) baseExpression, stubExpression, context.replaceExpectedType(NO_EXPECTED_TYPE).replaceBindingTrace(TemporaryBindingTrace.create(initialTrace)), context.trace);
@@ -694,16 +718,19 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
ExpressionReceiver receiver = new ExpressionReceiver(baseExpression, type);
OverloadResolutionResults<FunctionDescriptor> functionDescriptor = context.resolveCallWithGivenNameToDescriptor(
// Resolve the operation reference
OverloadResolutionResults<FunctionDescriptor> resolutionResults = context.resolveCallWithGivenNameToDescriptor(
CallMaker.makeCall(receiver, expression),
expression.getOperationReference(),
name);
if (!functionDescriptor.isSuccess()) {
if (!resolutionResults.isSuccess()) {
temporaryTrace.commit();
return null;
}
JetType returnType = functionDescriptor.getResultingDescriptor().getReturnType();
// Computing the return type
JetType returnType = resolutionResults.getResultingDescriptor().getReturnType();
JetType result;
if (operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS) {
if (JetTypeChecker.INSTANCE.isSubtypeOf(returnType, JetStandardClasses.getUnitType())) {
@@ -731,6 +758,20 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return DataFlowUtils.checkType(result, expression, context);
}
private boolean isKnownToBeNotNull(JetExpression expression, ExpressionTypingContext context) {
JetType type = context.trace.get(EXPRESSION_TYPE, expression);
assert type != null : "This method is only supposed to be called when the type is not null";
if (!type.isNullable()) return true;
List<JetType> possibleTypes = context.dataFlowInfo
.getPossibleTypes(DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, type, context.trace.getBindingContext()));
for (JetType possibleType : possibleTypes) {
if (!possibleType.isNullable()) {
return true;
}
}
return false;
}
public void checkLValue(BindingTrace trace, JetExpression expression) {
checkLValue(trace, expression, false);
}
@@ -266,6 +266,7 @@ LONG_TEMPLATE_ENTRY_END=\}
">=" { return JetTokens.GTEQ ; }
"==" { return JetTokens.EQEQ ; }
"!=" { return JetTokens.EXCLEQ ; }
"!!" { return JetTokens.EXCLEXCL ; }
"&&" { return JetTokens.ANDAND ; }
"||" { return JetTokens.OROR ; }
//"?." { return JetTokens.SAFE_ACCESS;}
@@ -107,6 +107,7 @@ public interface JetTokens {
JetToken EXCLEQEQEQ = new JetToken("EXCLEQEQEQ");
JetToken EQEQ = new JetToken("EQEQ");
JetToken EXCLEQ = new JetToken("EXCLEQ");
JetToken EXCLEXCL = new JetToken("EXCLEXCL");
JetToken ANDAND = new JetToken("ANDAND");
JetToken OROR = new JetToken("OROR");
JetToken SAFE_ACCESS = new JetToken("SAFE_ACCESS");
@@ -184,7 +185,7 @@ public interface JetTokens {
TokenSet COMMENTS = TokenSet.create(EOL_COMMENT, BLOCK_COMMENT, DOC_COMMENT);
TokenSet STRINGS = TokenSet.create(CHARACTER_LITERAL, REGULAR_STRING_PART);
TokenSet OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, DOT, PLUSPLUS, MINUSMINUS, MUL, PLUS,
TokenSet OPERATIONS = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, IN_KEYWORD, DOT, PLUSPLUS, MINUSMINUS, EXCLEXCL, MUL, PLUS,
MINUS, EXCL, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ, EXCLEQ, ANDAND, OROR,
SAFE_ACCESS, ELVIS,
// MAP, FILTER,
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
fun main(args : Array<String>) {
val a : Int? = null
val b : Int? = null
a!! : Int
a!! + 2
a!!.plus(2)
a!!.plus(b!!)
2.plus(b!!)
2 + b!!
val c = 1
c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
val d : Any? = null
if (d != null) {
d<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
}
if (d is String) {
d<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
}
if (d is String?) {
if (d != null) {
d<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
}
if (d is String) {
d<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
}
}
val <!UNUSED_VARIABLE!>f<!> : String = <!TYPE_MISMATCH!>a!!<!>
<!TYPE_MISMATCH!>a!!<!> : String
}
+1 -1
View File
@@ -212,7 +212,7 @@ prefixUnaryOperation
;
postfixUnaryOperation
: "++" : "--"
: "++" : "--" : "!!"
: callSuffix
: arrayAccess
: memberAccessOperation postfixUnaryExpression // TODO: Review
@@ -50,7 +50,7 @@ import static org.jetbrains.jet.lexer.JetTokens.*;
*/
public class DebugInfoAnnotator implements Annotator {
public static final TokenSet EXCLUDED = TokenSet.create(COLON, AS_KEYWORD, AS_SAFE, IS_KEYWORD, NOT_IS, OROR, ANDAND, EQ, EQEQEQ, EXCLEQEQEQ, ELVIS);
public static final TokenSet EXCLUDED = TokenSet.create(COLON, AS_KEYWORD, AS_SAFE, IS_KEYWORD, NOT_IS, OROR, ANDAND, EQ, EQEQEQ, EXCLEQEQEQ, ELVIS, EXCLEXCL);
private static volatile boolean debugInfoEnabled = true;
@@ -75,7 +75,7 @@ public class JetFormattingModelBuilder implements FormattingModelBuilder {
.aroundInside(TokenSet.create(LT, GT, LTEQ, GTEQ), BINARY_EXPRESSION).spaceIf(jetCommonSettings.SPACE_AROUND_RELATIONAL_OPERATORS)
.around(TokenSet.create(PLUS, MINUS)).spaceIf(jetCommonSettings.SPACE_AROUND_ADDITIVE_OPERATORS)
.aroundInside(TokenSet.create(MUL, DIV, PERC), BINARY_EXPRESSION).spaceIf(jetCommonSettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS)
.around(TokenSet.create(PLUSPLUS, MINUSMINUS, MINUS, PLUS, EXCL)).spaceIf(jetCommonSettings.SPACE_AROUND_UNARY_OPERATOR)
.around(TokenSet.create(PLUSPLUS, MINUSMINUS, EXCLEXCL, MINUS, PLUS, EXCL)).spaceIf(jetCommonSettings.SPACE_AROUND_UNARY_OPERATOR)
.around(RANGE).spaceIf(jetSettings.SPACE_AROUND_RANGE)
.beforeInside(BLOCK, FUN).spaceIf(jetCommonSettings.SPACE_BEFORE_METHOD_LBRACE)