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