001    /*
002     * Copyright 2010-2016 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;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.types.*;
022    
023    /**
024     * Bare types are somewhat like raw types, but in Kotlin they are only allowed on the right-hand side of is/as.
025     * For example:
026     *
027     *   fun foo(a: Any) {
028     *     if (a is List) {
029     *       // a is known to be List<*> here
030     *     }
031     *   }
032     *
033     * Another example:
034     *
035     *   fun foo(a: Collection<String>) {
036     *     if (a is List) {
037     *       // a is known to be List<String> here
038     *     }
039     *   }
040     *
041     * One can call reconstruct(supertype) to get an actual type from a bare type
042     */
043    public class PossiblyBareType {
044    
045        @NotNull
046        public static PossiblyBareType bare(@NotNull TypeConstructor bareTypeConstructor, boolean nullable) {
047            return new PossiblyBareType(null, bareTypeConstructor, nullable);
048        }
049    
050        @NotNull
051        public static PossiblyBareType type(@NotNull KotlinType actualType) {
052            return new PossiblyBareType(actualType, null, false);
053        }
054    
055        private final KotlinType actualType;
056        private final TypeConstructor bareTypeConstructor;
057        private final boolean nullable;
058    
059        private PossiblyBareType(@Nullable KotlinType actualType, @Nullable TypeConstructor bareTypeConstructor, boolean nullable) {
060            this.actualType = actualType;
061            this.bareTypeConstructor = bareTypeConstructor;
062            this.nullable = nullable;
063        }
064    
065        public boolean isBare() {
066            return actualType == null;
067        }
068    
069        @NotNull
070        public KotlinType getActualType() {
071            //noinspection ConstantConditions
072            return actualType;
073        }
074    
075        @NotNull
076        public TypeConstructor getBareTypeConstructor() {
077            //noinspection ConstantConditions
078            return bareTypeConstructor;
079        }
080    
081        private boolean isBareTypeNullable() {
082            return nullable;
083        }
084    
085        public boolean isNullable() {
086            if (isBare()) return isBareTypeNullable();
087            return getActualType().isMarkedNullable();
088        }
089    
090        public PossiblyBareType makeNullable() {
091            if (isBare()) {
092                return isBareTypeNullable() ? this : bare(getBareTypeConstructor(), true);
093            }
094    
095            return type(TypeUtils.makeNullable(getActualType()));
096        }
097    
098        @NotNull
099        public TypeReconstructionResult reconstruct(@NotNull KotlinType subjectType) {
100            if (!isBare()) return new TypeReconstructionResult(getActualType(), true);
101    
102            TypeReconstructionResult reconstructionResult = CastDiagnosticsUtil.findStaticallyKnownSubtype(
103                    TypeUtils.makeNotNullable(subjectType),
104                    getBareTypeConstructor()
105            );
106            KotlinType type = reconstructionResult.getResultingType();
107            // No need to make an absent type nullable
108            if (type == null) return reconstructionResult;
109    
110            KotlinType resultingType = TypeUtils.makeNullableAsSpecified(type, isBareTypeNullable());
111            return new TypeReconstructionResult(resultingType, reconstructionResult.isAllArgumentsInferred());
112        }
113    
114        @Override
115        public String toString() {
116            return isBare() ? "bare " + bareTypeConstructor + (isBareTypeNullable() ? "?" : "") : getActualType().toString();
117        }
118    }