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 org.jetbrains.annotations.NotNull;
024    import org.jetbrains.jet.JetNodeTypes;
025    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026    import org.jetbrains.jet.lang.diagnostics.AbstractDiagnosticFactory;
027    import org.jetbrains.jet.lang.diagnostics.Diagnostic;
028    import org.jetbrains.jet.lang.diagnostics.Errors;
029    import org.jetbrains.jet.lang.psi.JetReferenceExpression;
030    import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
031    import org.jetbrains.jet.lang.psi.JetSuperExpression;
032    import org.jetbrains.jet.lang.psi.JetTreeVisitorVoid;
033    import org.jetbrains.jet.lang.resolve.BindingContext;
034    import org.jetbrains.jet.lang.resolve.BindingContextUtils;
035    import org.jetbrains.jet.lang.types.ErrorUtils;
036    import org.jetbrains.jet.lang.types.JetType;
037    import org.jetbrains.jet.lexer.JetTokens;
038    
039    import java.util.Collection;
040    import java.util.Map;
041    
042    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
043    import static org.jetbrains.jet.lexer.JetTokens.*;
044    
045    public 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    }