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.JetNodeTypes;
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.JetTokens;
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.CallUtilPackage;
038 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
039 import org.jetbrains.kotlin.resolve.calls.tasks.TasksPackage;
040 import org.jetbrains.kotlin.types.ErrorUtils;
041 import org.jetbrains.kotlin.types.JetType;
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.JetTokens.*;
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 JetReferenceExpression expression) {
058 // do nothing
059 }
060
061 public abstract void reportElementWithErrorType(@NotNull JetReferenceExpression expression);
062
063 public abstract void reportMissingUnresolved(@NotNull JetReferenceExpression expression);
064
065 public abstract void reportUnresolvedWithTarget(@NotNull JetReferenceExpression expression, @NotNull String target);
066
067 public void reportDynamicCall(@NotNull JetElement 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<JetReferenceExpression, 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((JetReferenceExpression) diagnostic.getPsiElement(), factory);
080 }
081 else if (factory == Errors.SUPER_IS_NOT_AN_EXPRESSION
082 || factory == Errors.SUPER_NOT_AVAILABLE) {
083 JetSuperExpression superExpression = (JetSuperExpression) diagnostic.getPsiElement();
084 markedWithErrorElements.put(superExpression.getInstanceReference(), factory);
085 }
086 else if (factory == Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND) {
087 markedWithErrorElements.put((JetSimpleNameExpression) diagnostic.getPsiElement(), factory);
088 }
089 else if (factory == Errors.UNSUPPORTED) {
090 for (JetReferenceExpression reference : PsiTreeUtil.findChildrenOfType(diagnostic.getPsiElement(),
091 JetReferenceExpression.class)) {
092 markedWithErrorElements.put(reference, factory);
093 }
094 }
095 }
096
097 root.acceptChildren(new JetTreeVisitorVoid() {
098
099 @Override
100 public void visitForExpression(@NotNull JetForExpression expression) {
101 JetExpression 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 visitMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration) {
110 for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
111 reportIfDynamicCall(entry, entry, COMPONENT_RESOLVED_CALL);
112 }
113 super.visitMultiDeclaration(multiDeclaration);
114 }
115
116 @Override
117 public void visitProperty(@NotNull JetProperty 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 JetThisExpression expression) {
130 ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.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 JetReferenceExpression expression) {
139 super.visitReferenceExpression(expression);
140 if (!BindingContextUtils.isExpressionWithValidReference(expression, bindingContext)){
141 return;
142 }
143 IElementType referencedNameElementType = null;
144 if (expression instanceof JetSimpleNameExpression) {
145 JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) expression;
146 IElementType elementType = expression.getNode().getElementType();
147 if (elementType == JetNodeTypes.OPERATION_REFERENCE) {
148 referencedNameElementType = nameExpression.getReferencedNameElementType();
149 if (EXCLUDED.contains(referencedNameElementType)) {
150 return;
151 }
152 }
153 if (elementType == JetNodeTypes.LABEL ||
154 nameExpression.getReferencedNameElementType() == JetTokens.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 JetArrayAccessExpression &&
195 markedWithErrorElements.containsKey(((JetArrayAccessExpression) 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 JetType 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 JetElement, 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(JetElement element, DeclarationDescriptor declarationDescriptor, DebugInfoReporter debugInfoReporter) {
228 if (declarationDescriptor != null && TasksPackage.isDynamic(declarationDescriptor)) {
229 debugInfoReporter.reportDynamicCall(element, declarationDescriptor);
230 return true;
231 }
232 return false;
233 }
234 }