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 }