/* * Copyright 2017 The Closure Compiler Authors. * * 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 com.google.javascript.jscomp; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.NoType; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Arrays; import java.util.Collections; import java.util.List; import junit.framework.TestCase; /** * Tests exercising {@link CompilerOptions#assumeForwardDeclaredForMissingTypes} and {@link * DiagnosticGroups#MISSING_SOURCES_WARNINGS}. */ public class PartialCompilationTest extends TestCase { private Compiler compiler; /** * Asserts that the given lines of code compile and only give errors matching the {@link * DiagnosticGroups#MISSING_SOURCES_WARNINGS} category. */ private void assertPartialCompilationSucceeds(String... code) throws Exception { compiler = new Compiler(); compiler.setErrorManager( new BasicErrorManager() { @Override public void report(CheckLevel level, JSError error) { super.report(CheckLevel.ERROR, error); } @Override public void println(CheckLevel level, JSError error) { /* no-op */ } @Override protected void printSummary() { /* no-op */ } }); CompilerOptions options = new CompilerOptions(); options.setAssumeForwardDeclaredForMissingTypes(true); options.setLanguageIn(LanguageMode.ECMASCRIPT_2015); options.setStrictModeInput(true); options.setPreserveDetailedSourceInfo(true); CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); compiler.init( Collections.<SourceFile>emptyList(), Collections.singletonList(SourceFile.fromCode("input.js", Joiner.on('\n').join(code))), options); compiler.parse(); compiler.check(); List<JSError> errors = Arrays.asList(compiler.getErrors()); for (JSError error : errors) { if (!DiagnosticGroups.MISSING_SOURCES_WARNINGS.matches(error)) { fail("Unexpected error " + error); } } } public void testUsesMissingCode() throws Exception { assertPartialCompilationSucceeds( "goog.provide('missing_code_user');", "goog.require('some.thing.Missing');", "missing_code_user.fnUsesMissingNs = function() {", " missing_code_user.missingNamespace.foo();", " missingTopLevelNamespace.bar();", "};"); } public void testMissingType_variable() throws Exception { assertPartialCompilationSucceeds("/** @type {!some.thing.Missing} */ var foo;"); } public void testMissingType_assignment() throws Exception { assertPartialCompilationSucceeds( "/** @type {!some.thing.Missing} */ var foo;", // line break "/** @type {number} */ var bar = foo;"); } public void testMissingRequire() throws Exception { assertPartialCompilationSucceeds( "goog.provide('missing_extends');", // line break "goog.require('some.thing.Missing');"); } public void testMissingExtends() throws Exception { assertPartialCompilationSucceeds( "goog.provide('missing_extends');", "/** @constructor @extends {some.thing.Missing} */", "missing_extends.Extends = function() {}"); } public void testMissingExtends_template() throws Exception { assertPartialCompilationSucceeds( "goog.provide('missing_extends');", "/** @constructor @extends {some.thing.Missing<string>} x */", "missing_extends.Extends = function() {}"); } public void testMissingType_typedefAlias() throws Exception { assertPartialCompilationSucceeds("/** @typedef {string} */ var typedef;"); } public void testMissingType_typedefField() throws Exception { assertPartialCompilationSucceeds("/** @typedef {some.thing.Missing} */ var typedef;"); } public void testMissingEs6Externs() throws Exception { assertPartialCompilationSucceeds("let foo = {a, b};"); } public void testUnresolvedGenerics() throws Exception { assertPartialCompilationSucceeds( "/** @type {!some.thing.Missing<string, !AlsoMissing<!More>>} */", "var x;"); TypedVar x = compiler.getTopScope().getSlot("x"); assertThat(x.getType().isNoResolvedType()).named("type %s", x.getType()).isTrue(); NoType templatizedType = (NoType) x.getType(); assertThat(templatizedType.getReferenceName()).isEqualTo("some.thing.Missing"); ImmutableList<JSType> templateTypes = templatizedType.getTemplateTypes(); assertThat(templateTypes.get(0).isString()).isTrue(); assertThat(templateTypes.get(1).isObject()).isTrue(); ObjectType alsoMissing = (ObjectType) templateTypes.get(1); assertThat(alsoMissing.getReferenceName()).isEqualTo("AlsoMissing"); assertThat(alsoMissing.getTemplateTypes()).hasSize(1); ObjectType more = (ObjectType) alsoMissing.getTemplateTypes().get(0); assertThat(more.getReferenceName()).isEqualTo("More"); } public void testUnresolvedGenerics_defined() throws Exception { assertPartialCompilationSucceeds( "/** @param {!some.thing.Missing<string>} x */", "function useMissing(x) {}", "/** @const {!some.thing.Missing<string>} */", "var x;", "/** @constructor @template T */", "some.thing.Missing = function () {}", "function missingInside() {", " useMissing(new some.thing.Missing());", "}"); } }