001    /*
002     * Copyright 2010-2015 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.kotlin.checkers;
018    
019    import com.google.common.collect.Maps;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.psi.tree.IElementType;
022    import com.intellij.psi.tree.TokenSet;
023    import com.intellij.psi.util.PsiTreeUtil;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.kotlin.KtNodeTypes;
026    import org.jetbrains.kotlin.descriptors.CallableDescriptor;
027    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
028    import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
029    import org.jetbrains.kotlin.descriptors.VariableDescriptor;
030    import org.jetbrains.kotlin.diagnostics.Diagnostic;
031    import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
032    import org.jetbrains.kotlin.diagnostics.Errors;
033    import org.jetbrains.kotlin.lexer.KtTokens;
034    import org.jetbrains.kotlin.psi.*;
035    import org.jetbrains.kotlin.resolve.BindingContext;
036    import org.jetbrains.kotlin.resolve.BindingContextUtils;
037    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
038    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
039    import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
040    import org.jetbrains.kotlin.types.ErrorUtils;
041    import org.jetbrains.kotlin.types.KotlinType;
042    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
043    
044    import java.util.Collection;
045    import java.util.Map;
046    
047    import static org.jetbrains.kotlin.lexer.KtTokens.*;
048    import static org.jetbrains.kotlin.resolve.BindingContext.*;
049    
050    public class DebugInfoUtil {
051        private static final TokenSet MAY_BE_UNRESOLVED = TokenSet.create(IN_KEYWORD, NOT_IN);
052        private static final TokenSet EXCLUDED = TokenSet.create(
053                COLON, AS_KEYWORD, AS_SAFE, IS_KEYWORD, NOT_IS, OROR, ANDAND, EQ, EQEQEQ, EXCLEQEQEQ, ELVIS, EXCLEXCL);
054    
055        public abstract static class DebugInfoReporter {
056    
057            public void preProcessReference(@NotNull KtReferenceExpression expression) {
058                // do nothing
059            }
060    
061            public abstract void reportElementWithErrorType(@NotNull KtReferenceExpression expression);
062    
063            public abstract void reportMissingUnresolved(@NotNull KtReferenceExpression expression);
064    
065            public abstract void reportUnresolvedWithTarget(@NotNull KtReferenceExpression expression, @NotNull String target);
066    
067            public void reportDynamicCall(@NotNull KtElement element, DeclarationDescriptor declarationDescriptor) { }
068        }
069    
070        public static void markDebugAnnotations(
071                @NotNull PsiElement root,
072                @NotNull final BindingContext bindingContext,
073                @NotNull final DebugInfoReporter debugInfoReporter
074        ) {
075            final Map<KtReferenceExpression, DiagnosticFactory<?>> markedWithErrorElements = Maps.newHashMap();
076            for (Diagnostic diagnostic : bindingContext.getDiagnostics()) {
077                DiagnosticFactory<?> factory = diagnostic.getFactory();
078                if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) {
079                    markedWithErrorElements.put((KtReferenceExpression) diagnostic.getPsiElement(), factory);
080                }
081                else if (factory == Errors.SUPER_IS_NOT_AN_EXPRESSION
082                        || factory == Errors.SUPER_NOT_AVAILABLE) {
083                    KtSuperExpression superExpression = (KtSuperExpression) diagnostic.getPsiElement();
084                    markedWithErrorElements.put(superExpression.getInstanceReference(), factory);
085                }
086                else if (factory == Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND) {
087                    markedWithErrorElements.put((KtSimpleNameExpression) diagnostic.getPsiElement(), factory);
088                }
089                else if (factory == Errors.UNSUPPORTED) {
090                    for (KtReferenceExpression reference : PsiTreeUtil.findChildrenOfType(diagnostic.getPsiElement(),
091                                                                                          KtReferenceExpression.class)) {
092                        markedWithErrorElements.put(reference, factory);
093                    }
094                }
095            }
096    
097            root.acceptChildren(new KtTreeVisitorVoid() {
098    
099                @Override
100                public void visitForExpression(@NotNull KtForExpression expression) {
101                    KtExpression range = expression.getLoopRange();
102                    reportIfDynamicCall(range, range, LOOP_RANGE_ITERATOR_RESOLVED_CALL);
103                    reportIfDynamicCall(range, range, LOOP_RANGE_HAS_NEXT_RESOLVED_CALL);
104                    reportIfDynamicCall(range, range, LOOP_RANGE_NEXT_RESOLVED_CALL);
105                    super.visitForExpression(expression);
106                }
107    
108                @Override
109                public void visitDestructuringDeclaration(@NotNull KtDestructuringDeclaration destructuringDeclaration) {
110                    for (KtDestructuringDeclarationEntry entry : destructuringDeclaration.getEntries()) {
111                        reportIfDynamicCall(entry, entry, COMPONENT_RESOLVED_CALL);
112                    }
113                    super.visitDestructuringDeclaration(destructuringDeclaration);
114                }
115    
116                @Override
117                public void visitProperty(@NotNull KtProperty property) {
118                    VariableDescriptor descriptor = bindingContext.get(VARIABLE, property);
119                    if (descriptor instanceof PropertyDescriptor && property.getDelegate() != null) {
120                        PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
121                        reportIfDynamicCall(property.getDelegate(), propertyDescriptor.getGetter(), DELEGATED_PROPERTY_RESOLVED_CALL);
122                        reportIfDynamicCall(property.getDelegate(), propertyDescriptor.getSetter(), DELEGATED_PROPERTY_RESOLVED_CALL);
123                        reportIfDynamicCall(property.getDelegate(), propertyDescriptor, DELEGATED_PROPERTY_PD_RESOLVED_CALL);
124                    }
125                    super.visitProperty(property);
126                }
127    
128                @Override
129                public void visitThisExpression(@NotNull KtThisExpression expression) {
130                    ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
131                    if (resolvedCall != null) {
132                        reportIfDynamic(expression, resolvedCall.getResultingDescriptor(), debugInfoReporter);
133                    }
134                    super.visitThisExpression(expression);
135                }
136    
137                @Override
138                public void visitReferenceExpression(@NotNull KtReferenceExpression expression) {
139                    super.visitReferenceExpression(expression);
140                    if (!BindingContextUtils.isExpressionWithValidReference(expression, bindingContext)){
141                        return;
142                    }
143                    IElementType referencedNameElementType = null;
144                    if (expression instanceof KtSimpleNameExpression) {
145                        KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) expression;
146                        IElementType elementType = expression.getNode().getElementType();
147                        if (elementType == KtNodeTypes.OPERATION_REFERENCE) {
148                            referencedNameElementType = nameExpression.getReferencedNameElementType();
149                            if (EXCLUDED.contains(referencedNameElementType)) {
150                                return;
151                            }
152                        }
153                        if (elementType == KtNodeTypes.LABEL ||
154                            nameExpression.getReferencedNameElementType() == KtTokens.THIS_KEYWORD) {
155                            return;
156                        }
157                    }
158    
159                    debugInfoReporter.preProcessReference(expression);
160    
161                    String target = null;
162                    DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, expression);
163                    if (declarationDescriptor != null) {
164                        target = declarationDescriptor.toString();
165    
166                        reportIfDynamic(expression, declarationDescriptor, debugInfoReporter);
167                    }
168                    if (target == null) {
169                        PsiElement labelTarget = bindingContext.get(LABEL_TARGET, expression);
170                        if (labelTarget != null) {
171                            target = labelTarget.getText();
172                        }
173                    }
174                    if (target == null) {
175                        Collection<? extends DeclarationDescriptor> declarationDescriptors =
176                                bindingContext.get(AMBIGUOUS_REFERENCE_TARGET, expression);
177                        if (declarationDescriptors != null) {
178                            target = "[" + declarationDescriptors.size() + " descriptors]";
179                        }
180                    }
181                    if (target == null) {
182                        Collection<? extends PsiElement> labelTargets = bindingContext.get(AMBIGUOUS_LABEL_TARGET, expression);
183                        if (labelTargets != null) {
184                            target = "[" + labelTargets.size() + " elements]";
185                        }
186                    }
187    
188                    if (MAY_BE_UNRESOLVED.contains(referencedNameElementType)) {
189                        return;
190                    }
191    
192                    boolean resolved = target != null;
193                    boolean markedWithError = markedWithErrorElements.containsKey(expression);
194                    if (expression instanceof KtArrayAccessExpression &&
195                        markedWithErrorElements.containsKey(((KtArrayAccessExpression) expression).getArrayExpression())) {
196                        // if 'foo' in 'foo[i]' is unresolved it means 'foo[i]' is unresolved (otherwise 'foo[i]' is marked as 'missing unresolved')
197                        markedWithError = true;
198                    }
199                    KotlinType expressionType = bindingContext.getType(expression);
200                    DiagnosticFactory<?> factory = markedWithErrorElements.get(expression);
201                    if (declarationDescriptor != null &&
202                        (ErrorUtils.isError(declarationDescriptor) || ErrorUtils.containsErrorType(expressionType))) {
203                        if (factory != Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND) {
204                            debugInfoReporter.reportElementWithErrorType(expression);
205                        }
206                    }
207                    if (resolved && markedWithError) {
208                        if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(factory)) {
209                            debugInfoReporter.reportUnresolvedWithTarget(expression, target);
210                        }
211                    }
212                    else if (!resolved && !markedWithError) {
213                        debugInfoReporter.reportMissingUnresolved(expression);
214                    }
215                }
216    
217                private <E extends KtElement, K, D extends CallableDescriptor> boolean reportIfDynamicCall(E element, K key, WritableSlice<K, ResolvedCall<D>> slice) {
218                    ResolvedCall<D> resolvedCall = bindingContext.get(slice, key);
219                    if (resolvedCall != null) {
220                        return reportIfDynamic(element, resolvedCall.getResultingDescriptor(), debugInfoReporter);
221                    }
222                    return false;
223                }
224            });
225        }
226    
227        private static boolean reportIfDynamic(KtElement element, DeclarationDescriptor declarationDescriptor, DebugInfoReporter debugInfoReporter) {
228            if (declarationDescriptor != null && DynamicCallsKt.isDynamic(declarationDescriptor)) {
229                debugInfoReporter.reportDynamicCall(element, declarationDescriptor);
230                return true;
231            }
232            return false;
233        }
234    }