static void addOwnDataTo( @NotNull final BindingTrace trace, @Nullable final TraceEntryFilter filter, boolean commitDiagnostics, @NotNull MutableSlicedMap map, MutableDiagnosticsWithSuppression diagnostics) { map.forEach( new Function3<WritableSlice, Object, Object, Void>() { @Override public Void invoke(WritableSlice slice, Object key, Object value) { if (filter == null || filter.accept(slice, key)) { trace.record(slice, key, value); } return null; } }); if (!commitDiagnostics) return; for (Diagnostic diagnostic : diagnostics.getOwnDiagnostics()) { if (filter == null || filter.accept(null, diagnostic.getPsiElement())) { trace.report(diagnostic); } } }
@NotNull private static List<DiagnosticDescriptor> getSortedDiagnosticDescriptors( @NotNull Collection<Diagnostic> diagnostics) { LinkedListMultimap<TextRange, Diagnostic> diagnosticsGroupedByRanges = LinkedListMultimap.create(); for (Diagnostic diagnostic : diagnostics) { if (!diagnostic.isValid()) continue; for (TextRange textRange : diagnostic.getTextRanges()) { diagnosticsGroupedByRanges.put(textRange, diagnostic); } } List<DiagnosticDescriptor> diagnosticDescriptors = Lists.newArrayList(); for (TextRange range : diagnosticsGroupedByRanges.keySet()) { diagnosticDescriptors.add( new DiagnosticDescriptor( range.getStartOffset(), range.getEndOffset(), diagnosticsGroupedByRanges.get(range))); } Collections.sort( diagnosticDescriptors, new Comparator<DiagnosticDescriptor>() { @Override public int compare(@NotNull DiagnosticDescriptor d1, @NotNull DiagnosticDescriptor d2) { // Start early -- go first; start at the same offset, the one who end later is the // outer, i.e. goes first return (d1.start != d2.start) ? d1.start - d2.start : d2.end - d1.end; } }); return diagnosticDescriptors; }
private static void openDiagnosticsString( StringBuffer result, DiagnosticDescriptor currentDescriptor, Map<Diagnostic, TextDiagnostic> diagnosticToExpectedDiagnostic) { result.append("<!"); for (Iterator<Diagnostic> iterator = currentDescriptor.diagnostics.iterator(); iterator.hasNext(); ) { Diagnostic diagnostic = iterator.next(); if (diagnosticToExpectedDiagnostic.containsKey(diagnostic)) { TextDiagnostic expectedDiagnostic = diagnosticToExpectedDiagnostic.get(diagnostic); TextDiagnostic actualTextDiagnostic = TextDiagnostic.asTextDiagnostic(diagnostic); if (compareTextDiagnostic(expectedDiagnostic, actualTextDiagnostic)) { result.append(expectedDiagnostic.asString()); } else { result.append(actualTextDiagnostic.asString()); } } else { result.append(diagnostic.getFactory().getName()); } if (iterator.hasNext()) { result.append(", "); } } result.append("!>"); }
private static void assertSameFile(Collection<Diagnostic> actual) { if (actual.isEmpty()) return; PsiFile file = actual.iterator().next().getPsiElement().getContainingFile(); for (Diagnostic diagnostic : actual) { assert diagnostic.getPsiFile().equals(file) : "All diagnostics should come from the same file: " + diagnostic.getPsiFile() + ", " + file; } }
private static void unexpectedDiagnostics( List<Diagnostic> actual, DiagnosticDiffCallbacks callbacks) { for (Diagnostic diagnostic : actual) { List<TextRange> textRanges = diagnostic.getTextRanges(); for (TextRange textRange : textRanges) { callbacks.unexpectedDiagnostic( TextDiagnostic.asTextDiagnostic(diagnostic), textRange.getStartOffset(), textRange.getEndOffset()); } } }
@Override public int compare(@NotNull Diagnostic o1, @NotNull Diagnostic o2) { List<TextRange> ranges1 = o1.getTextRanges(); List<TextRange> ranges2 = o2.getTextRanges(); int minNumberOfRanges = ranges1.size() < ranges2.size() ? ranges1.size() : ranges2.size(); for (int i = 0; i < minNumberOfRanges; i++) { TextRange range1 = ranges1.get(i); TextRange range2 = ranges2.get(i); int startOffset1 = range1.getStartOffset(); int startOffset2 = range2.getStartOffset(); if (startOffset1 != startOffset2) { // Start early -- go first return startOffset1 - range2.getStartOffset(); } int endOffset1 = range1.getEndOffset(); int endOffset2 = range2.getEndOffset(); if (endOffset1 != endOffset2) { // start at the same offset, the one who end later is the outer, i.e. goes first return endOffset2 - endOffset1; } } return ranges1.size() - ranges2.size(); }
@NotNull public static TextDiagnostic asTextDiagnostic(@NotNull Diagnostic diagnostic) { DiagnosticRenderer renderer = DefaultErrorMessages.getRendererForDiagnostic(diagnostic); String diagnosticName = diagnostic.getFactory().getName(); if (renderer instanceof AbstractDiagnosticWithParametersRenderer) { //noinspection unchecked Object[] renderParameters = ((AbstractDiagnosticWithParametersRenderer) renderer).renderParameters(diagnostic); List<String> parameters = ContainerUtil.map( renderParameters, new Function<Object, String>() { @Override public String fun(Object o) { return o != null ? o.toString() : "null"; } }); return new TextDiagnostic(diagnosticName, parameters); } return new TextDiagnostic(diagnosticName, null); }
@NotNull @Override protected List<IntentionAction> doCreateActions(@NotNull Diagnostic diagnostic) { List<IntentionAction> actions = new LinkedList<IntentionAction>(); BindingContext context = ResolutionUtils.analyzeFully((JetFile) diagnostic.getPsiFile()); PsiElement diagnosticElement = diagnostic.getPsiElement(); if (!(diagnosticElement instanceof JetExpression)) { LOG.error("Unexpected element: " + diagnosticElement.getText()); return Collections.emptyList(); } JetExpression expression = (JetExpression) diagnosticElement; JetType expectedType; JetType expressionType; if (diagnostic.getFactory() == Errors.TYPE_MISMATCH) { DiagnosticWithParameters2<JetExpression, JetType, JetType> diagnosticWithParameters = Errors.TYPE_MISMATCH.cast(diagnostic); expectedType = diagnosticWithParameters.getA(); expressionType = diagnosticWithParameters.getB(); } else if (diagnostic.getFactory() == Errors.NULL_FOR_NONNULL_TYPE) { DiagnosticWithParameters1<JetConstantExpression, JetType> diagnosticWithParameters = Errors.NULL_FOR_NONNULL_TYPE.cast(diagnostic); expectedType = diagnosticWithParameters.getA(); expressionType = TypeUtilsKt.makeNullable(expectedType); } else if (diagnostic.getFactory() == Errors.CONSTANT_EXPECTED_TYPE_MISMATCH) { DiagnosticWithParameters2<JetConstantExpression, String, JetType> diagnosticWithParameters = Errors.CONSTANT_EXPECTED_TYPE_MISMATCH.cast(diagnostic); expectedType = diagnosticWithParameters.getB(); expressionType = context.getType(expression); if (expressionType == null) { LOG.error("No type inferred: " + expression.getText()); return Collections.emptyList(); } } else { LOG.error("Unexpected diagnostic: " + DefaultErrorMessages.render(diagnostic)); return Collections.emptyList(); } // We don't want to cast a cast or type-asserted expression: if (!(expression instanceof JetBinaryExpressionWithTypeRHS) && !(expression.getParent() instanceof JetBinaryExpressionWithTypeRHS)) { actions.add(new CastExpressionFix(expression, expectedType)); } // Property initializer type mismatch property type: JetProperty property = PsiTreeUtil.getParentOfType(expression, JetProperty.class); if (property != null) { JetPropertyAccessor getter = property.getGetter(); JetExpression initializer = property.getInitializer(); if (QuickFixUtil.canEvaluateTo(initializer, expression) || (getter != null && QuickFixUtil.canFunctionOrGetterReturnExpression( property.getGetter(), expression))) { LexicalScope scope = CorePackage.getResolutionScope( property, context, ResolutionUtils.getResolutionFacade(property)); JetType typeToInsert = TypeUtils.approximateWithResolvableType(expressionType, scope, false); actions.add(new ChangeVariableTypeFix(property, typeToInsert)); } } PsiElement expressionParent = expression.getParent(); // Mismatch in returned expression: JetCallableDeclaration function = expressionParent instanceof JetReturnExpression ? BindingContextUtilPackage.getTargetFunction( (JetReturnExpression) expressionParent, context) : PsiTreeUtil.getParentOfType(expression, JetFunction.class, true); if (function instanceof JetFunction && QuickFixUtil.canFunctionOrGetterReturnExpression(function, expression)) { LexicalScope scope = CorePackage.getResolutionScope( function, context, ResolutionUtils.getResolutionFacade(function)); JetType typeToInsert = TypeUtils.approximateWithResolvableType(expressionType, scope, false); actions.add(new ChangeFunctionReturnTypeFix((JetFunction) function, typeToInsert)); } // Fixing overloaded operators: if (expression instanceof JetOperationExpression) { ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(expression, context); if (resolvedCall != null) { JetFunction declaration = getFunctionDeclaration(resolvedCall); if (declaration != null) { actions.add(new ChangeFunctionReturnTypeFix(declaration, expectedType)); } } } // Change function return type when TYPE_MISMATCH is reported on call expression: if (expression instanceof JetCallExpression) { ResolvedCall<?> resolvedCall = CallUtilPackage.getResolvedCall(expression, context); if (resolvedCall != null) { JetFunction declaration = getFunctionDeclaration(resolvedCall); if (declaration != null) { actions.add(new ChangeFunctionReturnTypeFix(declaration, expectedType)); } } } ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilPackage.getParentResolvedCall(expression, context, true); if (resolvedCall != null) { // to fix 'type mismatch' on 'if' branches // todo: the same with 'when' JetExpression parentIf = QuickFixUtil.getParentIfForBranch(expression); JetExpression argumentExpression = (parentIf != null) ? parentIf : expression; ValueArgument valueArgument = CallUtilPackage.getValueArgumentForExpression(resolvedCall.getCall(), argumentExpression); if (valueArgument != null) { JetParameter correspondingParameter = QuickFixUtil.getParameterDeclarationForValueArgument(resolvedCall, valueArgument); JetType valueArgumentType = diagnostic.getFactory() == Errors.NULL_FOR_NONNULL_TYPE ? expressionType : context.getType(valueArgument.getArgumentExpression()); if (correspondingParameter != null && valueArgumentType != null) { JetCallableDeclaration callable = PsiTreeUtil.getParentOfType( correspondingParameter, JetCallableDeclaration.class, true); LexicalScope scope = callable != null ? CorePackage.getResolutionScope( callable, context, ResolutionUtils.getResolutionFacade(callable)) : null; JetType typeToInsert = TypeUtils.approximateWithResolvableType(valueArgumentType, scope, true); actions.add(new ChangeParameterTypeFix(correspondingParameter, typeToInsert)); } } } return actions; }
@Override public void report(@NotNull Diagnostic diagnostic) { if (diagnostic.getSeverity() == Severity.ERROR) { throw new IllegalStateException(DefaultErrorMessages.render(diagnostic)); } }
@Override public void report(@NotNull Diagnostic diagnostic) { if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) { throw new IllegalStateException("Unresolved: " + diagnostic.getPsiElement().getText()); } }