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                    trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(jetParameter, jetParameter, functionDescriptor));
097                }
098                index++;
099            }
100        }
101    
102        private static void checkNotVirtual(
103                @NotNull FunctionDescriptor functionDescriptor,
104                @NotNull JetFunction function,
105                @NotNull BindingTrace trace
106        ) {
107            if (functionDescriptor.getVisibility() == Visibilities.PRIVATE || functionDescriptor.getModality() == Modality.FINAL) {
108                return;
109            }
110    
111            if (functionDescriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor) {
112                return;
113            }
114    
115            trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(function));
116        }
117    
118    
119        private static void checkHasInlinableAndNullability(
120                @NotNull FunctionDescriptor functionDescriptor,
121                @NotNull JetFunction function,
122                @NotNull BindingTrace trace
123        ) {
124            boolean hasInlinable = false;
125            List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
126            int index = 0;
127            for (ValueParameterDescriptor parameter : parameters) {
128                hasInlinable |= checkInlinableParameter(parameter, function.getValueParameters().get(index++), functionDescriptor, trace);
129            }
130            ReceiverParameterDescriptor receiverParameter = functionDescriptor.getReceiverParameter();
131            if (receiverParameter != null) {
132                JetTypeReference receiver = function.getReceiverTypeRef();
133                assert receiver != null : "Descriptor has a receiver but psi doesn't " + function.getText();
134                hasInlinable |= checkInlinableParameter(receiverParameter, receiver, functionDescriptor, trace);
135            }
136    
137            if (!hasInlinable) {
138                trace.report(Errors.NOTHING_TO_INLINE.on(function, functionDescriptor));
139            }
140        }
141    
142        public static boolean checkInlinableParameter(
143                @NotNull CallableDescriptor parameter,
144                @NotNull JetElement expression,
145                @NotNull CallableDescriptor functionDescriptor,
146                @Nullable BindingTrace trace
147        ) {
148            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
149            JetType type = parameter.getReturnType();
150            if (type != null && builtIns.isExactFunctionOrExtensionFunctionType(type)) {
151                if (!InlineUtil.hasNoinlineAnnotation(parameter)) {
152                    if (type.isNullable()) {
153                        if (trace != null) {
154                            trace.report(Errors.NULLABLE_INLINE_PARAMETER.on(expression, expression, functionDescriptor));
155                        }
156                    }
157                    else {
158                        return true;
159                    }
160                }
161            }
162            return false;
163        }
164    }