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 visitObjectDeclaration(@NotNull JetObjectDeclaration declaration) {
066                    if (declaration.getParent() instanceof JetObjectLiteralExpression) {
067                        super.visitObjectDeclaration(declaration);
068                    } else {
069                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(declaration, declaration, descriptor));
070                    }
071                }
072    
073                @Override
074                public void visitNamedFunction(@NotNull JetNamedFunction function) {
075                    if (function.getParent().getParent() instanceof JetObjectDeclaration) {
076                        super.visitNamedFunction(function);
077                    } else {
078                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(function, function, descriptor));
079                    }
080                }
081            };
082    
083            function.acceptChildren(visitor);
084        }
085    
086        private static void checkDefaults(
087                @NotNull FunctionDescriptor functionDescriptor,
088                @NotNull JetFunction function,
089                @NotNull BindingTrace trace
090        ) {
091            int index = 0;
092            List<JetParameter> jetParameters = function.getValueParameters();
093            for (ValueParameterDescriptor parameter : functionDescriptor.getValueParameters()) {
094                if (parameter.hasDefaultValue()) {
095                    JetParameter jetParameter = jetParameters.get(index);
096                    //report not supported default only on inlinable lambda and on parameter with inherited dafault (there is some problems to inline it)
097                    if (checkInlinableParameter(parameter, jetParameter, functionDescriptor, null) || !parameter.declaresDefaultValue()) {
098                        trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(jetParameter, jetParameter, functionDescriptor));
099                    }
100                }
101                index++;
102            }
103        }
104    
105        private static void checkNotVirtual(
106                @NotNull FunctionDescriptor functionDescriptor,
107                @NotNull JetFunction function,
108                @NotNull BindingTrace trace
109        ) {
110            if (functionDescriptor.getVisibility() == Visibilities.PRIVATE || functionDescriptor.getModality() == Modality.FINAL) {
111                return;
112            }
113    
114            if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
115                return;
116            }
117    
118            trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(function));
119        }
120    
121    
122        private static void checkHasInlinableAndNullability(
123                @NotNull FunctionDescriptor functionDescriptor,
124                @NotNull JetFunction function,
125                @NotNull BindingTrace trace
126        ) {
127            boolean hasInlinable = false;
128            List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
129            int index = 0;
130            for (ValueParameterDescriptor parameter : parameters) {
131                hasInlinable |= checkInlinableParameter(parameter, function.getValueParameters().get(index++), functionDescriptor, trace);
132            }
133            ReceiverParameterDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
134            if (receiverParameter != null) {
135                JetTypeReference receiver = function.getReceiverTypeRef();
136                assert receiver != null : "Descriptor has a receiver but psi doesn't " + function.getText();
137                hasInlinable |= checkInlinableParameter(receiverParameter, receiver, functionDescriptor, trace);
138            }
139    
140            if (!hasInlinable) {
141                trace.report(Errors.NOTHING_TO_INLINE.on(function, functionDescriptor));
142            }
143        }
144    
145        public static boolean checkInlinableParameter(
146                @NotNull CallableDescriptor parameter,
147                @NotNull JetElement expression,
148                @NotNull CallableDescriptor functionDescriptor,
149                @Nullable BindingTrace trace
150        ) {
151            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
152            JetType type = parameter.getReturnType();
153            if (type != null && builtIns.isExactFunctionOrExtensionFunctionType(type)) {
154                if (!InlineUtil.hasNoinlineAnnotation(parameter)) {
155                    if (type.isNullable()) {
156                        if (trace != null) {
157                            trace.report(Errors.NULLABLE_INLINE_PARAMETER.on(expression, expression, functionDescriptor));
158                        }
159                    }
160                    else {
161                        return true;
162                    }
163                }
164            }
165            return false;
166        }
167    }