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 }