001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve.extension;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.*;
022    import org.jetbrains.jet.lang.diagnostics.Errors;
023    import org.jetbrains.jet.lang.psi.*;
024    import org.jetbrains.jet.lang.resolve.BindingTrace;
025    import org.jetbrains.jet.lang.resolve.FunctionAnalyzerExtension;
026    import org.jetbrains.jet.lang.types.JetType;
027    import org.jetbrains.jet.lang.types.lang.InlineUtil;
028    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
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    
040        @Override
041        public void process(
042                @NotNull final FunctionDescriptor descriptor, @NotNull JetNamedFunction function, @NotNull final BindingTrace trace
043        ) {
044            assert descriptor instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) descriptor).getInlineStrategy().isInline() :
045                    "This method should be invoced on inline function: " + descriptor;
046    
047            checkDefaults(descriptor, function, trace);
048            checkNotVirtual(descriptor, function, trace);
049            checkHasInlinableAndNullability(descriptor, function, trace);
050    
051            JetVisitorVoid visitor = new JetVisitorVoid() {
052    
053                @Override
054                public void visitJetElement(@NotNull JetElement element) {
055                    super.visitJetElement(element);
056                    element.acceptChildren(this);
057                }
058    
059                @Override
060                public void visitClass(@NotNull JetClass klass) {
061                    trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(klass, klass, descriptor));
062                }
063    
064                @Override
065                public void visitNamedFunction(@NotNull JetNamedFunction function) {
066                    if (function.getParent().getParent() instanceof JetObjectDeclaration) {
067                        super.visitNamedFunction(function);
068                    } else {
069                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, function, descriptor));
070                    }
071                }
072            };
073    
074            function.acceptChildren(visitor);
075        }
076    
077        private static void checkDefaults(
078                @NotNull FunctionDescriptor functionDescriptor,
079                @NotNull JetFunction function,
080                @NotNull BindingTrace trace
081        ) {
082            int index = 0;
083            List<JetParameter> jetParameters = function.getValueParameters();
084            for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) {
085                if (parameter.hasDefaultValue()) {
086                    JetParameter jetParameter = jetParameters.get(index);
087                    //report not supported default only on inlinable lambda and on parameter with inherited dafault (there is some problems to inline it)
088                    if (checkInlinableParameter(parameter, jetParameter, functionDescriptor, null) || !parameter.declaresDefaultValue()) {
089                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(jetParameter, jetParameter, functionDescriptor));
090                    }
091                }
092                index++;
093            }
094        }
095    
096        private static void checkNotVirtual(
097                @NotNull FunctionDescriptor functionDescriptor,
098                @NotNull JetFunction function,
099                @NotNull BindingTrace trace
100        ) {
101            if (functionDescriptor.getVisibility() == Visibilities.PRIVATE || functionDescriptor.getModality() == Modality.FINAL) {
102                return;
103            }
104    
105            if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
106                return;
107            }
108    
109            trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(function));
110        }
111    
112    
113        private static void checkHasInlinableAndNullability(
114                @NotNull FunctionDescriptor functionDescriptor,
115                @NotNull JetFunction function,
116                @NotNull BindingTrace trace
117        ) {
118            boolean hasInlinable = false;
119            List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
120            int index = 0;
121            for (ValueParameterDescriptor parameter : parameters) {
122                hasInlinable |= checkInlinableParameter(parameter, function.getValueParameters().get(index++), functionDescriptor, trace);
123            }
124            ReceiverParameterDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
125            if (receiverParameter != null) {
126                JetTypeReference receiver = function.getReceiverTypeRef();
127                assert receiver != null : "Descriptor has a receiver but psi doesn't " + function.getText();
128                hasInlinable |= checkInlinableParameter(receiverParameter, receiver, functionDescriptor, trace);
129            }
130    
131            if (!hasInlinable) {
132                trace.report(Errors.NOTHING_TO_INLINE.on(function, functionDescriptor));
133            }
134        }
135    
136        public static boolean checkInlinableParameter(
137                @NotNull CallableDescriptor parameter,
138                @NotNull JetElement expression,
139                @NotNull CallableDescriptor functionDescriptor,
140                @Nullable BindingTrace trace
141        ) {
142            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
143            JetType type = parameter.getReturnType();
144            if (type != null && builtIns.isExactFunctionOrExtensionFunctionType(type)) {
145                if (!InlineUtil.hasNoinlineAnnotation(parameter)) {
146                    if (type.isNullable()) {
147                        if (trace != null) {
148                            trace.report(Errors.NULLABLE_INLINE_PARAMETER.on(expression, expression, functionDescriptor));
149                        }
150                    }
151                    else {
152                        return true;
153                    }
154                }
155            }
156            return false;
157        }
158    }