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.inline;
018    
019    import com.intellij.psi.PsiElement;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.descriptors.*;
023    import org.jetbrains.kotlin.diagnostics.Errors;
024    import org.jetbrains.kotlin.lexer.KtTokens;
025    import org.jetbrains.kotlin.psi.*;
026    import org.jetbrains.kotlin.resolve.BindingTrace;
027    import org.jetbrains.kotlin.resolve.FunctionAnalyzerExtension;
028    import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
029    import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
030    
031    import java.util.List;
032    
033    public class InlineAnalyzerExtension implements FunctionAnalyzerExtension.AnalyzerExtension {
034    
035        public static final InlineAnalyzerExtension INSTANCE = new InlineAnalyzerExtension();
036    
037        private InlineAnalyzerExtension() {
038        }
039    
040        @Override
041        public void process(
042                @NotNull final FunctionDescriptor descriptor, @NotNull KtNamedFunction function, @NotNull final BindingTrace trace
043        ) {
044            assert InlineUtil.isInline(descriptor) : "This method should be invoked on inline function: " + descriptor;
045    
046            checkDefaults(descriptor, function, trace);
047            checkNotVirtual(descriptor, function, trace);
048            checkHasInlinableAndNullability(descriptor, function, trace);
049    
050            KtVisitorVoid visitor = new KtVisitorVoid() {
051                @Override
052                public void visitKtElement(@NotNull KtElement element) {
053                    super.visitKtElement(element);
054                    element.acceptChildren(this);
055                }
056    
057                @Override
058                public void visitClass(@NotNull KtClass klass) {
059                    trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(klass, klass, descriptor));
060                }
061    
062                @Override
063                public void visitNamedFunction(@NotNull KtNamedFunction function) {
064                    if (function.getParent().getParent() instanceof KtObjectDeclaration) {
065                        super.visitNamedFunction(function);
066                    } else {
067                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, function, descriptor));
068                    }
069                }
070            };
071    
072            function.acceptChildren(visitor);
073        }
074    
075        private static void checkDefaults(
076                @NotNull FunctionDescriptor functionDescriptor,
077                @NotNull KtFunction function,
078                @NotNull BindingTrace trace
079        ) {
080            List<KtParameter> jetParameters = function.getValueParameters();
081            for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) {
082                if (DescriptorUtilsKt.hasDefaultValue(parameter)) {
083                    KtParameter jetParameter = jetParameters.get(parameter.getIndex());
084                    //report not supported default only on inlinable lambda and on parameter with inherited default (there is some problems to inline it)
085                    if (checkInlinableParameter(parameter, jetParameter, functionDescriptor, null) || !parameter.declaresDefaultValue()) {
086                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(jetParameter, jetParameter, functionDescriptor));
087                    }
088                }
089            }
090        }
091    
092        private static void checkNotVirtual(
093                @NotNull FunctionDescriptor functionDescriptor,
094                @NotNull KtFunction function,
095                @NotNull BindingTrace trace
096        ) {
097            if (Visibilities.isPrivate(functionDescriptor.getVisibility()) || functionDescriptor.getModality() == Modality.FINAL) {
098                return;
099            }
100    
101            if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
102                return;
103            }
104    
105            trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(function));
106        }
107    
108    
109        private static void checkHasInlinableAndNullability(
110                @NotNull FunctionDescriptor functionDescriptor,
111                @NotNull KtFunction function,
112                @NotNull BindingTrace trace
113        ) {
114            boolean hasInlinable = false;
115            List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
116            int index = 0;
117            for (ValueParameterDescriptor parameter : parameters) {
118                hasInlinable |= checkInlinableParameter(parameter, function.getValueParameters().get(index++), functionDescriptor, trace);
119            }
120            ReceiverParameterDescriptor receiverParameter = functionDescriptor.getExtensionReceiverParameter();
121            if (receiverParameter != null) {
122                KtTypeReference receiver = function.getReceiverTypeReference();
123                assert receiver != null : "Descriptor has a receiver but psi doesn't " + function.getText();
124                hasInlinable |= checkInlinableParameter(receiverParameter, receiver, functionDescriptor, trace);
125            }
126    
127            hasInlinable |= InlineUtil.containsReifiedTypeParameters(functionDescriptor);
128    
129            if (!hasInlinable && !AnnotationUtilKt.isInlineOnly(functionDescriptor)) {
130                KtModifierList modifierList = function.getModifierList();
131                PsiElement inlineModifier = modifierList == null ? null : modifierList.getModifier(KtTokens.INLINE_KEYWORD);
132                PsiElement reportOn = inlineModifier == null ? function : inlineModifier;
133                trace.report(Errors.NOTHING_TO_INLINE.on(reportOn, functionDescriptor));
134            }
135        }
136    
137        public static boolean checkInlinableParameter(
138                @NotNull ParameterDescriptor parameter,
139                @NotNull KtElement expression,
140                @NotNull CallableDescriptor functionDescriptor,
141                @Nullable BindingTrace trace
142        ) {
143            if (InlineUtil.isInlineLambdaParameter(parameter)) {
144                if (parameter.getType().isMarkedNullable()) {
145                    if (trace != null) {
146                        trace.report(Errors.NULLABLE_INLINE_PARAMETER.on(expression, expression, functionDescriptor));
147                    }
148                }
149                else {
150                    return true;
151                }
152            }
153            return false;
154        }
155    }