Support for !! (analog of sure()/npe())
This commit is contained in:
@@ -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
|
||||
|
||||
+49
-8
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user