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