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