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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.descriptors.*;
022    import org.jetbrains.kotlin.diagnostics.Errors;
023    import org.jetbrains.kotlin.psi.*;
024    import org.jetbrains.kotlin.resolve.BindingTrace;
025    import org.jetbrains.kotlin.resolve.DescriptorUtils;
026    import org.jetbrains.kotlin.resolve.FunctionAnalyzerExtension;
027    import org.jetbrains.kotlin.types.JetType;
028    
029    import java.util.List;
030    
031    public class InlineAnalyzerExtension implements FunctionAnalyzerExtension.AnalyzerExtension {
032    
033        public static final InlineAnalyzerExtension INSTANCE = new InlineAnalyzerExtension();
034    
035        private InlineAnalyzerExtension() {
036        }
037    
038        @Override
039        public void process(
040                @NotNull final FunctionDescriptor descriptor, @NotNull JetNamedFunction function, @NotNull final BindingTrace trace
041        ) {
042            assert InlineUtil.isInline(descriptor) : "This method should be invoked on inline function: " + descriptor;
043    
044            checkDefaults(descriptor, function, trace);
045            checkNotVirtual(descriptor, function, trace);
046            checkHasInlinableAndNullability(descriptor, function, trace);
047    
048            JetVisitorVoid visitor = new JetVisitorVoid() {
049                @Override
050                public void visitJetElement(@NotNull JetElement element) {
051                    super.visitJetElement(element);
052                    element.acceptChildren(this);
053                }
054    
055                @Override
056                public void visitClass(@NotNull JetClass klass) {
057                    trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(klass, klass, descriptor));
058                }
059    
060                @Override
061                public void visitNamedFunction(@NotNull JetNamedFunction function) {
062                    if (function.getParent().getParent() instanceof JetObjectDeclaration) {
063                        super.visitNamedFunction(function);
064                    } else {
065                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, function, descriptor));
066                    }
067                }
068            };
069    
070            function.acceptChildren(visitor);
071        }
072    
073        private static void checkDefaults(
074                @NotNull FunctionDescriptor functionDescriptor,
075                @NotNull JetFunction function,
076                @NotNull BindingTrace trace
077        ) {
078            int index = 0;
079            List<JetParameter> jetParameters = function.getValueParameters();
080            for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) {
081                if (parameter.hasDefaultValue()) {
082                    JetParameter jetParameter = jetParameters.get(index);
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                index++;
089            }
090        }
091    
092        private static void checkNotVirtual(
093                @NotNull FunctionDescriptor functionDescriptor,
094                @NotNull JetFunction 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 JetFunction 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                JetTypeReference 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 |= DescriptorUtils.containsReifiedTypeParameters(functionDescriptor);
128    
129            if (!hasInlinable) {
130                trace.report(Errors.NOTHING_TO_INLINE.on(function, functionDescriptor));
131            }
132        }
133    
134        public static boolean checkInlinableParameter(
135                @NotNull ParameterDescriptor parameter,
136                @NotNull JetElement expression,
137                @NotNull CallableDescriptor functionDescriptor,
138                @Nullable BindingTrace trace
139        ) {
140            if (InlineUtil.isInlineLambdaParameter(parameter)) {
141                if (parameter.getType().isMarkedNullable()) {
142                    if (trace != null) {
143                        trace.report(Errors.NULLABLE_INLINE_PARAMETER.on(expression, expression, functionDescriptor));
144                    }
145                }
146                else {
147                    return true;
148                }
149            }
150            return false;
151        }
152    }