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.psi.KtConstantExpression; 028 import org.jetbrains.kotlin.psi.KtElement; 029 import org.jetbrains.kotlin.resolve.BindingTrace; 030 import org.jetbrains.kotlin.types.KotlinType; 031 import org.jetbrains.kotlin.types.TypeUtils; 032 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; 033 034 import java.util.Set; 035 036 import static org.jetbrains.kotlin.diagnostics.Errors.*; 037 038 public class CompileTimeConstantChecker { 039 private static final Set<DiagnosticFactory<?>> errorsThatDependOnExpectedType = 040 Sets.<DiagnosticFactory<?>>newHashSet(CONSTANT_EXPECTED_TYPE_MISMATCH, NULL_FOR_NONNULL_TYPE); 041 042 private final KotlinBuiltIns builtIns; 043 private final BindingTrace trace; 044 private final boolean checkOnlyErrorsThatDependOnExpectedType; 045 046 public CompileTimeConstantChecker( 047 @NotNull BindingTrace trace, 048 @NotNull KotlinBuiltIns builtIns, 049 boolean checkOnlyErrorsThatDependOnExpectedType 050 ) { 051 this.checkOnlyErrorsThatDependOnExpectedType = checkOnlyErrorsThatDependOnExpectedType; 052 this.builtIns = builtIns; 053 this.trace = trace; 054 } 055 056 // return true if there is an error 057 public boolean checkConstantExpressionType( 058 @Nullable ConstantValue<?> compileTimeConstant, 059 @NotNull KtConstantExpression expression, 060 @NotNull KotlinType expectedType 061 ) { 062 IElementType elementType = expression.getNode().getElementType(); 063 064 if (elementType == KtNodeTypes.INTEGER_CONSTANT) { 065 return checkIntegerValue(compileTimeConstant, expectedType, expression); 066 } 067 else if (elementType == KtNodeTypes.FLOAT_CONSTANT) { 068 return checkFloatValue(compileTimeConstant, expectedType, expression); 069 } 070 else if (elementType == KtNodeTypes.BOOLEAN_CONSTANT) { 071 return checkBooleanValue(expectedType, expression); 072 } 073 else if (elementType == KtNodeTypes.CHARACTER_CONSTANT) { 074 return checkCharValue(compileTimeConstant, expectedType, expression); 075 } 076 else if (elementType == KtNodeTypes.NULL) { 077 return checkNullValue(expectedType, expression); 078 } 079 return false; 080 } 081 082 private boolean checkIntegerValue( 083 @Nullable ConstantValue<?> value, 084 @NotNull KotlinType expectedType, 085 @NotNull KtConstantExpression expression 086 ) { 087 if (value == null) { 088 return reportError(INT_LITERAL_OUT_OF_RANGE.on(expression)); 089 } 090 091 if (expression.getText().endsWith("l")) { 092 return reportError(WRONG_LONG_SUFFIX.on(expression)); 093 } 094 095 if (!noExpectedTypeOrError(expectedType)) { 096 KotlinType valueType = value.getType(); 097 if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { 098 return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "integer", expectedType)); 099 } 100 } 101 return false; 102 } 103 104 private boolean checkFloatValue( 105 @Nullable ConstantValue<?> value, 106 @NotNull KotlinType expectedType, 107 @NotNull KtConstantExpression expression 108 ) { 109 if (value == null) { 110 return reportError(FLOAT_LITERAL_OUT_OF_RANGE.on(expression)); 111 } 112 if (!noExpectedTypeOrError(expectedType)) { 113 KotlinType valueType = value.getType(); 114 if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType)) { 115 return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "floating-point", expectedType)); 116 } 117 } 118 return false; 119 } 120 121 private boolean checkBooleanValue( 122 @NotNull KotlinType expectedType, 123 @NotNull KtConstantExpression expression 124 ) { 125 if (!noExpectedTypeOrError(expectedType) 126 && !KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.getBooleanType(), expectedType)) { 127 return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "boolean", expectedType)); 128 } 129 return false; 130 } 131 132 private boolean checkCharValue(ConstantValue<?> constant, KotlinType expectedType, KtConstantExpression expression) { 133 if (!noExpectedTypeOrError(expectedType) 134 && !KotlinTypeChecker.DEFAULT.isSubtypeOf(builtIns.getCharType(), expectedType)) { 135 return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "character", expectedType)); 136 } 137 138 if (constant != null) { 139 return false; 140 } 141 142 Diagnostic diagnostic = parseCharacter(expression).getDiagnostic(); 143 if (diagnostic != null) { 144 return reportError(diagnostic); 145 } 146 return false; 147 } 148 149 private boolean checkNullValue(@NotNull KotlinType expectedType, @NotNull KtConstantExpression expression) { 150 if (!noExpectedTypeOrError(expectedType) && !TypeUtils.acceptsNullable(expectedType)) { 151 return reportError(NULL_FOR_NONNULL_TYPE.on(expression, expectedType)); 152 } 153 return false; 154 } 155 156 @NotNull 157 private static CharacterWithDiagnostic parseCharacter(@NotNull KtConstantExpression expression) { 158 String text = expression.getText(); 159 // Strip the quotes 160 if (text.length() < 2 || text.charAt(0) != '\'' || text.charAt(text.length() - 1) != '\'') { 161 return createErrorCharacter(INCORRECT_CHARACTER_LITERAL.on(expression)); 162 } 163 text = text.substring(1, text.length() - 1); // now there're no quotes 164 165 if (text.length() == 0) { 166 return createErrorCharacter(EMPTY_CHARACTER_LITERAL.on(expression)); 167 } 168 169 if (text.charAt(0) != '\\') { 170 // No escape 171 if (text.length() == 1) { 172 return new CharacterWithDiagnostic(text.charAt(0)); 173 } 174 return createErrorCharacter(TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL.on(expression, expression)); 175 } 176 return escapedStringToCharacter(text, expression); 177 } 178 179 @NotNull 180 public static CharacterWithDiagnostic escapedStringToCharacter(@NotNull String text, @NotNull KtElement expression) { 181 assert text.length() > 0 && text.charAt(0) == '\\' : "Only escaped sequences must be passed to this routine: " + text; 182 183 // Escape 184 String escape = text.substring(1); // strip the slash 185 switch (escape.length()) { 186 case 0: 187 // bare slash 188 return illegalEscape(expression); 189 case 1: 190 // one-char escape 191 Character escaped = translateEscape(escape.charAt(0)); 192 if (escaped == null) { 193 return illegalEscape(expression); 194 } 195 return new CharacterWithDiagnostic(escaped); 196 case 5: 197 // unicode escape 198 if (escape.charAt(0) == 'u') { 199 try { 200 Integer intValue = Integer.valueOf(escape.substring(1), 16); 201 return new CharacterWithDiagnostic((char) intValue.intValue()); 202 } catch (NumberFormatException e) { 203 // Will be reported below 204 } 205 } 206 break; 207 } 208 return illegalEscape(expression); 209 } 210 211 @NotNull 212 private static CharacterWithDiagnostic illegalEscape(@NotNull KtElement expression) { 213 return createErrorCharacter(ILLEGAL_ESCAPE.on(expression, expression)); 214 } 215 216 @NotNull 217 private static CharacterWithDiagnostic createErrorCharacter(@NotNull Diagnostic diagnostic) { 218 return new CharacterWithDiagnostic(diagnostic); 219 } 220 221 public static class CharacterWithDiagnostic { 222 private Diagnostic diagnostic; 223 private Character value; 224 225 public CharacterWithDiagnostic(@NotNull Diagnostic diagnostic) { 226 this.diagnostic = diagnostic; 227 } 228 229 public CharacterWithDiagnostic(char value) { 230 this.value = value; 231 } 232 233 @Nullable 234 public Diagnostic getDiagnostic() { 235 return diagnostic; 236 } 237 238 @Nullable 239 public Character getValue() { 240 return value; 241 } 242 } 243 244 @Nullable 245 public static Character parseChar(@NotNull KtConstantExpression expression) { 246 return parseCharacter(expression).getValue(); 247 } 248 249 @Nullable 250 private static Character translateEscape(char c) { 251 switch (c) { 252 case 't': 253 return '\t'; 254 case 'b': 255 return '\b'; 256 case 'n': 257 return '\n'; 258 case 'r': 259 return '\r'; 260 case '\'': 261 return '\''; 262 case '\"': 263 return '\"'; 264 case '\\': 265 return '\\'; 266 case '$': 267 return '$'; 268 } 269 return null; 270 } 271 272 public static boolean noExpectedTypeOrError(KotlinType expectedType) { 273 return TypeUtils.noExpectedType(expectedType) || expectedType.isError(); 274 } 275 276 private boolean reportError(@NotNull Diagnostic diagnostic) { 277 if (!checkOnlyErrorsThatDependOnExpectedType || errorsThatDependOnExpectedType.contains(diagnostic.getFactory())) { 278 trace.report(diagnostic); 279 return true; 280 } 281 return false; 282 } 283 }