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 }