From b9fa027b9bd04822295bc157b5ecad688bdf217f Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Fri, 28 Apr 2023 20:48:40 +0200 Subject: [PATCH] support classes with the same FQ name in different modules GitOrigin-RevId: e6c7f706d151f32f8236ba2bfaab8df4dde73651 --- .../convertToCheckedExceptionMultiModule.log | 22 + .../module1/src/ppp/Client.java | 11 + .../module1/src/ppp/SomeException.java | 4 + .../module1/src/ppp/SomeException.java.new | 4 + .../module1/src/ppp/Task.java | 6 + .../module2/src/ppp/Client2.java | 11 + .../module2/src/ppp/Task.java | 6 + .../common/sameClassesInDifferentModules.log | 22 + .../moduleA/src/com/ppp/A.java | 8 + .../moduleA/src/com/ppp/A.java.new | 9 + .../moduleA/src/com/ppp/BA.java | 8 + .../moduleA/src/com/ppp/C.java | 7 + .../moduleB/src/com/ppp/A.java | 7 + .../moduleB/src/com/ppp/BB.java | 8 + .../moduleB/src/com/ppp/C.java | 7 + .../org/jetbrains/jps/javac/Iterators.java | 40 +- .../IntObjectPersistentMultiMaplet.java | 22 +- .../java/dependencyView/Mappings.java | 516 ++++++++++-------- .../jetbrains/ether/ClassPropertyTest.java | 26 +- .../org/jetbrains/ether/CommonTest.java | 23 +- 20 files changed, 498 insertions(+), 269 deletions(-) create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule.log create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Client.java create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java.new create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Task.java create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Client2.java create mode 100644 java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Task.java create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules.log create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java.new create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/BA.java create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/C.java create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/A.java create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/BB.java create mode 100644 java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/C.java diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule.log b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule.log new file mode 100644 index 000000000000..6c24a2b69277 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule.log @@ -0,0 +1,22 @@ +Cleaning output files: +out/production/module1/ppp/SomeException.class +End of files +Compiling files: +module1/src/ppp/SomeException.java +End of files +Cleaning output files: +out/production/module1/ppp/Client.class +out/production/module1/ppp/Task.class +End of files +Compiling files: +module1/src/ppp/Client.java +module1/src/ppp/Task.java +End of files +Cleaning output files: +out/production/module2/ppp/Client2.class +out/production/module2/ppp/Task.class +End of files +Compiling files: +module2/src/ppp/Client2.java +module2/src/ppp/Task.java +End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Client.java b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Client.java new file mode 100644 index 000000000000..0aa21bbd20cb --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Client.java @@ -0,0 +1,11 @@ +package ppp; + +public class Client { + public void foo(Task task) { + try { + task.execute(); + } + catch (Throwable/*RuntimeException*/ e) { + } + } +} diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java new file mode 100644 index 000000000000..b7ac46fbce07 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java @@ -0,0 +1,4 @@ +package ppp; + +public class SomeException extends RuntimeException{ +} diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java.new b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java.new new file mode 100644 index 000000000000..32883f13b3f7 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/SomeException.java.new @@ -0,0 +1,4 @@ +package ppp; + +public class SomeException extends Exception{ +} diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Task.java b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Task.java new file mode 100644 index 000000000000..133268457744 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module1/src/ppp/Task.java @@ -0,0 +1,6 @@ +package ppp; + +public class Task { + public void execute() throws SomeException{ + } +} diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Client2.java b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Client2.java new file mode 100644 index 000000000000..0d6590ad912e --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Client2.java @@ -0,0 +1,11 @@ +package ppp; + +public class Client2 { + public void foo(Task task) { + try { + task.execute2(); + } + catch (Throwable/*RuntimeException*/ e) { + } + } +} diff --git a/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Task.java b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Task.java new file mode 100644 index 000000000000..dd4daa21e973 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/classProperties/convertToCheckedExceptionMultiModule/module2/src/ppp/Task.java @@ -0,0 +1,6 @@ +package ppp; + +public class Task { + public void execute2() throws SomeException{ + } +} diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules.log b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules.log new file mode 100644 index 000000000000..874f3abf63f1 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules.log @@ -0,0 +1,22 @@ +Cleaning output files: +out/production/moduleA/com/ppp/A.class +End of files +Compiling files: +moduleA/src/com/ppp/A.java +End of files +Cleaning output files: +out/production/moduleA/com/ppp/BA.class +out/production/moduleA/com/ppp/C.class +End of files +Compiling files: +moduleA/src/com/ppp/BA.java +moduleA/src/com/ppp/C.java +End of files +Cleaning output files: +out/production/moduleB/com/ppp/BB.class +out/production/moduleB/com/ppp/C.class +End of files +Compiling files: +moduleB/src/com/ppp/BB.java +moduleB/src/com/ppp/C.java +End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java new file mode 100644 index 000000000000..bb49e8b92c3a --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java @@ -0,0 +1,8 @@ +package com.ppp; + +public class A { + public static int field2; + + public static void foo(Object obj) {} + +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java.new b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java.new new file mode 100644 index 000000000000..f02384fdcd6c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/A.java.new @@ -0,0 +1,9 @@ +package com.ppp; + +public class A { + public static int field; + + public static void foo(Object obj) {} + + public static void foo(Integer num) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/BA.java b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/BA.java new file mode 100644 index 000000000000..ac455efd1f72 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/BA.java @@ -0,0 +1,8 @@ +package com.ppp; + +public class BA { + void method() { + A.foo(new Integer(42)); + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/C.java b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/C.java new file mode 100644 index 000000000000..64fc97fd1462 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleA/src/com/ppp/C.java @@ -0,0 +1,7 @@ +package com.ppp; + +public class C { + void method() { + A.foo(new Integer(42)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/A.java b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/A.java new file mode 100644 index 000000000000..2bfa37b59edc --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/A.java @@ -0,0 +1,7 @@ +package com.ppp; + +public class A { + public static int field2; + + public static void foo(Object obj) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/BB.java b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/BB.java new file mode 100644 index 000000000000..08b8c3e21084 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/BB.java @@ -0,0 +1,8 @@ +package com.ppp; + +public class BB { + void method() { + int val = A.field2; + } +} + diff --git a/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/C.java b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/C.java new file mode 100644 index 000000000000..64fc97fd1462 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/sameClassesInDifferentModules/moduleB/src/com/ppp/C.java @@ -0,0 +1,7 @@ +package com.ppp; + +public class C { + void method() { + A.foo(new Integer(42)); + } +} \ No newline at end of file diff --git a/jps/jps-builders-6/src/org/jetbrains/jps/javac/Iterators.java b/jps/jps-builders-6/src/org/jetbrains/jps/javac/Iterators.java index db580af79265..983921a8cdcb 100644 --- a/jps/jps-builders-6/src/org/jetbrains/jps/javac/Iterators.java +++ b/jps/jps-builders-6/src/org/jetbrains/jps/javac/Iterators.java @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.jps.javac; import com.intellij.util.BooleanFunction; @@ -104,6 +104,7 @@ public final class Iterators { return Collections.emptyList(); } if (parts.size() == 1) { + //noinspection unchecked return (Iterable)parts.iterator().next(); } return flat((Iterable>)parts); @@ -155,18 +156,8 @@ public final class Iterators { } public static Iterator asIterator(final Iterable from) { - final Iterator it = from.iterator(); - return new BaseIterator() { - @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public I next() { - return it.next(); - } - }; + //noinspection unchecked + return from == null? Collections.emptyIterator() : (Iterator)from.iterator(); } public static Iterable asIterable(final T elem) { @@ -315,6 +306,29 @@ public final class Iterators { })); } + public static Iterable unique(final Iterable it) { + return isEmptyCollection(it)? Collections.emptyList() : new Iterable() { + @NotNull + @Override + public Iterator iterator() { + return unique(it.iterator()); + } + }; + } + + public static Iterator unique(final Iterator it) { + return filter(it, new BooleanFunction() { + private Set processed; + @Override + public boolean fun(T t) { + if (processed == null) { + processed = new HashSet<>(); + } + return processed.add(t); + } + }); + } + @SuppressWarnings("unchecked") public static BooleanFunction notNullFilter() { return (BooleanFunction)NOT_NULL_FILTER; diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/IntObjectPersistentMultiMaplet.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/IntObjectPersistentMultiMaplet.java index 93644f20546b..dffc89ca5e2a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/IntObjectPersistentMultiMaplet.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/IntObjectPersistentMultiMaplet.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.jps.builders.java.dependencyView; import com.intellij.util.containers.SLRUCache; @@ -103,16 +103,18 @@ public final class IntObjectPersistentMultiMaplet extends IntObjectMultiMaple @Override public void removeAll(int key, Collection values) { try { - final Collection collection = myCache.get(key); + if (!values.isEmpty()) { + final Collection collection = myCache.get(key); - if (collection != NULL_COLLECTION) { - if (collection.removeAll(values)) { - myCache.remove(key); - if (collection.isEmpty()) { - myMap.remove(key); - } - else { - myMap.put(key, collection); + if (collection != NULL_COLLECTION) { + if (collection.removeAll(values)) { + myCache.remove(key); + if (collection.isEmpty()) { + myMap.remove(key); + } + else { + myMap.put(key, collection); + } } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java index 9c1ac174d3c6..eb78f48fd840 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.jps.builders.java.dependencyView; import com.intellij.openapi.diagnostic.Logger; @@ -10,6 +10,7 @@ import com.intellij.util.containers.CollectionFactory; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FileCollectionFactory; import com.intellij.util.io.EnumeratorIntegerDescriptor; +import it.unimi.dsi.fastutil.ints.IntConsumer; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; @@ -30,6 +31,7 @@ import java.io.PrintStream; import java.lang.annotation.RetentionPolicy; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -135,7 +137,7 @@ public class Mappings { myRemovedSuperClasses = myIsDelta ? new IntIntTransientMultiMaplet() : null; myAddedSuperClasses = myIsDelta ? new IntIntTransientMultiMaplet() : null; - final Supplier> fileCollectionFactory = CollectionFactory::createFilePathSet; // todo: do we really need set and not a list here? + final Supplier> fileCollectionFactory = CollectionFactory::createFilePathSet; if (myIsDelta) { myClassToSubclasses = new IntIntTransientMultiMaplet(); myClassToClassDependency = new IntIntTransientMultiMaplet(); @@ -224,30 +226,31 @@ public class Mappings { } @Nullable - private ClassRepr getClassReprByName(final @Nullable File source, final int qName) { - final ClassFileRepr reprByName = getReprByName(source, qName); - return reprByName instanceof ClassRepr? (ClassRepr)reprByName : null; - } - - @Nullable - private ClassFileRepr getReprByName(@Nullable File source, int qName) { - final Iterable sources = source != null? Collections.singleton(toRelative(source)) : myClassToRelativeSourceFilePath.get(qName); - if (sources != null) { - for (String src : sources) { - final Collection reprs = myRelativeSourceFilePathToClasses.get(src); - if (reprs != null) { - for (ClassFileRepr repr : reprs) { - if (repr.name == qName) { - return repr; - } - } + private ClassFileRepr getReprByName(@NotNull File source, int qName) { + final Collection reprs = myRelativeSourceFilePathToClasses.get(toRelative(source)); + if (reprs != null) { + for (ClassFileRepr repr : reprs) { + if (repr.name == qName) { + return repr; } } } - return null; } + @NotNull + private Iterable getClassReprsByName(final int qName) { + return Iterators.filter(Iterators.map(getReprsByName(qName), repr -> repr instanceof ClassRepr? (ClassRepr)repr : null), Iterators.notNullFilter()); + } + + @NotNull + private Iterable getReprsByName(int qName) { + return Iterators.unique(Iterators.filter( + Iterators.flat(Iterators.map(myClassToRelativeSourceFilePath.get(qName), src -> myRelativeSourceFilePathToClasses.get(src))), + repr -> repr.name == qName + )); + } + private Collection sourceFileToClassesGet(File unchangedSource) { return myRelativeSourceFilePathToClasses.get(toRelative(unchangedSource)); } @@ -336,17 +339,22 @@ public class Mappings { if (acc.contains(reflcass)) { return acc; // SOE prevention } - final ClassRepr repr = classReprByName(reflcass); - if (repr != null) { + final Iterable reprs = classReprsByName(reflcass); + if (!Iterators.isEmpty(reprs)) { if (!root) { - final Set members = isField ? repr.getFields() : repr.getMethods(); - for (ProtoMember m : members) { - if (isSame.test(m)) { - return acc; + Iterable reprsWithoutMatchingMember = Iterators.filter(reprs, repr -> { + for (ProtoMember m : isField? repr.getFields() : repr.getMethods()) { + if (isSame.test(m)) { + return false; + } } + return true; + }); + if (Iterators.isEmpty(reprsWithoutMatchingMember)) { + return acc; } - + // should continue, if at least one repr does not have matching member defined acc.add(reflcass); } @@ -407,18 +415,30 @@ public class Mappings { } final IntSet _visitedClasses = visitedClasses; subClasses.forEach(subClassName -> { - final ClassRepr r = classReprByName(subClassName); - - if (r != null) { - boolean cont = true; - final Collection methods = r.findMethods(predicate); - for (MethodRepr mm : methods) { - if (isVisibleIn(fromClass, m, r)) { - container.add(Pair.create(mm, r)); - cont = false; + Iterable reprs = classReprsByName(subClassName); + if (!Iterators.isEmpty(reprs)) { + Iterable> overriding = Iterators.filter(Iterators.map(reprs, r -> { + for (MethodRepr mm : r.findMethods(predicate)) { + if (isVisibleIn(fromClass, m, r)) { + return Pair.create(mm, r); + } } + return null; + }), Iterators.notNullFilter()); + + for (Pair pair : overriding) { + container.add(pair); } - if (cont) { + + for (ClassRepr r : Iterators.filter(reprs, r -> { + for (Pair pair : overriding) { + if (pair.getSecond() == r) { + return false; + } + } + return true; + })) { + // continue with reprs, for those no overriding members were found addOverridingMethods(m, r, predicate, container, _visitedClasses); } } @@ -451,8 +471,7 @@ public class Mappings { if (!visitedClasses.add(superName.className) || superName.className == myObjectClassName) { continue; } - final ClassRepr superClass = classReprByName(superName.className); - if (superClass != null) { + for (ClassRepr superClass : classReprsByName(superName.className)) { for (MethodRepr mm : superClass.findMethods(predicate)) { if (isVisibleIn(superClass, mm, fromClass)) { return true; @@ -475,10 +494,15 @@ public class Mappings { if (!visitedClasses.add(superName.className) || superName.className == myObjectClassName) { continue; } - final ClassRepr superClass = classReprByName(superName.className); - if (superClass == null || extendsLibraryClass(superClass, visitedClasses)) { + Iterable superClasses = classReprsByName(superName.className); + if (Iterators.isEmpty(superClasses)) { return true; } + for (ClassRepr superClass : superClasses) { + if (extendsLibraryClass(superClass, visitedClasses)) { + return true; + } + } } return false; } @@ -492,17 +516,30 @@ public class Mappings { if (!visitedClasses.add(superName.className) || superName.className == myObjectClassName) { continue; // prevent SOE } - final ClassRepr superClass = classReprByName(superName.className); - if (superClass != null) { - boolean cont = true; - final Collection methods = superClass.findMethods(predicate); - for (MethodRepr mm : methods) { - if (isVisibleIn(superClass, mm, fromClass)) { - container.add(Pair.create(mm, superClass)); - cont = false; + Iterable superClasses = classReprsByName(superName.className); + if (!Iterators.isEmpty(superClasses)) { + Iterable> pairs = Iterators.filter(Iterators.map(superClasses, superClass -> { + for (MethodRepr mm : superClass.findMethods(predicate)) { + if (isVisibleIn(superClass, mm, fromClass)) { + return Pair.create(mm, superClass); + } } + return null; + }), Iterators.notNullFilter()); + + for (Pair pair : pairs) { + container.add(pair); } - if (cont) { + + for (ClassRepr superClass : Iterators.filter(superClasses, superClass -> { + for (Pair pair : pairs) { + if (pair.getSecond() == superClass) { + return false; + } + } + return true; + })) { + // continue with those, for whom the matching method was not found addOverridenMethods(superClass, predicate, container, visitedClasses); } } @@ -521,8 +558,7 @@ public class Mappings { if (!visitedClasses.add(supername.className) || supername.className == myObjectClassName) { continue; } - final ClassRepr superClass = classReprByName(supername.className); - if (superClass != null) { + for (ClassRepr superClass : classReprsByName(supername.className)) { final FieldRepr ff = superClass.findField(f.name); if (ff != null && isVisibleIn(superClass, ff, fromClass)) { container.add(Pair.create(ff, superClass)); @@ -543,14 +579,12 @@ public class Mappings { if (!visitedClasses.add(supername.className) || supername.className == myObjectClassName) { continue; } - final ClassRepr superClass = classReprByName(supername.className); - if (superClass != null) { + for (ClassRepr superClass : classReprsByName(supername.className)) { final FieldRepr ff = superClass.findField(f.name); if (ff != null && isVisibleIn(superClass, ff, fromClass)) { return true; } - final boolean found = hasOverriddenFields(f, superClass, visitedClasses); - if (found) { + if (hasOverriddenFields(f, superClass, visitedClasses)) { return true; } } @@ -560,17 +594,20 @@ public class Mappings { // test if a ClassRepr is a SAM interface boolean isLambdaTarget(int name) { - final ClassRepr cls = classReprByName(name); - if (cls == null || !cls.isInterface()) { - return false; - } - int amFound = 0; - for (MethodRepr method : allMethodsRecursively(cls)) { - if (method.isAbstract() && ++amFound > 1) { - return false; + for (ClassRepr cls : classReprsByName(name)) { + if (cls.isInterface()) { + int amFound = 0; + for (MethodRepr method : allMethodsRecursively(cls)) { + if (method.isAbstract() && ++amFound > 1) { + break; + } + } + if (amFound == 1) { + return true; + } } } - return amFound == 1; + return false; } private Iterable allMethodsRecursively(ClassRepr cls) { @@ -578,47 +615,45 @@ public class Mappings { } private Iterable findAllOverloads(final ClassRepr cls, Function correspondenceFinder) { - Function> converter = c -> c == null? Collections.emptyList() : Iterators.filter(Iterators.map(c.getMethods(), m -> { + Function> converter = c -> Iterators.filter(Iterators.map(c.getMethods(), m -> { Integer accessScope = correspondenceFinder.apply(m); return accessScope != null? new OverloadDescriptor(accessScope, m, c) : null; - }), Objects::nonNull); + }), Iterators.notNullFilter()); return Iterators.flat(Iterators.flat( collectRecursively(cls, converter), - Iterators.map(getAllSubclasses(cls.name), subName -> converter.apply(subName != cls.name? classReprByName(subName) : null)) + Iterators.map( + Iterators.flat(Iterators.map(getAllSubclasses(cls.name), subName -> subName != cls.name? classReprsByName(subName) : Collections.emptyList())), + repr -> converter.apply(repr) + ) )); } private Iterable collectRecursively(ClassRepr cls, Function mapper) { return Iterators.flat(Iterators.asIterable(mapper.apply(cls)), Iterators.flat(Iterators.map(cls.getSuperTypes(), st -> { - final ClassRepr cr = classReprByName(st.className); - return cr != null ? collectRecursively(cr, mapper) : Collections.emptyList(); + return Iterators.flat(Iterators.map(classReprsByName(st.className), cr -> collectRecursively(cr, mapper))); }))); } - @Nullable - ClassRepr classReprByName(final int name) { - final ClassFileRepr r = reprByName(name); - return r instanceof ClassRepr? (ClassRepr)r : null; + @NotNull + Iterable classReprsByName(final int name) { + return Iterators.filter(Iterators.map(reprsByName(name), r -> r instanceof ClassRepr? (ClassRepr)r : null), Iterators.notNullFilter()); } - @Nullable - ModuleRepr moduleReprByName(final int name) { - final ClassFileRepr r = reprByName(name); - return r instanceof ModuleRepr? (ModuleRepr)r : null; + @NotNull + Iterable moduleReprsByName(final int name) { + return Iterators.filter(Iterators.map(reprsByName(name), r -> r instanceof ModuleRepr? (ModuleRepr)r : null), Iterators.notNullFilter()); } - @Nullable - ClassFileRepr reprByName(final int name) { + @NotNull + Iterable reprsByName(final int name) { if (myMappings != null) { - final ClassFileRepr r = myMappings.getReprByName(null, name); - - if (r != null) { + Iterable r = myMappings.getReprsByName(name); + if (!Iterators.isEmpty(r)) { return r; } } - - return getReprByName(null, name); + return getReprsByName(name); } @Nullable @@ -627,9 +662,7 @@ public class Mappings { return Boolean.TRUE; } - final ClassRepr repr = classReprByName(who); - - if (repr != null) { + for (ClassRepr repr : classReprsByName(who)) { if (visitedClasses == null) { visitedClasses = new IntOpenHashSet(); visitedClasses.add(who); @@ -679,20 +712,27 @@ public class Mappings { } boolean isMethodVisible(final ClassRepr classRepr, final MethodRepr m) { - return classRepr.findMethods(MethodRepr.equalByJavaRules(m)).size() > 0 || hasOverriddenMethods(classRepr, MethodRepr.equalByJavaRules(m), null); + return !classRepr.findMethods(MethodRepr.equalByJavaRules(m)).isEmpty() || hasOverriddenMethods(classRepr, MethodRepr.equalByJavaRules(m), null); } boolean isFieldVisible(final int className, final FieldRepr field) { - final ClassRepr r = classReprByName(className); - if (r == null || r.getFields().contains(field)) { + final Iterable reprs = classReprsByName(className); + if (Iterators.isEmpty(reprs)) { return true; } - return hasOverriddenFields(field, r, null); + for (ClassRepr r : reprs) { + if (r.getFields().contains(field)) { + return true; + } + if (hasOverriddenFields(field, r, null)) { + return true; + } + } + return false; } void collectSupersRecursively(final int className, @NotNull final IntSet container) { - final ClassRepr classRepr = classReprByName(className); - if (classRepr != null) { + for (ClassRepr classRepr : classReprsByName(className)) { final Iterable supers = classRepr.getSuperTypes(); boolean added = false; for (TypeRepr.ClassType aSuper : supers) { @@ -732,10 +772,10 @@ public class Mappings { if (usages) { debug("Class usages affection requested"); - final ClassRepr classRepr = classReprByName(className); - if (classRepr != null) { + for (ClassRepr classRepr : classReprsByName(className)) { debug("Added class usage for ", classRepr.name); affectedUsages.add(classRepr.createUsage()); + break; } } @@ -846,11 +886,11 @@ public class Mappings { } depNames.forEach(depName -> { if (visited.add(depName)) { - final ClassFileRepr depRepr = reprByName(depName); - if (depRepr instanceof ModuleRepr) { + for (ModuleRepr depRepr : moduleReprsByName(depName)) { state.myDependants.add(depName); - if (checkTransitive && ((ModuleRepr)depRepr).requiresTransitevely(modName)) { + if (checkTransitive && depRepr.requiresTransitevely(modName)) { next.add(depName); + break; } } } @@ -1296,20 +1336,19 @@ public class Mappings { } getAllSubclasses(it.name).forEach(subClass -> { - final ClassRepr r = myFuture.classReprByName(subClass); - if (r == null) { + Iterable reprs = myFuture.classReprsByName(subClass); + if (Iterators.isEmpty(reprs)) { return; } final Iterable sourceFileNames = classToSourceFileGet(subClass); if (sourceFileNames != null && !containsAll(myCompiledFiles, sourceFileNames)) { - final int outerClass = r.getOuterClassName(); - if (!isEmpty(outerClass)) { - final ClassRepr outerClassRepr = myFuture.classReprByName(outerClass); - if (outerClassRepr != null && (myFuture.isMethodVisible(outerClassRepr, addedMethod) || myFuture.extendsLibraryClass(outerClassRepr, null))) { + for (ClassRepr outerClassRepr : Iterators.flat(Iterators.map(reprs, r -> isEmpty(r.getOuterClassName())? Collections.emptyList() : myFuture.classReprsByName(r.getOuterClassName())))) { + if (myFuture.isMethodVisible(outerClassRepr, addedMethod) || myFuture.extendsLibraryClass(outerClassRepr, null)) { ContainerUtil.addAll(myAffectedFiles, sourceFileNames); for (File sourceFileName : sourceFileNames) { debug("Affecting file due to local overriding: ", sourceFileName); } + break; } } } @@ -1341,7 +1380,7 @@ public class Mappings { myFuture.affectStaticMemberImportUsages(m.name, it.name, propagated.get(), state.myAffectedUsages, state.myDependants); } - if (overriddenMethods.size() == 0) { + if (overriddenMethods.isEmpty()) { debug("No overridden methods found, affecting method usages"); myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants); } @@ -1383,9 +1422,7 @@ public class Mappings { if (!m.isAbstract() && !m.isStatic()) { propagated.get().forEach(p -> { if (p != it.name) { - final ClassRepr s = myFuture.classReprByName(p); - - if (s != null) { + for (ClassRepr s : myFuture.classReprsByName(p)) { final Collection> overridenInS = myFuture.findOverriddenMethods(m, s); overridenInS.addAll(overriddenMethods); @@ -1415,7 +1452,6 @@ public class Mappings { if (allAbstract && visited) { final Iterable sources = classToSourceFileGet(p); - if (sources != null && !containsAll(myCompiledFiles, sources)) { ContainerUtil.addAll(myAffectedFiles, sources); debug("Removed method is not abstract & overrides some abstract method which is not then over-overridden in subclass ", p); @@ -1423,6 +1459,7 @@ public class Mappings { debug("Affecting subclass source file ", source); } } + break; // classReprs } } } @@ -1668,8 +1705,12 @@ public class Mappings { state.myAffectedUsages.add(usage); // only mark synthetic classes used to implement switch statements: this will limit the number of recompiled classes to those where switch statements on changed enum are used state.myUsageConstraints.put(usage, residence -> { - final ClassRepr candidate = myPresent.classReprByName(residence); - return candidate != null && candidate.isSynthetic(); + for (ClassRepr candidate : myPresent.classReprsByName(residence)) { + if (candidate.isSynthetic()) { + return true; + } + } + return false; }); } @@ -1678,23 +1719,27 @@ public class Mappings { if (!f.isPrivate()) { getAllSubclasses(classRepr.name).forEach(subClass -> { - final ClassRepr r = myFuture.classReprByName(subClass); - if (r != null) { + final Iterable reprs = myFuture.classReprsByName(subClass); + if (!Iterators.isEmpty(reprs)) { final Iterable sourceFileNames = classToSourceFileGet(subClass); if (sourceFileNames != null && !containsAll(myCompiledFiles, sourceFileNames)) { - if (r.isLocal()) { - for (File sourceFileName : sourceFileNames) { - debug("Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", sourceFileName); - myAffectedFiles.add(sourceFileName); - } - } - else { - final int outerClass = r.getOuterClassName(); - if (!isEmpty(outerClass) && myFuture.isFieldVisible(outerClass, f)) { + for (ClassRepr r : reprs) { + if (r.isLocal()) { for (File sourceFileName : sourceFileNames) { - debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", sourceFileName); + debug("Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): ", sourceFileName); myAffectedFiles.add(sourceFileName); } + break; + } + else { + final int outerClass = r.getOuterClassName(); + if (!isEmpty(outerClass) && myFuture.isFieldVisible(outerClass, f)) { + for (File sourceFileName : sourceFileNames) { + debug("Affecting inner subclass (introduced field can potentially hide surrounding class fields): ", sourceFileName); + myAffectedFiles.add(sourceFileName); + } + break; + } } } } @@ -1949,11 +1994,9 @@ public class Mappings { if (superClassChanged) { myDelta.registerRemovedSuperClass(changedClass.name, changedClass.getSuperClass().className); - final ClassRepr newClass = myDelta.getClassReprByName(null, changedClass.name); - - assert (newClass != null); - - myDelta.registerAddedSuperClass(changedClass.name, newClass.getSuperClass().className); + for (ClassRepr newClass : myDelta.getClassReprsByName(changedClass.name)) { + myDelta.registerAddedSuperClass(changedClass.name, newClass.getSuperClass().className); + } } if (interfacesChanged) { @@ -1988,8 +2031,7 @@ public class Mappings { if (extendsChanged && directDeps != null) { final TypeRepr.ClassType excClass = TypeRepr.createClassType(myContext, changedClass.name); directDeps.forEach(depClass -> { - final ClassRepr depClassRepr = myPresent.classReprByName(depClass); - if (depClassRepr != null) { + for (ClassRepr depClassRepr : myPresent.classReprsByName(depClass)) { myPresent.affectMethodUsagesThrowing(depClassRepr, excClass, state.myAffectedUsages, state.myDependants); } }); @@ -2284,75 +2326,91 @@ public class Mappings { debug("Checking dependent classes:"); assert myAffectedFiles != null; assert myCompiledFiles != null; - IntIterator iterator = state.myDependants.iterator(); - while (iterator.hasNext()) { - int depClass = iterator.nextInt(); - Iterable depFiles = classToSourceFileGet(depClass); - if (depFiles != null) { - for (File depFile : depFiles) { - if (!processDependentFile(depClass, depFile, state)) { - debug("Turning non-incremental for the BuildTarget because dependent class is annotation-processor generated"); - return false; - } - } - } - } - return true; - } - @SuppressWarnings("DataFlowIssue") - private boolean processDependentFile(int depClass, @NotNull File depFile, DiffState state) { - debug("Dependent class: ", depClass); - - final ClassFileRepr repr = getReprByName(depFile, depClass); - if (repr == null) { + if (state.myDependants.isEmpty()) { return true; } - boolean isGenerated = false; - if (repr instanceof ClassRepr) { - final ClassRepr clsRepr = (ClassRepr)repr; - if (!clsRepr.hasInlinedConstants() && myCompiledFiles.contains(depFile)) { - // Classes containing inlined constants from other classes and compiled against older constant values - // may need to be recompiled several times within compile session. - // Otherwise, it is safe to skip the file if it has already been compiled in this session. - return true; + + BiFunction dependentReprProcessor = new BiFunction<>() { + private final Map> isAffected = new HashMap<>(); + + @Override + public Boolean apply(ClassFileRepr repr, File depFile) { + final boolean isGenerated; + if (repr instanceof ClassRepr) { + final ClassRepr clsRepr = (ClassRepr)repr; + if (!clsRepr.hasInlinedConstants() && myCompiledFiles.contains(depFile)) { + // Classes containing inlined constants from other classes and compiled against older constant values + // may need to be recompiled several times within compile session. + // Otherwise, it is safe to skip the file if it has already been compiled in this session. + return true; + } + + // If among affected files are annotation processor-generated, then we might need to re-generate them. + // To achieve this, we need to recompile the whole chunk which will cause processors to re-generate these affected files + isGenerated = clsRepr.isGenerated(); + } + else { + isGenerated = false; + } + + if (myAffectedFiles.contains(depFile)) { + return !isGenerated; + } + + Pair shouldAffect = isAffected.computeIfAbsent(repr, r -> { + debug("Dependent class: ", r.name); + final Set depUsages = r.getUsages(); + if (depUsages == null || depUsages.isEmpty()) { + return Pair.create(false, !isGenerated); + } + for (UsageRepr.Usage usage : depUsages) { + if (usage instanceof UsageRepr.AnnotationUsage) { + final UsageRepr.AnnotationUsage annotationUsage = (UsageRepr.AnnotationUsage)usage; + for (final UsageRepr.AnnotationUsage query : state.myAnnotationQuery) { + if (query.satisfies(annotationUsage)) { + debug("Added file due to annotation query"); + return Pair.create(true, !isGenerated); + } + } + } + else if (state.myAffectedUsages.contains(usage)) { + final UsageConstraint constraint = state.myUsageConstraints.get(usage); + if (constraint == null) { + debug("Added file with no constraints"); + return Pair.create(true, !isGenerated); + } + if (constraint.checkResidence(r.name)) { + debug("Added file with satisfied constraint"); + return Pair.create(true, !isGenerated); + } + } + } + return Pair.create(false, true); + }); + + if (shouldAffect.getFirst()) { + myAffectedFiles.add(depFile); + } + return shouldAffect.getSecond(); } - - // If among affected files are annotation processor-generated, then we might need to re-generate them. - // To achieve this, we need to recompile the whole chunk which will cause processors to re-generate these affected files - isGenerated = clsRepr.isGenerated(); - } + }; - if (myAffectedFiles.contains(depFile)) { - return !isGenerated; - } - final Set depUsages = repr.getUsages(); - if (depUsages == null || depUsages.isEmpty()) { - return !isGenerated; - } - - for (UsageRepr.Usage usage : depUsages) { - if (usage instanceof UsageRepr.AnnotationUsage) { - final UsageRepr.AnnotationUsage annotationUsage = (UsageRepr.AnnotationUsage)usage; - for (final UsageRepr.AnnotationUsage query : state.myAnnotationQuery) { - if (query.satisfies(annotationUsage)) { - debug("Added file due to annotation query"); - myAffectedFiles.add(depFile); - return !isGenerated; + for (IntIterator dependants = state.myDependants.iterator(); dependants.hasNext(); ) { + int depName = dependants.nextInt(); + Iterable> dependentReprs = Iterators.filter(Iterators.map(Iterators.map(myClassToRelativeSourceFilePath.get(depName), src -> Pair.create(myRelativeSourceFilePathToClasses.get(src), toFull(src))), p -> { + for (ClassFileRepr repr : p.getFirst()) { + if (repr.name == depName) { + return Pair.create(repr, p.getSecond()); } } - } - else if (state.myAffectedUsages.contains(usage)) { - final UsageConstraint constraint = state.myUsageConstraints.get(usage); - if (constraint == null) { - debug("Added file with no constraints"); - myAffectedFiles.add(depFile); - return !isGenerated; - } - if (constraint.checkResidence(depClass)) { - debug("Added file with satisfied constraint"); - myAffectedFiles.add(depFile); - return !isGenerated; + return null; + }), Iterators.notNullFilter()); + + for (Pair pair : dependentReprs) { + if (!dependentReprProcessor.apply(pair.getFirst(), pair.getSecond())) { + debug("Turning non-incremental for the BuildTarget because dependent class is annotation-processor generated"); + return false; } } } @@ -2509,12 +2567,9 @@ public class Mappings { myPresent.affectDependentModules(state, moduleRepr.name, new UsageConstraint() { @Override public boolean checkResidence(int dep) { - final ModuleRepr depModule = myPresent.moduleReprByName(dep); - if (depModule != null) { - for (ModuleRequiresRepr requires : depModule.getRequires()) { - if (requires.name == moduleRepr.name && requires.getVersion() == version) { - return true; - } + for (ModuleRequiresRepr requires : Iterators.flat(Iterators.map(myPresent.moduleReprsByName(dep), depModule -> depModule.getRequires()))) { + if (requires.name == moduleRepr.name && requires.getVersion() == version) { + return true; } } return false; @@ -2612,20 +2667,13 @@ public class Mappings { return new Differential(delta, removed, filesToCompile, compiledWithErrors, compiledFiles, affectedFiles, filter).differentiate(); } - private void cleanupBackDependency(final int className, @Nullable Set usages, final IntIntMultiMaplet buffer) { + private void cleanupBackDependency(final int className, @Nullable Iterable usages, final IntIntMultiMaplet buffer) { if (usages == null) { - final ClassFileRepr repr = getReprByName(null, className); - if (repr != null) { - usages = repr.getUsages(); - } + usages = Iterators.flat(Iterators.map(getReprsByName(className), repr -> repr.getUsages())); } - - if (usages != null) { - for (final UsageRepr.Usage u : usages) { - final int owner = u.getOwner(); - if (owner != className) { - buffer.put(owner, className); - } + for (Integer owner : Iterators.unique(Iterators.map(usages, usage -> usage.getOwner()))) { + if (owner != className) { + buffer.put(owner, className); } } } @@ -2653,6 +2701,8 @@ public class Mappings { // many files for (File file : currentlyMapped) { if (!FileUtil.filesEqual(sourceFile, file) && file.exists()) { + // ensure association with particular sourceFile is removed + myClassToRelativeSourceFilePath.removeFrom(className, toRelative(sourceFile)); return; } } @@ -2755,18 +2805,40 @@ public class Mappings { } }); - delta.getChangedClasses().forEach(className -> { - final Collection sourceFiles = delta.myClassToRelativeSourceFilePath.get(className); - myClassToRelativeSourceFilePath.replace(className, sourceFiles); + final Set changedRelativePaths = CollectionFactory.createFilePathSet(); + for (File file : delta.getChangedFiles()) { + changedRelativePaths.add(toRelative(file)); + } - cleanupBackDependency(className, null, dependenciesTrashBin); + delta.getChangedClasses().forEach(new IntConsumer() { + final Set pathsBuffer = CollectionFactory.createFilePathSet(); + @Override + public void accept(int className) { + Collection currentPaths = myClassToRelativeSourceFilePath.get(className); + if (currentPaths != null && !currentPaths.isEmpty()) { + try { + pathsBuffer.addAll(currentPaths); + pathsBuffer.removeAll(changedRelativePaths); + pathsBuffer.addAll(delta.myClassToRelativeSourceFilePath.get(className)); + if (pathsBuffer.size() != currentPaths.size() || !pathsBuffer.containsAll(currentPaths)) { + myClassToRelativeSourceFilePath.replace(className, pathsBuffer); + } + } + finally { + pathsBuffer.clear(); + } + } + else { + myClassToRelativeSourceFilePath.replace(className, delta.myClassToRelativeSourceFilePath.get(className)); + } + + cleanupBackDependency(className, null, dependenciesTrashBin); + } }); - delta.getChangedFiles().forEach(fileName -> { - String relative = toRelative(fileName); - Collection classes = delta.myRelativeSourceFilePathToClasses.get(relative); - myRelativeSourceFilePathToClasses.replace(relative, classes); - }); + for (String path : changedRelativePaths) { + myRelativeSourceFilePathToClasses.replace(path, delta.myRelativeSourceFilePathToClasses.get(path)); + } // some classes may be associated with multiple sources. // In case some of these sources was not compiled, but the class was changed, we need to update @@ -2807,7 +2879,7 @@ public class Mappings { } else { myClassToSubclasses.putAll(delta.myClassToSubclasses); - myClassToRelativeSourceFilePath.replaceAll(delta.myClassToRelativeSourceFilePath); + myClassToRelativeSourceFilePath.putAll(delta.myClassToRelativeSourceFilePath); myRelativeSourceFilePathToClasses.replaceAll(delta.myRelativeSourceFilePathToClasses); delta.myRelativeSourceFilePathToClasses.forEachEntry( (src, classes) -> { @@ -3185,7 +3257,7 @@ public class Mappings { } private static Supplier lazy(Supplier calculation) { - return new Supplier() { + return new Supplier<>() { Ref calculated; @Override public T get() { diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/ClassPropertyTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/ClassPropertyTest.java index 8c90dc13689e..49cd90d6f791 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/ClassPropertyTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/ClassPropertyTest.java @@ -1,20 +1,9 @@ -/* - * Copyright 2000-2012 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. - */ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.ether; +import org.jetbrains.jps.model.JpsModuleRootModificationUtil; +import org.jetbrains.jps.model.module.JpsModule; + public class ClassPropertyTest extends IncrementalTestCase { public ClassPropertyTest() { super("classProperties"); @@ -63,4 +52,11 @@ public class ClassPropertyTest extends IncrementalTestCase { public void testConvertToCheckedException() { doTest(); } + + public void testConvertToCheckedExceptionMultiModule() { + JpsModule module1 = addModule("module1", "module1/src"); + JpsModule module2 = addModule("module2", "module2/src"); + JpsModuleRootModificationUtil.addDependency(module2, module1); + doTestBuild(1).assertSuccessful(); + } } diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java index d0e36503dfc3..4e317bef2df6 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.ether; import com.intellij.testFramework.PlatformTestUtil; @@ -141,6 +141,13 @@ public class CommonTest extends IncrementalTestCase { doTestBuild(1).assertSuccessful(); } + public void testSameClassesInDifferentModules() { + JpsModule moduleA = addModule("moduleA", "moduleA/src"); + JpsModule moduleB = addModule("moduleB", "moduleB/src"); + JpsModuleRootModificationUtil.addDependency(moduleB, moduleA); // ensure compilation sequence + doTestBuild(1).assertSuccessful(); + } + public void testCompileDependenciesOnMovedClassesInFirstRound() { doTest().assertSuccessful(); } @@ -168,12 +175,10 @@ public class CommonTest extends IncrementalTestCase { doTest(); } - // Disabled because now several classes with the same qName in different modules are not supporter - // - //public void testConflictingClasses() { - // JpsModule module1 = addModule("module1", "module1/src"); - // JpsModule module2 = addModule("module2", "module2/src"); - // JpsModuleRootModificationUtil.addDependency(module2, module1); - // doTestBuild(1).assertSuccessful(); - //} + public void testConflictingClasses() { + JpsModule module1 = addModule("module1", "module1/src"); + JpsModule module2 = addModule("module2", "module2/src"); + JpsModuleRootModificationUtil.addDependency(module2, module1); + doTestBuild(1).assertSuccessful(); + } }