EA-43573 - assert: KotlinJavaFileStubProvider$.generate

The problem was that KotlinJavaFileStubProvider instances remembered information that changed between their invocation:
 e.g. when a package gets renamed the qualified name in a java file stub does not get updated
This commit is contained in:
Andrey Breslav
2013-04-01 13:09:22 +04:00
parent d57003dece
commit ffeb73beb5
3 changed files with 68 additions and 27 deletions
@@ -115,7 +115,7 @@ public class JavaElementFinder extends PsiElementFinder implements JavaPsiFacade
Collection<JetFile> filesForPackage = lightClassGenerationSupport.findFilesForPackage(qualifiedName, scope); Collection<JetFile> filesForPackage = lightClassGenerationSupport.findFilesForPackage(qualifiedName, scope);
if (!filesForPackage.isEmpty() && NamespaceCodegen.shouldGenerateNSClass(filesForPackage)) { if (!filesForPackage.isEmpty() && NamespaceCodegen.shouldGenerateNSClass(filesForPackage)) {
KotlinLightClassForPackage lightClass = KotlinLightClassForPackage.create(psiManager, qualifiedName, filesForPackage); KotlinLightClassForPackage lightClass = KotlinLightClassForPackage.create(psiManager, qualifiedName, scope, filesForPackage);
if (lightClass != null) { if (lightClass != null) {
answer.add(lightClass); answer.add(lightClass);
@@ -28,6 +28,7 @@ import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.compiled.ClsFileImpl; import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl; import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.PsiClassHolderFileStub; import com.intellij.psi.stubs.PsiClassHolderFileStub;
import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValueProvider;
@@ -55,13 +56,28 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
@NotNull @NotNull
public static KotlinJavaFileStubProvider createForPackageClass( public static KotlinJavaFileStubProvider createForPackageClass(
@NotNull Project project, @NotNull final Project project,
@NotNull final FqName packageFqName, @NotNull final FqName packageFqName,
@NotNull final Collection<JetFile> files @NotNull final GlobalSearchScope searchScope
) { ) {
return new KotlinJavaFileStubProvider(project, packageFqName, files, new StubGenerationStrategy.NoDeclaredClasses() { return new KotlinJavaFileStubProvider(project, new StubGenerationStrategy.NoDeclaredClasses() {
@NotNull
@Override @Override
public void generate(GenerationState state) { public Collection<JetFile> getFiles() {
// Don't memoize this, it can be called again after an out-of-code-block modification occurs,
// and the set of files changes
return LightClassGenerationSupport.getInstance(project).findFilesForPackage(packageFqName, searchScope);
}
@NotNull
@Override
public FqName getPackageFqName() {
return packageFqName;
}
@Override
public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) {
NamespaceCodegen codegen = state.getFactory().forNamespace(packageFqName, files); NamespaceCodegen codegen = state.getFactory().forNamespace(packageFqName, files);
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION); codegen.generate(CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().files(); state.getFactory().files();
@@ -73,16 +89,29 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
public static KotlinJavaFileStubProvider createForDeclaredTopLevelClass( public static KotlinJavaFileStubProvider createForDeclaredTopLevelClass(
@NotNull final JetClassOrObject classOrObject @NotNull final JetClassOrObject classOrObject
) { ) {
JetFile file = (JetFile) classOrObject.getContainingFile(); return new KotlinJavaFileStubProvider(classOrObject.getProject(), new StubGenerationStrategy.WithDeclaredClasses() {
assert classOrObject.getParent() == file : "Not a top-level class: " + classOrObject.getText(); private JetFile getFile() {
JetFile file = (JetFile) classOrObject.getContainingFile();
assert classOrObject.getParent() == file : "Not a top-level class: " + classOrObject.getText();
return file;
}
final FqName packageFqName = JetPsiUtil.getFQName(file); @NotNull
return new KotlinJavaFileStubProvider(classOrObject.getProject(), packageFqName, Collections.singletonList(file), new StubGenerationStrategy.WithDeclaredClasses() {
@Override @Override
public void generate(GenerationState state) { public Collection<JetFile> getFiles() {
return Collections.singletonList(getFile());
}
NamespaceCodegen namespaceCodegen = state.getFactory().forNamespace(packageFqName, state.getFiles()); @NotNull
@Override
public FqName getPackageFqName() {
return JetPsiUtil.getFQName(getFile());
}
@Override
public void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files) {
FqName packageFqName = getPackageFqName();
NamespaceCodegen namespaceCodegen = state.getFactory().forNamespace(packageFqName, files);
NamespaceDescriptor namespaceDescriptor = NamespaceDescriptor namespaceDescriptor =
state.getBindingContext().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, packageFqName); state.getBindingContext().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, packageFqName);
assert namespaceDescriptor != null : "No package descriptor for " + packageFqName + " for class " + classOrObject.getText(); assert namespaceDescriptor != null : "No package descriptor for " + packageFqName + " for class " + classOrObject.getText();
@@ -95,19 +124,13 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
private static final Logger LOG = Logger.getInstance(KotlinJavaFileStubProvider.class); private static final Logger LOG = Logger.getInstance(KotlinJavaFileStubProvider.class);
private final Collection<JetFile> files;
private final FqName packageFqName;
private final Project project; private final Project project;
private final StubGenerationStrategy stubGenerationStrategy; private final StubGenerationStrategy stubGenerationStrategy;
private KotlinJavaFileStubProvider( private KotlinJavaFileStubProvider(
@NotNull Project project, @NotNull Project project,
@NotNull FqName packageFqName,
@NotNull Collection<JetFile> files,
@NotNull StubGenerationStrategy stubGenerationStrategy @NotNull StubGenerationStrategy stubGenerationStrategy
) { ) {
this.files = files;
this.packageFqName = packageFqName;
this.project = project; this.project = project;
this.stubGenerationStrategy = stubGenerationStrategy; this.stubGenerationStrategy = stubGenerationStrategy;
} }
@@ -115,6 +138,9 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
@Nullable @Nullable
@Override @Override
public Result<PsiJavaFileStub> compute() { public Result<PsiJavaFileStub> compute() {
FqName packageFqName = stubGenerationStrategy.getPackageFqName();
Collection<JetFile> files = stubGenerationStrategy.getFiles();
checkForBuiltIns(packageFqName, files); checkForBuiltIns(packageFqName, files);
LightClassConstructionContext context = LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files); LightClassConstructionContext context = LightClassGenerationSupport.getInstance(project).analyzeRelevantCode(files);
@@ -124,7 +150,7 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
throw new IllegalStateException("failed to analyze: " + error, error); throw new IllegalStateException("failed to analyze: " + error, error);
} }
PsiJavaFileStub javaFileStub = createJavaFileStub(getRepresentativeVirtualFile(files)); PsiJavaFileStub javaFileStub = createJavaFileStub(packageFqName, getRepresentativeVirtualFile(files));
try { try {
Stack<StubElement> stubStack = new Stack<StubElement>(); Stack<StubElement> stubStack = new Stack<StubElement>();
stubStack.push(javaFileStub); stubStack.push(javaFileStub);
@@ -140,7 +166,7 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
/*generateDeclaredClasses=*/stubGenerationStrategy.generateDeclaredClasses()); /*generateDeclaredClasses=*/stubGenerationStrategy.generateDeclaredClasses());
state.beforeCompile(); state.beforeCompile();
stubGenerationStrategy.generate(state); stubGenerationStrategy.generate(state, files);
StubElement pop = stubStack.pop(); StubElement pop = stubStack.pop();
if (pop != javaFileStub) { if (pop != javaFileStub) {
@@ -160,7 +186,7 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
} }
@NotNull @NotNull
private PsiJavaFileStub createJavaFileStub(VirtualFile virtualFile) { private PsiJavaFileStub createJavaFileStub(@NotNull final FqName packageFqName, @NotNull VirtualFile virtualFile) {
PsiManager manager = PsiManager.getInstance(project); PsiManager manager = PsiManager.getInstance(project);
final PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(packageFqName.getFqName(), true); final PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(packageFqName.getFqName(), true);
@@ -214,8 +240,10 @@ public class KotlinJavaFileStubProvider implements CachedValueProvider<PsiJavaFi
} }
private interface StubGenerationStrategy { private interface StubGenerationStrategy {
@NotNull Collection<JetFile> getFiles();
@NotNull FqName getPackageFqName();
boolean generateDeclaredClasses(); boolean generateDeclaredClasses();
void generate(GenerationState state); void generate(@NotNull GenerationState state, @NotNull Collection<JetFile> files);
abstract class NoDeclaredClasses implements StubGenerationStrategy { abstract class NoDeclaredClasses implements StubGenerationStrategy {
@Override @Override
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager; import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub; import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.CachedValuesManager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -38,27 +39,39 @@ import java.util.Collection;
public class KotlinLightClassForPackage extends KotlinLightClassForPackageBase implements KotlinLightClass, JetJavaMirrorMarker { public class KotlinLightClassForPackage extends KotlinLightClassForPackageBase implements KotlinLightClass, JetJavaMirrorMarker {
@Nullable @Nullable
public static KotlinLightClassForPackage create(@NotNull PsiManager manager, @NotNull FqName qualifiedName, @NotNull Collection<JetFile> files) { public static KotlinLightClassForPackage create(
@NotNull PsiManager manager,
@NotNull FqName qualifiedName,
@NotNull GlobalSearchScope searchScope,
@NotNull Collection<JetFile> files // this is redundant, but computing it multiple times is costly
) {
for (JetFile file : files) { for (JetFile file : files) {
if (LightClassUtil.belongsToKotlinBuiltIns(file)) return null; if (LightClassUtil.belongsToKotlinBuiltIns(file)) return null;
} }
return new KotlinLightClassForPackage(manager, qualifiedName, files); return new KotlinLightClassForPackage(manager, qualifiedName, searchScope, files);
} }
private final FqName packageFqName; private final FqName packageFqName;
private final FqName packageClassFqName; // derived from packageFqName private final FqName packageClassFqName; // derived from packageFqName
private final GlobalSearchScope searchScope;
private final Collection<JetFile> files; private final Collection<JetFile> files;
private final int hashCode; private final int hashCode;
private final CachedValue<PsiJavaFileStub> javaFileStub; private final CachedValue<PsiJavaFileStub> javaFileStub;
private KotlinLightClassForPackage(@NotNull PsiManager manager, @NotNull FqName packageFqName, @NotNull Collection<JetFile> files) { private KotlinLightClassForPackage(
@NotNull PsiManager manager,
@NotNull FqName packageFqName,
@NotNull GlobalSearchScope searchScope,
@NotNull Collection<JetFile> files
) {
super(manager); super(manager);
this.packageFqName = packageFqName; this.packageFqName = packageFqName;
this.packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName); this.packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName);
this.searchScope = searchScope;
assert !files.isEmpty() : "No files for package " + packageFqName; assert !files.isEmpty() : "No files for package " + packageFqName;
this.files = Sets.newHashSet(files); // needed for hashCode this.files = Sets.newHashSet(files); // needed for hashCode
this.hashCode = computeHashCode(); this.hashCode = computeHashCode();
KotlinJavaFileStubProvider stubProvider = KotlinJavaFileStubProvider.createForPackageClass(getProject(), packageFqName, files); KotlinJavaFileStubProvider stubProvider = KotlinJavaFileStubProvider.createForPackageClass(getProject(), packageFqName, searchScope);
this.javaFileStub = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider, /*trackValue = */false); this.javaFileStub = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider, /*trackValue = */false);
} }
@@ -95,7 +108,7 @@ public class KotlinLightClassForPackage extends KotlinLightClassForPackageBase i
@NotNull @NotNull
@Override @Override
public PsiElement copy() { public PsiElement copy() {
return new KotlinLightClassForPackage(getManager(), packageFqName, files); return new KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files);
} }
@NotNull @NotNull