/* * Copyright 2010-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.kotlin.idea.highlighter; import com.google.common.collect.Sets; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.analyzer.AnalysisResult; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; import org.jetbrains.kotlin.config.CommonConfigurationKeysKt; import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.config.LanguageVersion; import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl; import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages; import org.jetbrains.kotlin.idea.highlighter.formatHtml.FormatHtmlUtilKt; import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm; import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil; import org.jetbrains.kotlin.test.ConfigurationKind; import org.jetbrains.kotlin.test.InTextDirectivesUtils; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.KotlinTestWithEnvironment; import java.io.File; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; public abstract class AbstractDiagnosticMessageTest extends KotlinTestWithEnvironment { private static final String DIAGNOSTICS_NUMBER_DIRECTIVE = "DIAGNOSTICS_NUMBER"; private static final String DIAGNOSTICS_DIRECTIVE = "DIAGNOSTICS"; private static final String MESSAGE_TYPE_DIRECTIVE = "MESSAGE_TYPE"; private enum MessageType { TEXT("TEXT", "txt"), HTML("HTML", "html"); public final String directive; public final String extension; MessageType(String directive, String extension) { this.directive = directive; this.extension = extension; } } @NotNull @Override protected KotlinCoreEnvironment createEnvironment() { return createEnvironmentWithMockJdk(ConfigurationKind.ALL); } @NotNull protected String getTestDataPath() { return PluginTestCaseBase.getTestDataPathBase() + "/diagnosticMessage/"; } @NotNull protected AnalysisResult analyze(@NotNull KtFile file, @Nullable LanguageVersion explicitLanguageVersion) { CompilerConfiguration configuration = getEnvironment().getConfiguration(); if (explicitLanguageVersion != null) { CommonConfigurationKeysKt.setLanguageVersionSettings( configuration, new LanguageVersionSettingsImpl(explicitLanguageVersion, LanguageVersionSettingsImpl.DEFAULT.getApiVersion()) ); } return JvmResolveUtil.analyze(Collections.singleton(file), getEnvironment(), configuration); } public void doTest(String filePath) throws Exception { File file = new File(filePath); String fileName = file.getName(); String fileData = KotlinTestUtils.doLoadFile(file); Map<String,String> directives = KotlinTestUtils.parseDirectives(fileData); int diagnosticNumber = getDiagnosticNumber(directives); final Set<DiagnosticFactory<?>> diagnosticFactories = getDiagnosticFactories(directives); MessageType messageType = getMessageTypeDirective(directives); String explicitLanguageVersion = InTextDirectivesUtils.findStringWithPrefixes(fileData, "// LANGUAGE_VERSION:"); LanguageVersion version = explicitLanguageVersion == null ? null : LanguageVersion.fromVersionString(explicitLanguageVersion); KtFile psiFile = KotlinTestUtils.createFile(fileName, KotlinTestUtils.doLoadFile(getTestDataPath(), fileName), getProject()); AnalysisResult analysisResult = analyze(psiFile, version); BindingContext bindingContext = analysisResult.getBindingContext(); List<Diagnostic> diagnostics = ContainerUtil.filter(bindingContext.getDiagnostics().all(), new Condition<Diagnostic>() { @Override public boolean value(Diagnostic diagnostic) { return diagnosticFactories.contains(diagnostic.getFactory()); } }); assertEquals("Expected diagnostics number mismatch:", diagnosticNumber, diagnostics.size()); int index = 1; String name = FileUtil.getNameWithoutExtension(fileName); for (Diagnostic diagnostic : diagnostics) { String readableDiagnosticText; String extension; if (messageType != MessageType.TEXT && IdeErrorMessages.hasIdeSpecificMessage(diagnostic)) { readableDiagnosticText = FormatHtmlUtilKt.formatHtml(IdeErrorMessages.render(diagnostic)); extension = MessageType.HTML.extension; } else { readableDiagnosticText = DefaultErrorMessages.render(diagnostic); extension = MessageType.TEXT.extension; } String errorMessageFileName = name + index; String path = getTestDataPath() + "/" + errorMessageFileName + "." + extension; String actualText = "<!-- " + errorMessageFileName + " -->\n" + readableDiagnosticText; assertSameLinesWithFile(path, actualText); index++; } } private static int getDiagnosticNumber(Map<String, String> directives) { String diagnosticsNumber = directives.get(DIAGNOSTICS_NUMBER_DIRECTIVE); assert diagnosticsNumber != null : DIAGNOSTICS_NUMBER_DIRECTIVE + " should be present."; try { return Integer.parseInt(diagnosticsNumber); } catch (NumberFormatException e) { throw new AssertionError(DIAGNOSTICS_NUMBER_DIRECTIVE + " should contain number as its value."); } } @NotNull private Set<DiagnosticFactory<?>> getDiagnosticFactories(Map<String, String> directives) { String diagnosticsData = directives.get(DIAGNOSTICS_DIRECTIVE); assert diagnosticsData != null : DIAGNOSTICS_DIRECTIVE + " should be present."; Set<DiagnosticFactory<?>> diagnosticFactories = Sets.newHashSet(); String[] diagnostics = diagnosticsData.split(" "); for (String diagnosticName : diagnostics) { Object diagnostic = getDiagnostic(diagnosticName); assert diagnostic instanceof DiagnosticFactory: "Can't load diagnostic factory for " + diagnosticName; diagnosticFactories.add((DiagnosticFactory) diagnostic); } return diagnosticFactories; } @Nullable private Object getDiagnostic(@NotNull String diagnosticName) { Field field = getPlatformSpecificDiagnosticField(diagnosticName); if (field == null) { field = getFieldOrNull(Errors.class, diagnosticName); } if (field == null) return null; try { return field.get(null); } catch (IllegalAccessException e) { return null; } } @Nullable protected Field getPlatformSpecificDiagnosticField(@NotNull String diagnosticName) { return getFieldOrNull(ErrorsJvm.class, diagnosticName); } @Nullable protected static Field getFieldOrNull(@NotNull Class<?> kind, @NotNull String field) { try { return kind.getField(field); } catch (NoSuchFieldException e) { return null; } } @Nullable private static MessageType getMessageTypeDirective(Map<String, String> directives) { String messageType = directives.get(MESSAGE_TYPE_DIRECTIVE); if (messageType == null) return null; try { return MessageType.valueOf(messageType); } catch (IllegalArgumentException e) { throw new AssertionError(MESSAGE_TYPE_DIRECTIVE + " should be " + MessageType.TEXT.directive + " or " + MessageType.HTML.directive + ". But was: \"" + messageType + "\"."); } } }