JetExceptionFilter handles correctly the case of several files with the same name
Extract the logic to DebuggerUtils.findSourceFileForClass() This fixes navigation from stack trace to a file, when there's more than one file with this name in this package in the project
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.filters;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.execution.filters.ExceptionFilter;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.execution.filters.HyperlinkInfo;
|
||||
@@ -28,12 +26,10 @@ import com.intellij.psi.search.GlobalSearchScope;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil;
|
||||
import org.jetbrains.jet.lang.resolve.java.JetFilesProvider;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
import org.jetbrains.jet.plugin.util.DebuggerUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -51,42 +47,31 @@ public class JetExceptionFilter implements Filter {
|
||||
Project project = searchScope.getProject();
|
||||
if (project == null) return null;
|
||||
|
||||
final StackTraceElement element = parseStackTraceLine(line);
|
||||
StackTraceElement element = parseStackTraceLine(line);
|
||||
if (element == null) return null;
|
||||
|
||||
// We don't want to rely on FqName here, since this name can contain dollar signs
|
||||
String fileName = element.getFileName();
|
||||
|
||||
// fullyQualifiedName is of format "package.Class$Inner"
|
||||
String fullyQualifiedName = element.getClassName();
|
||||
|
||||
int lastDot = fullyQualifiedName.lastIndexOf('.');
|
||||
String classNameWithInners = fullyQualifiedName.substring(lastDot + 1);
|
||||
final String packageName = lastDot >= 0 ? fullyQualifiedName.substring(0, lastDot) : "";
|
||||
|
||||
// All classes except 'namespace' and its inner classes are handled correctly in the default ExceptionFilter
|
||||
if (!classNameWithInners.equals(JvmAbi.PACKAGE_CLASS) && !classNameWithInners.startsWith(JvmAbi.PACKAGE_CLASS + "$")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only consider files with the file name from the stack trace and in the given package
|
||||
Collection<JetFile> files = Collections2
|
||||
.filter(JetFilesProvider.getInstance(project).allInScope(searchScope), new Predicate<JetFile>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable JetFile file) {
|
||||
return file != null
|
||||
&& file.getName().equals(element.getFileName())
|
||||
&& JetPsiUtil.getFQName(file).getFqName().equals(packageName);
|
||||
}
|
||||
});
|
||||
String internalName = fullyQualifiedName.replace('.', '/');
|
||||
JvmClassName jvmClassName = JvmClassName.byInternalName(internalName);
|
||||
|
||||
if (files.isEmpty()) return null;
|
||||
JetFile file = DebuggerUtils.findSourceFileForClass(searchScope, jvmClassName, fileName);
|
||||
|
||||
if (files.size() == 1) {
|
||||
JetFile file = files.iterator().next();
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
return virtualFile == null ? null : new OpenFileHyperlinkInfo(project, virtualFile, element.getLineNumber() - 1);
|
||||
}
|
||||
if (file == null) return null;
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
// TODO multiple files with the same name within the same package
|
||||
|
||||
return null;
|
||||
return new OpenFileHyperlinkInfo(project, virtualFile, element.getLineNumber() - 1);
|
||||
}
|
||||
|
||||
// Matches strings like "\tat test.namespace$foo$f$1.invoke(a.kt:3)\n"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.util;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
|
||||
import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor;
|
||||
import org.jetbrains.jet.lang.psi.JetFile;
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil;
|
||||
import org.jetbrains.jet.lang.resolve.java.JetFilesProvider;
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.plugin.project.WholeProjectAnalyzerFacade;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class DebuggerUtils {
|
||||
@Nullable
|
||||
public static JetFile findSourceFileForClass(
|
||||
@NotNull GlobalSearchScope searchScope,
|
||||
@NotNull JvmClassName className,
|
||||
@NotNull final String fileName
|
||||
) {
|
||||
JetFilesProvider filesProvider = JetFilesProvider.getInstance(searchScope.getProject());
|
||||
Collection<JetFile> filesInScope = filesProvider.allInScope(searchScope);
|
||||
|
||||
final FqName packageFqName = className.getOuterClassFqName().parent();
|
||||
|
||||
// Only consider files with the file name from the stack trace and in the given package
|
||||
Collection<JetFile> files = Collections2.filter(filesInScope, new Predicate<JetFile>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable JetFile file) {
|
||||
return file != null
|
||||
&& file.getName().equals(fileName)
|
||||
&& JetPsiUtil.getFQName(file).equals(packageFqName);
|
||||
}
|
||||
});
|
||||
|
||||
if (files.isEmpty()) return null;
|
||||
|
||||
JetFile anyFile = files.iterator().next();
|
||||
if (files.size() == 1) {
|
||||
return anyFile;
|
||||
}
|
||||
|
||||
List<JetFile> allNamespaceFiles = filesProvider.allNamespaceFiles().fun(anyFile);
|
||||
JetFile file = PsiCodegenPredictor.getFileForNamespacePartName(allNamespaceFiles, className);
|
||||
if (file != null) {
|
||||
return file;
|
||||
}
|
||||
|
||||
// In the rare case that there's more than one file with this name in this package,
|
||||
// we may actually need to analyze the project in order to find a file which produces this class
|
||||
AnalyzeExhaust analyzeExhaust = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile(anyFile);
|
||||
|
||||
return PsiCodegenPredictor.getFileForCodegenNamedClass(analyzeExhaust.getBindingContext(), allNamespaceFiles, className);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package multiSameName
|
||||
|
||||
fun foo() {
|
||||
val f = { null!! }
|
||||
f()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package multiSameName
|
||||
|
||||
fun foo(x: Int) {
|
||||
val f = { null!! }
|
||||
f()
|
||||
}
|
||||
@@ -110,4 +110,10 @@ public class JetExceptionFilterTest extends MultiFileTestCase {
|
||||
doTest("a.kt", "namespace$a$f$1", 3);
|
||||
doTest("main.kt", "namespace$main$f$1", 3);
|
||||
}
|
||||
|
||||
public void testMultiSameName() {
|
||||
// The order and the exact names do matter here
|
||||
doTest("1/foo.kt", "multiSameName.namespace$foo$f$1", 4);
|
||||
doTest("2/foo.kt", "multiSameName.namespace$foo$f$2", 4);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user