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
017package org.jetbrains.jet.lang.resolve.lazy.descriptors;
018
019import com.google.common.collect.ImmutableSet;
020import com.google.common.collect.Lists;
021import com.intellij.openapi.util.Computable;
022import com.intellij.psi.PsiElement;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.annotations.Nullable;
025import org.jetbrains.jet.lang.descriptors.*;
026import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
027import org.jetbrains.jet.lang.diagnostics.Errors;
028import org.jetbrains.jet.lang.psi.*;
029import org.jetbrains.jet.lang.resolve.*;
030import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
031import org.jetbrains.jet.lang.resolve.lazy.data.JetClassLikeInfo;
032import org.jetbrains.jet.lang.resolve.lazy.declarations.ClassMemberDeclarationProvider;
033import org.jetbrains.jet.lang.resolve.lazy.storage.NullableLazyValue;
034import org.jetbrains.jet.lang.resolve.name.Name;
035import org.jetbrains.jet.lang.resolve.scopes.JetScope;
036import org.jetbrains.jet.lang.types.DeferredType;
037import org.jetbrains.jet.lang.types.ErrorUtils;
038import org.jetbrains.jet.lang.types.JetType;
039import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
040import org.jetbrains.jet.util.lazy.RecursionIntolerantLazyValue;
041
042import java.util.*;
043
044public class LazyClassMemberScope extends AbstractLazyMemberScope<LazyClassDescriptor, ClassMemberDeclarationProvider> {
045
046    @NotNull
047    private static final Set<ClassKind> GENERATE_CONSTRUCTORS_FOR =
048            ImmutableSet.of(ClassKind.CLASS, ClassKind.ANNOTATION_CLASS, ClassKind.OBJECT,
049                            ClassKind.ENUM_CLASS, ClassKind.ENUM_ENTRY, ClassKind.CLASS_OBJECT);
050
051    private interface MemberExtractor<T extends CallableMemberDescriptor> {
052        MemberExtractor<FunctionDescriptor> EXTRACT_FUNCTIONS = new MemberExtractor<FunctionDescriptor>() {
053            @NotNull
054            @Override
055            public Collection<FunctionDescriptor> extract(@NotNull JetType extractFrom, @NotNull Name name) {
056                return extractFrom.getMemberScope().getFunctions(name);
057            }
058        };
059
060        MemberExtractor<PropertyDescriptor> EXTRACT_PROPERTIES = new MemberExtractor<PropertyDescriptor>() {
061            @NotNull
062            @Override
063            public Collection<PropertyDescriptor> extract(@NotNull JetType extractFrom, @NotNull Name name) {
064                //noinspection unchecked
065                return (Collection) extractFrom.getMemberScope().getProperties(name);
066            }
067        };
068
069        @NotNull
070        Collection<T> extract(@NotNull JetType extractFrom, @NotNull Name name);
071    }
072
073    private final NullableLazyValue<ConstructorDescriptor> primaryConstructor;
074
075    public LazyClassMemberScope(
076            @NotNull ResolveSession resolveSession,
077            @NotNull ClassMemberDeclarationProvider declarationProvider,
078            @NotNull LazyClassDescriptor thisClass
079    ) {
080        super(resolveSession, declarationProvider, thisClass);
081        this.primaryConstructor = resolveSession.getStorageManager().createNullableLazyValue(new Computable<ConstructorDescriptor>() {
082            @Override
083            public ConstructorDescriptor compute() {
084                return resolvePrimaryConstructor();
085            }
086        });
087    }
088
089    @NotNull
090    @Override
091    protected JetScope getScopeForMemberDeclarationResolution(JetDeclaration declaration) {
092        if (declaration instanceof JetProperty) {
093            return thisDescriptor.getScopeForPropertyInitializerResolution();
094        }
095        return thisDescriptor.getScopeForMemberDeclarationResolution();
096    }
097
098    private <D extends CallableMemberDescriptor> void generateFakeOverrides(
099            @NotNull Name name,
100            @NotNull Collection<D> fromSupertypes,
101            @NotNull final Collection<D> result,
102            @NotNull final Class<? extends D> exactDescriptorClass
103    ) {
104        OverrideResolver.generateOverridesInFunctionGroup(
105                name,
106                fromSupertypes,
107                Lists.newArrayList(result),
108                thisDescriptor,
109                new OverrideResolver.DescriptorSink() {
110                    @Override
111                    public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) {
112                        assert exactDescriptorClass.isInstance(fakeOverride) : "Wrong descriptor type in an override: " +
113                                                                               fakeOverride +
114                                                                               " while expecting " +
115                                                                               exactDescriptorClass.getSimpleName();
116                        //noinspection unchecked
117                        result.add((D) fakeOverride);
118                    }
119
120                    @Override
121                    public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) {
122                        BindingTrace trace = resolveSession.getTrace();
123                        JetDeclaration declaration = (JetDeclaration) BindingContextUtils.descriptorToDeclaration(trace.getBindingContext(),
124                                                                                                                  fromCurrent);
125                        assert declaration != null : "fromCurrent can not be a fake override";
126                        trace.report(Errors.CONFLICTING_OVERLOADS
127                                             .on(declaration, fromCurrent, fromCurrent.getContainingDeclaration().getName().asString()));
128                    }
129                }
130        );
131        OverrideResolver.resolveUnknownVisibilities(result, resolveSession.getTrace());
132    }
133
134    @NotNull
135    @Override
136    public Set<FunctionDescriptor> getFunctions(@NotNull Name name) {
137        // TODO: this should be handled by lazy function descriptors
138        Set<FunctionDescriptor> functions = super.getFunctions(name);
139        for (FunctionDescriptor functionDescriptor : functions) {
140            if (functionDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
141            PsiElement element =
142                    BindingContextUtils.callableDescriptorToDeclaration(resolveSession.getTrace().getBindingContext(), functionDescriptor);
143            OverrideResolver.resolveUnknownVisibilityForMember((JetDeclaration) element, functionDescriptor, resolveSession.getTrace());
144        }
145        return functions;
146    }
147
148    @Override
149    protected void getNonDeclaredFunctions(@NotNull Name name, @NotNull Set<FunctionDescriptor> result) {
150        Collection<FunctionDescriptor> fromSupertypes = Lists.newArrayList();
151        for (JetType supertype : thisDescriptor.getTypeConstructor().getSupertypes()) {
152            fromSupertypes.addAll(supertype.getMemberScope().getFunctions(name));
153        }
154        generateDelegatingDescriptors(name, MemberExtractor.EXTRACT_FUNCTIONS, result);
155        generateEnumClassObjectMethods(result, name);
156        generateDataClassMethods(result, name);
157        generateFakeOverrides(name, fromSupertypes, result, FunctionDescriptor.class);
158    }
159
160    private void generateDataClassMethods(@NotNull Collection<FunctionDescriptor> result, @NotNull Name name) {
161        if (!KotlinBuiltIns.getInstance().isData(thisDescriptor)) return;
162
163        ConstructorDescriptor constructor = getPrimaryConstructor();
164        if (constructor == null) return;
165
166        int parameterIndex = 0;
167        for (ValueParameterDescriptor parameter : constructor.getValueParameters()) {
168            if (ErrorUtils.isErrorType(parameter.getType())) continue;
169            Set<VariableDescriptor> properties = getProperties(parameter.getName());
170            if (properties.isEmpty()) continue;
171            assert properties.size() == 1 : "A constructor parameter is resolved to more than one (" + properties.size() + ") property: " + parameter;
172            PropertyDescriptor property = (PropertyDescriptor) properties.iterator().next();
173            if (property == null) continue;
174            ++parameterIndex;
175
176            if (name.equals(Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + parameterIndex))) {
177                SimpleFunctionDescriptor functionDescriptor =
178                        DescriptorResolver.createComponentFunctionDescriptor(parameterIndex, property,
179                                                                             parameter, thisDescriptor, resolveSession.getTrace());
180                result.add(functionDescriptor);
181                break;
182            }
183        }
184        if (!constructor.getValueParameters().isEmpty() && name.equals(DescriptorResolver.COPY_METHOD_NAME)) {
185            SimpleFunctionDescriptor copyFunctionDescriptor = DescriptorResolver.createCopyFunctionDescriptor(
186                    constructor.getValueParameters(),
187                    thisDescriptor, resolveSession.getTrace());
188            result.add(copyFunctionDescriptor);
189        }
190    }
191
192    private void generateEnumClassObjectMethods(@NotNull Collection<? super FunctionDescriptor> result, @NotNull Name name) {
193        if (!DescriptorUtils.isEnumClassObject(thisDescriptor)) return;
194
195        if (name.equals(DescriptorResolver.VALUES_METHOD_NAME)) {
196            SimpleFunctionDescriptor valuesMethod = DescriptorResolver
197                    .createEnumClassObjectValuesMethod(thisDescriptor, resolveSession.getTrace());
198            result.add(valuesMethod);
199        }
200        else if (name.equals(DescriptorResolver.VALUE_OF_METHOD_NAME)) {
201            SimpleFunctionDescriptor valueOfMethod = DescriptorResolver
202                    .createEnumClassObjectValueOfMethod(thisDescriptor, resolveSession.getTrace());
203            result.add(valueOfMethod);
204        }
205    }
206
207    @NotNull
208    @Override
209    public Set<VariableDescriptor> getProperties(@NotNull Name name) {
210        // TODO: this should be handled by lazy property descriptors
211        Set<VariableDescriptor> properties = super.getProperties(name);
212        for (VariableDescriptor variableDescriptor : properties) {
213            PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
214            if (propertyDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) continue;
215            PsiElement element =
216                    BindingContextUtils.callableDescriptorToDeclaration(resolveSession.getTrace().getBindingContext(), propertyDescriptor);
217            OverrideResolver.resolveUnknownVisibilityForMember((JetDeclaration) element, propertyDescriptor, resolveSession.getTrace());
218        }
219        return properties;
220    }
221
222    @Override
223    @SuppressWarnings("unchecked")
224    protected void getNonDeclaredProperties(@NotNull Name name, @NotNull Set<VariableDescriptor> result) {
225        JetClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
226
227        // From primary constructor parameters
228        ConstructorDescriptor primaryConstructor = getPrimaryConstructor();
229        if (primaryConstructor != null) {
230            List<ValueParameterDescriptor> valueParameterDescriptors = primaryConstructor.getValueParameters();
231            List<? extends JetParameter> primaryConstructorParameters = classInfo.getPrimaryConstructorParameters();
232            assert valueParameterDescriptors.size() == primaryConstructorParameters.size() : "From descriptor: " + valueParameterDescriptors.size() + " but from PSI: " + primaryConstructorParameters.size();
233            for (ValueParameterDescriptor valueParameterDescriptor : valueParameterDescriptors) {
234                JetParameter parameter = primaryConstructorParameters.get(valueParameterDescriptor.getIndex());
235                if (parameter.getValOrVarNode() != null && name.equals(parameter.getNameAsName())) {
236                    PropertyDescriptor propertyDescriptor =
237                            resolveSession.getInjector().getDescriptorResolver().resolvePrimaryConstructorParameterToAProperty(
238                                    thisDescriptor,
239                                    valueParameterDescriptor,
240                                    thisDescriptor.getScopeForClassHeaderResolution(),
241                                    parameter, resolveSession.getTrace()
242                            );
243                    result.add(propertyDescriptor);
244                }
245            }
246        }
247
248        // Members from supertypes
249        Collection<PropertyDescriptor> fromSupertypes = Lists.newArrayList();
250        for (JetType supertype : thisDescriptor.getTypeConstructor().getSupertypes()) {
251            fromSupertypes.addAll((Set) supertype.getMemberScope().getProperties(name));
252        }
253        generateDelegatingDescriptors(name, MemberExtractor.EXTRACT_PROPERTIES, result);
254        generateFakeOverrides(name, fromSupertypes, (Set) result, PropertyDescriptor.class);
255    }
256
257    private <T extends CallableMemberDescriptor> void generateDelegatingDescriptors(
258            @NotNull Name name,
259            @NotNull MemberExtractor<T> extractor,
260            @NotNull Set<? super T> result
261    ) {
262        for (JetDelegationSpecifier delegationSpecifier : declarationProvider.getOwnerInfo().getDelegationSpecifiers()) {
263            if (delegationSpecifier instanceof JetDelegatorByExpressionSpecifier) {
264                JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier;
265                JetTypeReference typeReference = specifier.getTypeReference();
266                if (typeReference != null) {
267                    JetType supertype = resolveSession.getInjector().getTypeResolver().resolveType(
268                            thisDescriptor.getScopeForClassHeaderResolution(),
269                            typeReference,
270                            resolveSession.getTrace(),
271                            false);
272                    Collection<T> descriptors =
273                            DelegationResolver.generateDelegatedMembers(thisDescriptor, extractor.extract(supertype, name));
274                    result.addAll(descriptors);
275                }
276            }
277        }
278    }
279
280    @Override
281    protected void addExtraDescriptors(@NotNull Collection<DeclarationDescriptor> result) {
282        for (JetType supertype : thisDescriptor.getTypeConstructor().getSupertypes()) {
283            for (DeclarationDescriptor descriptor : supertype.getMemberScope().getAllDescriptors()) {
284                if (descriptor instanceof FunctionDescriptor) {
285                    result.addAll(getFunctions(descriptor.getName()));
286                }
287                else if (descriptor instanceof PropertyDescriptor) {
288                    result.addAll(getProperties(descriptor.getName()));
289                }
290                // Nothing else is inherited
291            }
292        }
293
294        result.addAll(getFunctions(DescriptorResolver.VALUES_METHOD_NAME));
295        result.addAll(getFunctions(DescriptorResolver.VALUE_OF_METHOD_NAME));
296
297        addDataClassMethods(result);
298    }
299
300    private void addDataClassMethods(@NotNull Collection<DeclarationDescriptor> result) {
301        if (!KotlinBuiltIns.getInstance().isData(thisDescriptor)) return;
302
303        ConstructorDescriptor constructor = getPrimaryConstructor();
304        if (constructor == null) return;
305
306        // Generate componentN functions until there's no such function for some n
307        int n = 1;
308        while (true) {
309            Name componentName = Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + n);
310            Set<FunctionDescriptor> functions = getFunctions(componentName);
311            if (functions.isEmpty()) break;
312
313            result.addAll(functions);
314
315            n++;
316        }
317        result.addAll(getFunctions(Name.identifier("copy")));
318    }
319
320    @Override
321    public NamespaceDescriptor getNamespace(@NotNull Name name) {
322        return null;
323    }
324
325    @NotNull
326    @Override
327    protected ReceiverParameterDescriptor getImplicitReceiver() {
328        return thisDescriptor.getThisAsReceiverParameter();
329    }
330
331    @NotNull
332    public Set<ConstructorDescriptor> getConstructors() {
333        ConstructorDescriptor constructor = getPrimaryConstructor();
334        return constructor == null ? Collections.<ConstructorDescriptor>emptySet() : Collections.singleton(constructor);
335    }
336
337    @Nullable
338    public ConstructorDescriptor getPrimaryConstructor() {
339        return primaryConstructor.compute();
340    }
341
342    @Nullable
343    private ConstructorDescriptor resolvePrimaryConstructor() {
344        ConstructorDescriptor primaryConstructor = null;
345        if (GENERATE_CONSTRUCTORS_FOR.contains(thisDescriptor.getKind())) {
346            JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
347            if (!thisDescriptor.getKind().isObject()) {
348                JetClass jetClass = (JetClass) classOrObject;
349                ConstructorDescriptorImpl constructor = resolveSession.getInjector().getDescriptorResolver()
350                        .resolvePrimaryConstructorDescriptor(thisDescriptor.getScopeForClassHeaderResolution(),
351                                                             thisDescriptor,
352                                                             jetClass,
353                                                             resolveSession.getTrace());
354                primaryConstructor = constructor;
355                setDeferredReturnType(constructor);
356            }
357            else {
358                ConstructorDescriptorImpl constructor =
359                        DescriptorResolver.createAndRecordPrimaryConstructorForObject(classOrObject, thisDescriptor, resolveSession.getTrace());
360                setDeferredReturnType(constructor);
361                primaryConstructor = constructor;
362            }
363        }
364        return primaryConstructor;
365    }
366
367    private void setDeferredReturnType(@NotNull ConstructorDescriptorImpl descriptor) {
368        descriptor.setReturnType(DeferredType.create(resolveSession.getTrace(), new RecursionIntolerantLazyValue<JetType>() {
369            @Override
370            protected JetType compute() {
371                return thisDescriptor.getDefaultType();
372            }
373        }));
374    }
375
376    @Override
377    public String toString() {
378        // Do not add details here, they may compromise the laziness during debugging
379        return "lazy scope for class " + thisDescriptor.getName();
380    }
381}