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.checkers;
018
019import com.google.common.collect.Maps;
020import com.intellij.psi.PsiElement;
021import com.intellij.psi.tree.IElementType;
022import com.intellij.psi.tree.TokenSet;
023import org.jetbrains.annotations.NotNull;
024import org.jetbrains.jet.JetNodeTypes;
025import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
027import org.jetbrains.jet.lang.diagnostics.Diagnostic;
028import org.jetbrains.jet.lang.diagnostics.Errors;
029import org.jetbrains.jet.lang.psi.JetReferenceExpression;
030import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
031import org.jetbrains.jet.lang.psi.JetSuperExpression;
032import org.jetbrains.jet.lang.psi.JetTreeVisitorVoid;
033import org.jetbrains.jet.lang.resolve.BindingContext;
034import org.jetbrains.jet.lang.resolve.BindingContextUtils;
035import org.jetbrains.jet.lang.types.ErrorUtils;
036import org.jetbrains.jet.lang.types.JetType;
037import org.jetbrains.jet.lexer.JetTokens;
038
039import java.util.Collection;
040import java.util.Map;
041
042import static org.jetbrains.jet.lang.resolve.BindingContext.*;
043import static org.jetbrains.jet.lexer.JetTokens.*;
044
045public class DebugInfoUtil {
046    private static final TokenSet EXCLUDED = TokenSet.create(
047            COLON, AS_KEYWORD, AS_SAFE, IS_KEYWORD, NOT_IS, OROR, ANDAND, EQ, EQEQEQ, EXCLEQEQEQ, ELVIS, EXCLEXCL, IN_KEYWORD, NOT_IN);
048
049    public interface DebugInfoReporter {
050
051        void reportElementWithErrorType(@NotNull JetReferenceExpression expression);
052
053        void reportMissingUnresolved(@NotNull JetReferenceExpression expression);
054
055        void reportUnresolvedWithTarget(@NotNull JetReferenceExpression expression, @NotNull String target);
056    }
057
058    public static void markDebugAnnotations(
059            @NotNull PsiElement root,
060            @NotNull final BindingContext bindingContext,
061            @NotNull final DebugInfoReporter debugInfoReporter
062    ) {
063        final Map<JetReferenceExpression, AbstractDiagnosticFactory> markedWithErrorElements = Maps.newHashMap();
064        for (Diagnostic diagnostic : bindingContext.getDiagnostics()) {
065            AbstractDiagnosticFactory factory = diagnostic.getFactory();
066            if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) {
067                markedWithErrorElements.put((JetReferenceExpression) diagnostic.getPsiElement(), factory);
068            }
069            else if (factory == Errors.SUPER_IS_NOT_AN_EXPRESSION
070                    || factory == Errors.SUPER_NOT_AVAILABLE) {
071                JetSuperExpression superExpression = (JetSuperExpression) diagnostic.getPsiElement();
072                markedWithErrorElements.put(superExpression.getInstanceReference(), factory);
073            }
074            else if (factory == Errors.EXPRESSION_EXPECTED_NAMESPACE_FOUND) {
075                markedWithErrorElements.put((JetSimpleNameExpression) diagnostic.getPsiElement(), factory);
076            }
077        }
078
079        root.acceptChildren(new JetTreeVisitorVoid() {
080
081            @Override
082            public void visitReferenceExpression(JetReferenceExpression expression) {
083                super.visitReferenceExpression(expression);
084                if (!BindingContextUtils.isExpressionWithValidReference(expression, bindingContext)){
085                    return;
086                }
087                if (expression instanceof JetSimpleNameExpression) {
088                    JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) expression;
089                    IElementType elementType = expression.getNode().getElementType();
090                    if (elementType == JetNodeTypes.OPERATION_REFERENCE) {
091                        IElementType referencedNameElementType = nameExpression.getReferencedNameElementType();
092                        if (EXCLUDED.contains(referencedNameElementType)) {
093                            return;
094                        }
095                        if (JetTokens.LABELS.contains(referencedNameElementType)) return;
096                    }
097                    else if (nameExpression.getReferencedNameElementType() == JetTokens.THIS_KEYWORD) {
098                        return;
099                    }
100                }
101
102                String target = null;
103                DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, expression);
104                if (declarationDescriptor != null) {
105                    target = declarationDescriptor.toString();
106                }
107                if (target == null) {
108                    PsiElement labelTarget = bindingContext.get(LABEL_TARGET, expression);
109                    if (labelTarget != null) {
110                        target = labelTarget.getText();
111                    }
112                }
113                if (target == null) {
114                    Collection<? extends DeclarationDescriptor> declarationDescriptors =
115                            bindingContext.get(AMBIGUOUS_REFERENCE_TARGET, expression);
116                    if (declarationDescriptors != null) {
117                        target = "[" + declarationDescriptors.size() + " descriptors]";
118                    }
119                }
120                if (target == null) {
121                    Collection<? extends PsiElement> labelTargets = bindingContext.get(AMBIGUOUS_LABEL_TARGET, expression);
122                    if (labelTargets != null) {
123                        target = "[" + labelTargets.size() + " elements]";
124                    }
125                }
126
127                boolean resolved = target != null;
128                boolean markedWithError = markedWithErrorElements.containsKey(expression);
129                JetType expressionType = bindingContext.get(EXPRESSION_TYPE, expression);
130                AbstractDiagnosticFactory factory = markedWithErrorElements.get(expression);
131                if (declarationDescriptor != null &&
132                    (ErrorUtils.isError(declarationDescriptor) || ErrorUtils.containsErrorType(expressionType))) {
133                    if (factory != Errors.EXPRESSION_EXPECTED_NAMESPACE_FOUND) {
134                        debugInfoReporter.reportElementWithErrorType(expression);
135                    }
136                }
137                if (resolved && markedWithError) {
138                    if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(factory)) {
139                        debugInfoReporter.reportUnresolvedWithTarget(expression, target);
140                    }
141                }
142                else if (!resolved && !markedWithError) {
143                    debugInfoReporter.reportMissingUnresolved(expression);
144                }
145            }
146        });
147    }
148
149}