/* * Copyright 2015 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; /** Unit tests for {@link ImplicitNullabilityCheck}. */ public final class ImplicitNullabilityCheckTest extends TypeICompilerTestCase { @Override protected CompilerOptions getOptions(CompilerOptions options) { options.setWarningLevel(DiagnosticGroups.ANALYZER_CHECKS, CheckLevel.WARNING); return options; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ImplicitNullabilityCheck(compiler); } @Override protected int getNumRepetitions() { return 1; } public void testExplicitJsdocDoesntWarn() { noWarning("/** @type {boolean} */ var x;"); noWarning("/** @type {null} */ var x;"); noWarning("/** @type {!Object} */ var x;"); noWarning("/** @type {?Object} */ var x;"); noWarning("/** @type {function(new:Object)} */ function f(){}"); noWarning("/** @type {function(this:Object)} */ function f(){}"); noWarning("/** @typedef {!Object} */ var Obj; var /** Obj */ x;"); } public void testExplicitlyNullableUnion() { noWarning("/** @type {(Object|null)} */ var x;"); noWarning("/** @type {(Object|number)?} */ var x;"); noWarning("/** @type {?(Object|number)} */ var x;"); noWarning("/** @type {(Object|?number)} */ var x;"); warnImplicitlyNullable("/** @type {(Object|number)} */ var x;"); } public void testJsdocPositions() { warnImplicitlyNullable("/** @type {Object} */ var x;"); warnImplicitlyNullable("var /** Object */ x;"); warnImplicitlyNullable("/** @typedef {Object} */ var x;"); warnImplicitlyNullable("/** @param {Object} x */ function f(x){}"); warnImplicitlyNullable( "/** @return {Object} */ function f(x){ return {}; }"); } public void testParameterizedObject() { warnImplicitlyNullable(LINE_JOINER.join( "/** @param {Object<string, string>=} opt_values */", "function getMsg(opt_values) {};")); } public void testNullableTypedef() { // Arguable whether or not this deserves a warning, so leaving // out of NTI for now. this.mode = TypeInferenceMode.OTI_ONLY; warnImplicitlyNullable( "/** @typedef {?number} */ var Num; var /** Num */ x;"); } public void testUnkownTypenameDoesntWarn() { // Different warnings in OTI and NTI this.mode = TypeInferenceMode.OTI_ONLY; testSame( DEFAULT_EXTERNS, "/** @type {gibberish} */ var x;", RhinoErrorReporter.UNRECOGNIZED_TYPE_ERROR); this.mode = TypeInferenceMode.NTI_ONLY; testSame( DEFAULT_EXTERNS, "/** @type {gibberish} */ var x;", GlobalTypeInfo.UNRECOGNIZED_TYPE_NAME); } public void testThrowsDoesntWarn() { noWarning("/** @throws {Error} */ function f() {}"); noWarning("/** @throws {TypeError}\n * @throws {SyntaxError} */ function f() {}"); } public void testUserDefinedClass() { warnImplicitlyNullable(LINE_JOINER.join( "/** @constructor */", "function Foo() {}", "/** @type {Foo} */ var x;")); // TODO(aravindpg): this ought to warn under both, or at any rate NTI. noWarning(LINE_JOINER.join( "function f() {", " /** @constructor */", " function Foo() {}", " /** @type {Foo} */ var x;", "}")); } public void testNamespacedTypeDoesntCrash() { warnImplicitlyNullable(LINE_JOINER.join( "/** @const */ var a = {};", "/** @const */ a.b = {};", "/** @constructor */ a.b.Foo = function() {};", "/** @type Array<!a.b.Foo> */ var foos = [];")); } private void warnImplicitlyNullable(String js) { testSame(DEFAULT_EXTERNS, js, ImplicitNullabilityCheck.IMPLICITLY_NULLABLE_JSDOC); } private void noWarning(String js) { testSame(DEFAULT_EXTERNS, js, null); } }