|
21 | 21 | import static com.google.javascript.rhino.testing.NodeSubject.assertNode;
|
22 | 22 | import static org.junit.Assert.assertThrows;
|
23 | 23 |
|
| 24 | +import com.google.common.base.Preconditions; |
24 | 25 | import com.google.common.collect.ImmutableList;
|
25 | 26 | import com.google.common.collect.ImmutableMap;
|
| 27 | +import com.google.common.collect.ImmutableSet; |
26 | 28 | import com.google.javascript.jscomp.CompilationLevel;
|
27 | 29 | import com.google.javascript.jscomp.Compiler;
|
28 | 30 | import com.google.javascript.jscomp.CompilerOptions;
|
@@ -181,6 +183,157 @@ public void disambiguatesAndDeletesMethodsAcrossLibraries_withTranspilation() th
|
181 | 183 | .isEqualTo(expectedRoot);
|
182 | 184 | }
|
183 | 185 |
|
| 186 | + @Test |
| 187 | + public void disambiguatesAndDeletesMethodsAcrossLibraries_skippedIfInvalidatingTypeError() |
| 188 | + throws IOException { |
| 189 | + SourceFile lib1 = code("class Lib1 { m() { return 'lib1'; } n() { return 'delete me'; } }"); |
| 190 | + SourceFile lib2 = code("class Lib2 { m() { return 'delete me'; } n() { return 'lib2'; } }"); |
| 191 | + precompileLibrary(lib1); |
| 192 | + precompileLibrary(lib2); |
| 193 | + precompileLibrary( |
| 194 | + extern(new TestExternsBuilder().addAlert().build()), |
| 195 | + typeSummary(lib1), |
| 196 | + typeSummary(lib2), |
| 197 | + code("alert(new Lib1().m()); alert(new Lib2().n());")); |
| 198 | + // assigning an instance of Lib1 to a variable of type 'string' causes the disambiguator to |
| 199 | + // 'invalidate' the type of Lib1 and any associated properties. |
| 200 | + SourceFile invalidating = |
| 201 | + code("/** @suppress {checkTypes} @type {string} */ const str = new Lib1();"); |
| 202 | + precompileLibrary(typeSummary(lib1), invalidating); |
| 203 | + |
| 204 | + CompilerOptions options = new CompilerOptions(); |
| 205 | + CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
| 206 | + options.setDependencyOptions(DependencyOptions.none()); |
| 207 | + options.setDisambiguateProperties(true); |
| 208 | + options.setPropertiesThatMustDisambiguate(ImmutableSet.of("m")); |
| 209 | + |
| 210 | + Compiler compiler = compileTypedAstShardsWithoutErrorChecks(options); |
| 211 | + |
| 212 | + assertThat(compiler.getErrors()) |
| 213 | + .comparingElementsUsing(JSCompCorrespondences.DESCRIPTION_EQUALITY) |
| 214 | + .containsExactly( |
| 215 | + "Property 'm' was required to be disambiguated but was invalidated.\n" |
| 216 | + + "See go/disambiguation-errors for more information."); |
| 217 | + } |
| 218 | + |
| 219 | + @Test |
| 220 | + public void disambiguatesAndDeletesMethodsAcrossLibraries_ignoresInvalidationsInUnusedShards() |
| 221 | + throws IOException { |
| 222 | + SourceFile lib1 = code("class Lib1 { m() { return 'lib1'; } n() { return 'delete me'; } }"); |
| 223 | + SourceFile lib2 = code("class Lib2 { m() { return 'delete me'; } n() { return 'lib2'; } }"); |
| 224 | + precompileLibrary(lib1); |
| 225 | + precompileLibrary(lib2); |
| 226 | + precompileLibrary( |
| 227 | + extern(new TestExternsBuilder().addAlert().build()), |
| 228 | + typeSummary(lib1), |
| 229 | + typeSummary(lib2), |
| 230 | + code("alert(new Lib1().m()); alert(new Lib2().n());")); |
| 231 | + // assigning an instance of Lib1 to a variable of type 'string' causes the disambiguator to |
| 232 | + // 'invalidate' the type of Lib1 and any associated properties. |
| 233 | + SourceFile invalidating = |
| 234 | + code("/** @suppress {checkTypes} @type {string} */ const str = new Lib1();"); |
| 235 | + precompileLibrary(typeSummary(lib1), invalidating); |
| 236 | + |
| 237 | + CompilerOptions options = new CompilerOptions(); |
| 238 | + CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
| 239 | + options.setDependencyOptions(DependencyOptions.none()); |
| 240 | + options.setDisambiguateProperties(true); |
| 241 | + options.setPropertiesThatMustDisambiguate(ImmutableSet.of("m")); |
| 242 | + |
| 243 | + // Drop the invalidating source from the list of SourceFile inputs to JSCompiler. |
| 244 | + // However, leave in the associated TypedAST in this.shards. |
| 245 | + // We want to verify that JSCompiler is able to disambiguate properties on Lib1 despite the |
| 246 | + // invalidation in the unused TypedAST shard. |
| 247 | + Preconditions.checkState(this.sourceFiles.get(3) == invalidating, this.sourceFiles); |
| 248 | + this.sourceFiles.remove(3); |
| 249 | + |
| 250 | + Compiler compiler = compileTypedAstShards(options); |
| 251 | + |
| 252 | + Node expectedRoot = parseExpectedCode("", "", "alert('lib1'); alert('lib2')"); |
| 253 | + assertNode(compiler.getRoot().getSecondChild()) |
| 254 | + .usingSerializer(compiler::toSource) |
| 255 | + .isEqualTo(expectedRoot); |
| 256 | + } |
| 257 | + |
| 258 | + @Test |
| 259 | + public void exportAnnotationOnPropertyPreventsRenaming() throws IOException { |
| 260 | + SourceFile externs = extern(new TestExternsBuilder().addAlert().build()); |
| 261 | + SourceFile lib1 = |
| 262 | + code( |
| 263 | + lines( |
| 264 | + "class C {", |
| 265 | + " constructor(foo, bar) {", |
| 266 | + " this.foo = foo;", |
| 267 | + " this.bar = bar;", |
| 268 | + " }", |
| 269 | + "}", |
| 270 | + "alert(new C(1, 2))")); |
| 271 | + SourceFile lib2 = code("const obj = { /** @export */ foo: 0, bar: 1};"); |
| 272 | + precompileLibrary(externs, lib1); |
| 273 | + precompileLibrary(lib2); |
| 274 | + |
| 275 | + CompilerOptions options = new CompilerOptions(); |
| 276 | + CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
| 277 | + options.setDependencyOptions(DependencyOptions.none()); |
| 278 | + options.setDisambiguateProperties(true); |
| 279 | + |
| 280 | + Compiler compiler = compileTypedAstShards(options); |
| 281 | + |
| 282 | + Node expectedRoot = |
| 283 | + parseExpectedCode( |
| 284 | + lines( |
| 285 | + "class a {", // |
| 286 | + "constructor() { this.foo = 1; }", |
| 287 | + "}", |
| 288 | + "alert(new a());"), |
| 289 | + ""); |
| 290 | + assertNode(compiler.getRoot().getSecondChild()) |
| 291 | + .usingSerializer(compiler::toSource) |
| 292 | + .isEqualTo(expectedRoot); |
| 293 | + } |
| 294 | + |
| 295 | + @Test |
| 296 | + public void exportAnnotationOnProperty_ignoredInUnusedTypedAstShard() throws IOException { |
| 297 | + SourceFile externs = extern(new TestExternsBuilder().addAlert().build()); |
| 298 | + SourceFile lib1 = |
| 299 | + code( |
| 300 | + lines( |
| 301 | + "class C {", |
| 302 | + " constructor(foo, bar) {", |
| 303 | + " this.foo = foo;", |
| 304 | + " this.bar = bar;", |
| 305 | + " }", |
| 306 | + "}", |
| 307 | + "alert(new C(1, 2))")); |
| 308 | + SourceFile unusedLib = code("const obj = { /** @export */ foo: 0, bar: 1};"); |
| 309 | + precompileLibrary(externs, lib1); |
| 310 | + precompileLibrary(unusedLib); |
| 311 | + |
| 312 | + CompilerOptions options = new CompilerOptions(); |
| 313 | + CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options); |
| 314 | + options.setDependencyOptions(DependencyOptions.none()); |
| 315 | + options.setDisambiguateProperties(true); |
| 316 | + |
| 317 | + // Drop the unusedLib source from the list of SourceFile inputs to JSCompiler. |
| 318 | + // However, leave in the associated TypedAST in this.shards. |
| 319 | + // We want to verify that JSCompiler does /not/ pay attention to the @export in |
| 320 | + // the unusedLib file, as it's not part of the compilation. |
| 321 | + Preconditions.checkState(this.sourceFiles.size() == 2, this.sourceFiles); |
| 322 | + Preconditions.checkState(this.shards.size() == 2, this.shards); |
| 323 | + this.sourceFiles.remove(1); |
| 324 | + |
| 325 | + Compiler compiler = compileTypedAstShards(options); |
| 326 | + |
| 327 | + Node expectedRoot = |
| 328 | + parseExpectedCode( |
| 329 | + lines( |
| 330 | + "class a {}", // |
| 331 | + "alert(new a());")); |
| 332 | + assertNode(compiler.getRoot().getSecondChild()) |
| 333 | + .usingSerializer(compiler::toSource) |
| 334 | + .isEqualTo(expectedRoot); |
| 335 | + } |
| 336 | + |
184 | 337 | @Test
|
185 | 338 | public void lateFulfilledGlobalVariableIsRenamed() throws IOException {
|
186 | 339 | SourceFile lib1 =
|
@@ -509,7 +662,9 @@ private Compiler compileTypedAstShardsWithoutErrorChecks(CompilerOptions options
|
509 | 662 | }
|
510 | 663 | compiler.parse();
|
511 | 664 | compiler.stage2Passes();
|
512 |
| - compiler.stage3Passes(); |
| 665 | + if (!compiler.hasErrors()) { |
| 666 | + compiler.stage3Passes(); |
| 667 | + } |
513 | 668 |
|
514 | 669 | return compiler;
|
515 | 670 | }
|
|
0 commit comments