001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.resolve.constants; 018 019 import com.google.common.collect.Sets; 020 import com.intellij.psi.tree.IElementType; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.kotlin.KtNodeTypes; 024 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 025 import org.jetbrains.kotlin.diagnostics.Diagnostic; 026 import org.jetbrains.kotlin.diagnostics.DiagnosticFactory; 027 import org.jetbrains.kotlin.diagnostics.DiagnosticUtilsKt; 028 import org.jetbrains.kotlin.psi.KtConstantExpression; 029 import org.jetbrains.kotlin.psi.KtElement; 030 import org.jetbrains.kotlin.resolve.BindingTrace; 031 import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; 032 import org.jetbrains.kotlin.types.KotlinType; 033 import org.jetbrains.kotlin.types.TypeUtils; 034 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; 035 import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext; 036 037 import java.util.Set; 038 039 import static org.jetbrains.kotlin.diagnostics.Errors.*; 040 041 public class CompileTimeConstantChecker { 042 private static final Set<DiagnosticFactory<?>> errorsThatDependOnExpectedType = 043 Sets.<DiagnosticFactory<?>>newHashSet(CONSTANT_EXPECTED_TYPE_MISMATCH, NULL_FOR_NONNULL_TYPE); 044 045 private final KotlinBuiltIns builtIns; 046 private final BindingTrace trace; 047 private final boolean checkOnlyErrorsThatDependOnExpectedType; 048 private final ResolutionContext<?> context; 049 050 public CompileTimeConstantChecker( 051 @NotNull ResolutionContext<?> context, 052 @NotNull KotlinBuiltIns builtIns, 053 boolean checkOnlyErrorsThatDependOnExpectedType 054 ) { 055 this.checkOnlyErrorsThatDependOnExpectedType = checkOnlyErrorsThatDependOnExpectedType; 056 this.builtIns = builtIns; 057 this.trace = context.trace; 058 this.context = context; 059 } 060 061 // return true if there is an error 062 public boolean checkConstantExpressionType( 063 @Nullable ConstantValue<?> compileTimeConstant, 064 @NotNull KtConstantExpression expression, 065 @NotNull KotlinType expectedType 066 ) { 067 IElementType elementType = expression.getNode().getElementType(); 068 069 if (elementType == KtNodeTypes.INTEGER_CONSTANT) { 070 return checkIntegerValue(compileTimeConstant, expectedType, expression); 071 } 072 else if (elementType == KtNodeTypes.FLOAT_CONSTANT) { 073 return checkFloatValue(compileTimeConstant, expectedType, expression); 074 } 075 else if (elementType == KtNodeTypes.BOOLEAN_CONSTANT) { 076 return checkBooleanValue(expectedType, expression); 077 } 078 else if (elementType == KtNodeTypes.CHARACTER_CONSTANT) { 079 return checkCharValue(compileTimeConstant, expectedType, expression); 080 } 081 else if (elementType == KtNodeTypes.NULL) { 082 return checkNullValue(expectedType, expression); 083 } 084 return false; 085 } 086 087 private boolean checkIntegerValue( 088 @Nullable ConstantValue<?> value, 089 @NotNull KotlinType expectedType, 090 @NotNull KtConstantExpression expression 091 ) { 092 if (value == null) { 093 return reportError(INT_LITERAL_OUT_OF_RANGE.on(expression)); 094 } 095 096 if (expression.getText().endsWith("l")) { 097 return reportError(WRONG_LONG_SUFFIX.on(expression)); 098 } 099 100 if (!noExpectedTypeOrError(expectedType)) { 101 KotlinType valueType = value.getType(); 102 if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { 103 return reportConstantExpectedTypeMismatch(expression, "integer", expectedType, null); 104 } 105 } 106 return false; 107 } 108 109 private boolean checkFloatValue( 110 @Nullable ConstantValue<?> value, 111 @NotNull KotlinType expectedType, 112 @NotNull KtConstantExpression expression 113 ) { 114 if (value == null) { 115 return reportError(FLOAT_LITERAL_OUT_OF_RANGE.on(expression)); 116 } 117 if (!noExpectedTypeOrError(expectedType)) { 118 KotlinType valueType = value.getType(); 119 if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { 120 return reportConstantExpectedTypeMismatch(expression, "floating-point", expectedType, null); 121 } 122 } 123 return false; 124 } 125 126 private boolean checkBooleanValue( 127 @NotNull KotlinType expectedType, 128 @NotNull KtConstantExpression expression 129 ) { 130 if (!noExpectedTypeOrError(expectedType) 131 && !KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.getBooleanType(), expectedType)) { 132 return reportConstantExpectedTypeMismatch(expression, "boolean", expectedType, builtIns.getBooleanType()); 133 } 134 return false; 135 } 136 137 private boolean checkCharValue(ConstantValue<?> constant, KotlinType expectedType, KtConstantExpression expression) { 138 if (!noExpectedTypeOrError(expectedType) 139 && !KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.getCharType(), expectedType)) { 140 return reportConstantExpectedTypeMismatch(expression, "character", expectedType, builtIns.getCharType()); 141 } 142 143 if (constant != null) { 144 return false; 145 } 146 147 Diagnostic diagnostic = parseCharacter(expression).getDiagnostic(); 148 if (diagnostic != null) { 149 return reportError(diagnostic); 150 } 151 return false; 152 } 153 154 private boolean checkNullValue(@NotNull KotlinType expectedType, @NotNull KtConstantExpression expression) { 155 if (!noExpectedTypeOrError(expectedType) && !TypeUtils.acceptsNullable(expectedType)) { 156 if (DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection(context, expression, expectedType, builtIns.getNullableNothingType())) { 157 return true; 158 } 159 return reportError(NULL_FOR_NONNULL_TYPE.on(expression, expectedType)); 160 } 161 return false; 162 } 163 164 @NotNull 165 private static CharacterWithDiagnostic parseCharacter(@NotNull KtConstantExpression expression) { 166 String text = expression.getText(); 167 // Strip the quotes 168 if (text.length() < 2 || text.charAt(0) != '\'' || text.charAt(text.length() - 1) != '\'') { 169 return createErrorCharacter(INCORRECT_CHARACTER_LITERAL.on(expression)); 170 } 171 text = text.substring(1, text.length() - 1); // now there're no quotes 172 173 if (text.length() == 0) { 174 return createErrorCharacter(EMPTY_CHARACTER_LITERAL.on(expression)); 175 } 176 177 if (text.charAt(0) != '\\') { 178 // No escape 179 if (text.length() == 1) { 180 return new CharacterWithDiagnostic(text.charAt(0)); 181 } 182 return createErrorCharacter(TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL.on(expression, expression)); 183 } 184 return escapedStringToCharacter(text, expression); 185 } 186 187 @NotNull 188 public static CharacterWithDiagnostic escapedStringToCharacter(@NotNull String text, @NotNull KtElement expression) { 189 assert text.length() > 0 && text.charAt(0) == '\\' : "Only escaped sequences must be passed to this routine: " + text; 190 191 // Escape 192 String escape = text.substring(1); // strip the slash 193 switch (escape.length()) { 194 case 0: 195 // bare slash 196 return illegalEscape(expression); 197 case 1: 198 // one-char escape 199 Character escaped = translateEscape(escape.charAt(0)); 200 if (escaped == null) { 201 return illegalEscape(expression); 202 } 203 return new CharacterWithDiagnostic(escaped); 204 case 5: 205 // unicode escape 206 if (escape.charAt(0) == 'u') { 207 try { 208 Integer intValue = Integer.valueOf(escape.substring(1), 16); 209 return new CharacterWithDiagnostic((char) intValue.intValue()); 210 } catch (NumberFormatException e) { 211 // Will be reported below 212 } 213 } 214 break; 215 } 216 return illegalEscape(expression); 217 } 218 219 @NotNull 220 private static CharacterWithDiagnostic illegalEscape(@NotNull KtElement expression) { 221 return createErrorCharacter(ILLEGAL_ESCAPE.on(expression, expression)); 222 } 223 224 @NotNull 225 private static CharacterWithDiagnostic createErrorCharacter(@NotNull Diagnostic diagnostic) { 226 return new CharacterWithDiagnostic(diagnostic); 227 } 228 229 public static class CharacterWithDiagnostic { 230 private Diagnostic diagnostic; 231 private Character value; 232 233 public CharacterWithDiagnostic(@NotNull Diagnostic diagnostic) { 234 this.diagnostic = diagnostic; 235 } 236 237 public CharacterWithDiagnostic(char value) { 238 this.value = value; 239 } 240 241 @Nullable 242 public Diagnostic getDiagnostic() { 243 return diagnostic; 244 } 245 246 @Nullable 247 public Character getValue() { 248 return value; 249 } 250 } 251 252 @Nullable 253 public static Character parseChar(@NotNull KtConstantExpression expression) { 254 return parseCharacter(expression).getValue(); 255 } 256 257 @Nullable 258 private static Character translateEscape(char c) { 259 switch (c) { 260 case 't': 261 return '\t'; 262 case 'b': 263 return '\b'; 264 case 'n': 265 return '\n'; 266 case 'r': 267 return '\r'; 268 case '\'': 269 return '\''; 270 case '\"': 271 return '\"'; 272 case '\\': 273 return '\\'; 274 case '$': 275 return '$'; 276 } 277 return null; 278 } 279 280 private static boolean noExpectedTypeOrError(KotlinType expectedType) { 281 return TypeUtils.noExpectedType(expectedType) || expectedType.isError(); 282 } 283 284 private boolean reportConstantExpectedTypeMismatch( 285 @NotNull KtConstantExpression expression, 286 @NotNull String typeName, 287 @NotNull KotlinType expectedType, 288 @Nullable KotlinType expressionType 289 ) { 290 if (DiagnosticUtilsKt.reportTypeMismatchDueToTypeProjection(context, expression, expectedType, expressionType)) return true; 291 292 trace.report(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, typeName, expectedType)); 293 return true; 294 } 295 296 private boolean reportError(@NotNull Diagnostic diagnostic) { 297 if (!checkOnlyErrorsThatDependOnExpectedType || errorsThatDependOnExpectedType.contains(diagnostic.getFactory())) { 298 trace.report(diagnostic); 299 return true; 300 } 301 return false; 302 } 303 }