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