mirror of
https://github.com/github/codeql.git
synced 2026-05-24 16:17:07 +02:00
Merge pull request #18134 from smowton/smowton/ke2/external-class-extraction
KE2: basic external class extraction
This commit is contained in:
@@ -23,13 +23,11 @@ import com.github.codeql.Logger;
|
||||
//import static com.github.codeql.ClassNamesKt.getIrElementBinaryName;
|
||||
//import static com.github.codeql.ClassNamesKt.getIrClassVirtualFile;
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement;
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass;
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol;
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration;
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName;
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
@@ -51,6 +49,8 @@ import com.semmle.util.trap.pathtransformers.PathTransformer;
|
||||
|
||||
import com.github.codeql.Compression;
|
||||
|
||||
import static com.github.codeql.ClassNamesKt.getSymbolBinaryName;
|
||||
|
||||
public class OdasaOutput {
|
||||
private final File trapFolder;
|
||||
private final File sourceArchiveFolder;
|
||||
@@ -136,7 +136,7 @@ public class OdasaOutput {
|
||||
currentSpecFileEntry.getTrapFolder(), PathTransformer.std().fileAsDatabaseString(file) + ".set");
|
||||
}
|
||||
|
||||
public void addDependency(IrDeclaration sym, String signature) {
|
||||
public void addDependency(KaSymbol sym, String signature) {
|
||||
String path = trapFilePathForDecl(sym, signature);
|
||||
trapDependenciesForSource.addDependency(path);
|
||||
}
|
||||
@@ -203,29 +203,28 @@ public class OdasaOutput {
|
||||
PathTransformer.std().fileAsDatabaseString(file) + ".trap" + compression.getExtension());
|
||||
}
|
||||
|
||||
private File getTrapFileForDecl(IrElement sym, String signature) {
|
||||
private File getTrapFileForDecl(KaSymbol sym, String signature) {
|
||||
if (currentSpecFileEntry == null)
|
||||
return null;
|
||||
return trapFileForDecl(sym, signature);
|
||||
}
|
||||
|
||||
private File trapFileForDecl(IrElement sym, String signature) {
|
||||
private File trapFileForDecl(KaSymbol sym, String signature) {
|
||||
return FileUtil.fileRelativeTo(currentSpecFileEntry.getTrapFolder(),
|
||||
trapFilePathForDecl(sym, signature));
|
||||
}
|
||||
|
||||
private String trapFilePathForDecl(IrElement sym, String signature) {
|
||||
// String binaryName = getIrElementBinaryName(sym);
|
||||
private String trapFilePathForDecl(KaSymbol sym, String signature) {
|
||||
String binaryName = getSymbolBinaryName(sym);
|
||||
// TODO: Reinstate this?
|
||||
// if (getTrackClassOrigins())
|
||||
// classId += "-" + StringDigestor.digest(sym.getSourceFileId());
|
||||
// String result = CLASSES_DIR + "/" +
|
||||
// binaryName.replace('.', '/') +
|
||||
// signature +
|
||||
// ".members" +
|
||||
// ".trap" + compression.getExtension();
|
||||
// return result;
|
||||
return null;
|
||||
String result = CLASSES_DIR + "/" +
|
||||
binaryName.replace('.', '/') +
|
||||
signature +
|
||||
".members" +
|
||||
".trap" + compression.getExtension();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -248,7 +247,7 @@ public class OdasaOutput {
|
||||
* signature.
|
||||
*/
|
||||
private TrapFileManager getMembersWriterForDecl(File trap, File trapFileBase, TrapClassVersion trapFileVersion,
|
||||
IrElement sym, String signature) {
|
||||
KaSymbol sym, String signature) {
|
||||
// If the TRAP file already exists then we
|
||||
// don't need to write it.
|
||||
if (trap.exists()) {
|
||||
@@ -288,7 +287,7 @@ public class OdasaOutput {
|
||||
return trapWriter(trap, sym, signature);
|
||||
}
|
||||
|
||||
private TrapFileManager trapWriter(File trapFile, IrElement sym, String signature) {
|
||||
private TrapFileManager trapWriter(File trapFile, KaSymbol sym, String signature) {
|
||||
if (!trapFile.getName().endsWith(".trap" + compression.getExtension()))
|
||||
throw new CatastrophicError("OdasaOutput only supports writing to compressed trap files");
|
||||
String relative = FileUtil.relativePath(trapFile, currentSpecFileEntry.getTrapFolder());
|
||||
@@ -297,7 +296,7 @@ public class OdasaOutput {
|
||||
return concurrentWriter(trapFile, relative, log, sym, signature);
|
||||
}
|
||||
|
||||
private TrapFileManager concurrentWriter(File trapFile, String relative, Logger log, IrElement sym,
|
||||
private TrapFileManager concurrentWriter(File trapFile, String relative, Logger log, KaSymbol sym,
|
||||
String signature) {
|
||||
if (trapFile.exists())
|
||||
return null;
|
||||
@@ -308,11 +307,11 @@ public class OdasaOutput {
|
||||
|
||||
private TrapDependencies trapDependenciesForClass;
|
||||
private File trapFile;
|
||||
private IrElement sym;
|
||||
private KaSymbol sym;
|
||||
private String signature;
|
||||
private boolean hasError = false;
|
||||
|
||||
private TrapFileManager(File trapFile, String relative, boolean concurrentCreation, Logger log, IrElement sym,
|
||||
private TrapFileManager(File trapFile, String relative, boolean concurrentCreation, Logger log, KaSymbol sym,
|
||||
String signature) {
|
||||
trapDependenciesForClass = new TrapDependencies(relative);
|
||||
this.trapFile = trapFile;
|
||||
@@ -324,11 +323,11 @@ public class OdasaOutput {
|
||||
return trapFile;
|
||||
}
|
||||
|
||||
public void addDependency(IrElement dep, String signature) {
|
||||
public void addDependency(KaSymbol dep, String signature) {
|
||||
trapDependenciesForClass.addDependency(trapFilePathForDecl(dep, signature));
|
||||
}
|
||||
|
||||
public void addDependency(IrClass c) {
|
||||
public void addDependency(KaClassSymbol c) {
|
||||
addDependency(c, "");
|
||||
}
|
||||
|
||||
@@ -374,7 +373,7 @@ public class OdasaOutput {
|
||||
* {@link OdasaOutput#setCurrentSourceFile(File)}.
|
||||
*/
|
||||
public TrapLocker getTrapLockerForCurrentSourceFile() {
|
||||
return new TrapLocker((IrClass) null, null, true);
|
||||
return new TrapLocker((KaClassSymbol) null, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,12 +426,12 @@ public class OdasaOutput {
|
||||
* @return a {@link TrapLocker} for the trap file corresponding to the given
|
||||
* class symbol.
|
||||
*/
|
||||
public TrapLocker getTrapLockerForDecl(IrElement sym, String signature, boolean fromSource) {
|
||||
public TrapLocker getTrapLockerForDecl(KaSymbol sym, String signature, boolean fromSource) {
|
||||
return new TrapLocker(sym, signature, fromSource);
|
||||
}
|
||||
|
||||
public class TrapLocker implements AutoCloseable {
|
||||
private final IrElement sym;
|
||||
private final KaSymbol sym;
|
||||
private final File trapFile;
|
||||
// trapFileBase is used when doing lockless TRAP file writing.
|
||||
// It is trapFile without the #metadata.trap.gz suffix.
|
||||
@@ -440,7 +439,7 @@ public class OdasaOutput {
|
||||
private TrapClassVersion trapFileVersion = null;
|
||||
private final String signature;
|
||||
|
||||
private TrapLocker(IrElement decl, String signature, boolean fromSource) {
|
||||
private TrapLocker(KaSymbol decl, String signature, boolean fromSource) {
|
||||
this.sym = decl;
|
||||
this.signature = signature;
|
||||
if (sym == null) {
|
||||
@@ -675,7 +674,7 @@ public class OdasaOutput {
|
||||
return vf.getTimeStamp();
|
||||
}
|
||||
|
||||
private static VirtualFile getVirtualFileIfClass(IrElement e) {
|
||||
private static VirtualFile getVirtualFileIfClass(KaSymbol e) {
|
||||
// TODO:
|
||||
return null;
|
||||
// if (e instanceof IrClass)
|
||||
@@ -684,10 +683,12 @@ public class OdasaOutput {
|
||||
// return null;
|
||||
}
|
||||
|
||||
private static TrapClassVersion fromSymbol(IrElement sym, Logger log) {
|
||||
private static TrapClassVersion fromSymbol(KaSymbol sym, Logger log) {
|
||||
VirtualFile vf = getVirtualFileIfClass(sym);
|
||||
/* OLD: KE1
|
||||
if (vf == null && sym instanceof IrDeclaration)
|
||||
vf = getVirtualFileIfClass(((IrDeclaration) sym).getParent());
|
||||
*/
|
||||
if (vf == null)
|
||||
return new TrapClassVersion(-1, 0, 0, null);
|
||||
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
package com.github.codeql
|
||||
|
||||
/*
|
||||
OLD: KE1
|
||||
import com.github.codeql.utils.isExternalFileClassMember
|
||||
import com.semmle.extractor.java.OdasaOutput
|
||||
import com.semmle.util.data.StringDigestor
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KaNamedSymbol
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.lang.Error
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.isFileClass
|
||||
import org.jetbrains.kotlin.ir.util.packageFqName
|
||||
|
||||
context (KaSession)
|
||||
class ExternalDeclExtractor(
|
||||
val logger: FileLogger,
|
||||
val compression: Compression,
|
||||
val invocationTrapFile: String,
|
||||
val sourceFilePath: String,
|
||||
/* OLD: KE1
|
||||
val primitiveTypeMapping: PrimitiveTypeMapping,
|
||||
val pluginContext: IrPluginContext,
|
||||
val globalExtensionState: KotlinExtractorGlobalState,
|
||||
*/
|
||||
val diagnosticTrapWriter: DiagnosticTrapWriter
|
||||
) {
|
||||
|
||||
val declBinaryNames = HashMap<IrDeclaration, String>()
|
||||
val declBinaryNames = HashMap<KaSymbol, String>()
|
||||
val externalDeclsDone = HashSet<Pair<String, String>>()
|
||||
val externalDeclWorkList = ArrayList<Pair<IrDeclaration, String>>()
|
||||
val externalDeclWorkList = ArrayList<Pair<KaSymbol, String>>()
|
||||
|
||||
val propertySignature = ";property"
|
||||
val fieldSignature = ";field"
|
||||
@@ -38,7 +37,8 @@ class ExternalDeclExtractor(
|
||||
it.setCurrentSourceFile(File(sourceFilePath))
|
||||
}
|
||||
|
||||
fun extractLater(d: IrDeclarationWithName, signature: String): Boolean {
|
||||
fun extractLater(d: KaSymbol, signature: String): Boolean {
|
||||
/* OLD: KE1
|
||||
if (d !is IrClass && !isExternalFileClassMember(d)) {
|
||||
logger.errorElement(
|
||||
"External declaration is neither a class, nor a top-level declaration",
|
||||
@@ -46,15 +46,16 @@ class ExternalDeclExtractor(
|
||||
)
|
||||
return false
|
||||
}
|
||||
val declBinaryName = declBinaryNames.getOrPut(d) { getIrElementBinaryName(d) }
|
||||
*/
|
||||
val declBinaryName = declBinaryNames.getOrPut(d) { getSymbolBinaryName(d) }
|
||||
val ret = externalDeclsDone.add(Pair(declBinaryName, signature))
|
||||
if (ret) externalDeclWorkList.add(Pair(d, signature))
|
||||
return ret
|
||||
}
|
||||
|
||||
fun extractLater(c: IrClass) = extractLater(c, "")
|
||||
fun extractLater(c: KaClassSymbol) = extractLater(c, "")
|
||||
|
||||
fun writeStubTrapFile(e: IrElement, signature: String = "") {
|
||||
fun writeStubTrapFile(e: KaSymbol, signature: String = "") {
|
||||
extractElement(e, signature, true) { trapFileBW, _, _ ->
|
||||
trapFileBW.write(
|
||||
"// Trap file stubbed because this declaration was extracted from source in $sourceFilePath\n"
|
||||
@@ -64,7 +65,7 @@ class ExternalDeclExtractor(
|
||||
}
|
||||
|
||||
private fun extractElement(
|
||||
element: IrElement,
|
||||
element: KaSymbol,
|
||||
possiblyLongSignature: String,
|
||||
fromSource: Boolean,
|
||||
extractorFn: (BufferedWriter, String, OdasaOutput.TrapFileManager) -> Unit
|
||||
@@ -84,8 +85,8 @@ class ExternalDeclExtractor(
|
||||
locker.trapFileManager.useAC { manager ->
|
||||
val shortName =
|
||||
when (element) {
|
||||
is IrDeclarationWithName -> element.name.asString()
|
||||
is IrFile -> element.name
|
||||
is KaNamedSymbol -> element.name.asString()
|
||||
is KaFileSymbol -> "(TODO file symbol name)"
|
||||
else -> "(unknown name)"
|
||||
}
|
||||
if (manager == null) {
|
||||
@@ -109,7 +110,7 @@ class ExternalDeclExtractor(
|
||||
logger.error("Failed to rename $trapTmpFile to $trapFile")
|
||||
}
|
||||
logger.info("Finished writing TRAP file $trapFile")
|
||||
} catch (e: Exception) {
|
||||
} catch (e: Throwable) {
|
||||
manager.setHasError()
|
||||
logger.error(
|
||||
"Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile",
|
||||
@@ -126,14 +127,16 @@ class ExternalDeclExtractor(
|
||||
val nextBatch = ArrayList(externalDeclWorkList)
|
||||
externalDeclWorkList.clear()
|
||||
nextBatch.forEach { workPair ->
|
||||
val (irDecl, possiblyLongSignature) = workPair
|
||||
extractElement(irDecl, possiblyLongSignature, false) {
|
||||
val (sym, possiblyLongSignature) = workPair
|
||||
extractElement(sym, possiblyLongSignature, false) {
|
||||
trapFileBW,
|
||||
signature,
|
||||
manager ->
|
||||
val binaryPath = getIrDeclarationBinaryPath(irDecl)
|
||||
val binaryPath = getSymbolBinaryPath(sym)
|
||||
if (binaryPath == null) {
|
||||
logger.errorElement("Unable to get binary path", irDecl)
|
||||
sym.psi?.also {
|
||||
logger.errorElement("Unable to get binary path", it)
|
||||
} ?: logger.error("Unable to get binary path")
|
||||
} else {
|
||||
// We want our comments to be the first thing in the file,
|
||||
// so start off with a PlainTrapWriter
|
||||
@@ -161,40 +164,46 @@ class ExternalDeclExtractor(
|
||||
KotlinFileExtractor(
|
||||
logger,
|
||||
ftw,
|
||||
this,
|
||||
/* OLD: KE1
|
||||
null,
|
||||
binaryPath,
|
||||
manager,
|
||||
this,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
KotlinFileExtractor.DeclarationStack(),
|
||||
globalExtensionState
|
||||
*/
|
||||
)
|
||||
|
||||
if (irDecl is IrClass) {
|
||||
if (sym is KaClassSymbol) {
|
||||
// Populate a location and compilation-unit package for the file. This
|
||||
// is similar to
|
||||
// the beginning of `KotlinFileExtractor.extractFileContents` but
|
||||
// without an `IrFile`
|
||||
// to start from.
|
||||
val pkg = irDecl.packageFqName?.asString() ?: ""
|
||||
val pkg = sym.classId?.packageFqName?.asString() ?: ""
|
||||
val pkgId = fileExtractor.extractPackage(pkg)
|
||||
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
|
||||
ftw.writeCupackage(ftw.fileId, pkgId)
|
||||
|
||||
fileExtractor.extractClassSource(
|
||||
irDecl,
|
||||
extractDeclarations = !irDecl.isFileClass,
|
||||
sym,
|
||||
extractDeclarations = /* OLD: KE1 !sym.isFileClass */true,
|
||||
/* OLD: KE1
|
||||
extractStaticInitializer = false,
|
||||
extractPrivateMembers = false,
|
||||
extractFunctionBodies = false
|
||||
*/
|
||||
)
|
||||
} else {
|
||||
fileExtractor.extractDeclaration(
|
||||
irDecl,
|
||||
sym as KaDeclarationSymbol,
|
||||
/* OLD: KE1
|
||||
extractPrivateMembers = false,
|
||||
extractFunctionBodies = false,
|
||||
extractAnnotations = true
|
||||
*/
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -204,4 +213,3 @@ class ExternalDeclExtractor(
|
||||
output.writeTrapSet()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -149,7 +149,7 @@ OLD: KE1
|
||||
OLD: KE1
|
||||
val globalExtensionState = KotlinExtractorGlobalState()
|
||||
*/
|
||||
doAnalysis(compression, trapDir, srcDir, loggerBase, dtw, compilation, invocationExtractionProblems, kotlinArgs)
|
||||
doAnalysis(compression, trapDir, srcDir, loggerBase, dtw, compilation, invocationExtractionProblems, kotlinArgs, invocationTrapFile)
|
||||
loggerBase.printLimitedDiagnosticCounts(dtw)
|
||||
/*
|
||||
OLD: KE1
|
||||
@@ -177,7 +177,8 @@ private fun doAnalysis(
|
||||
dtw: DiagnosticTrapWriter,
|
||||
compilation: Label<DbCompilation>,
|
||||
invocationExtractionProblems: ExtractionProblems,
|
||||
args: List<String>
|
||||
args: List<String>,
|
||||
invocationTrapFile: String
|
||||
) {
|
||||
lateinit var sourceModule: KaSourceModule
|
||||
val k2args: K2JVMCompilerArguments = parseCommandLineArguments(args.toList())
|
||||
@@ -266,8 +267,8 @@ private fun doAnalysis(
|
||||
/*
|
||||
OLD: KE1
|
||||
fileExtractionProblems,
|
||||
invocationTrapFile,
|
||||
*/
|
||||
invocationTrapFile,
|
||||
fileDiagnosticTrapWriter,
|
||||
loggerBase,
|
||||
checkTrapIdentical,
|
||||
@@ -290,11 +291,8 @@ private fun doAnalysis(
|
||||
// continue trying to extract everything else even if we get a
|
||||
// stack overflow or an assertion failure in one file.
|
||||
} catch (e: Throwable) {
|
||||
/*
|
||||
OLD: KE1
|
||||
logger.error("Extraction failed while extracting '${psiFile.virtualFilePath}'.", e)
|
||||
fileExtractionProblems.setNonRecoverableProblem()
|
||||
*/
|
||||
loggerBase.error(dtw, "Extraction failed while extracting '${psiFile.virtualFilePath}'.", e)
|
||||
}
|
||||
} else {
|
||||
System.out.println("Warning: Not a KtFile")
|
||||
@@ -455,8 +453,8 @@ private fun doFile(
|
||||
/*
|
||||
OLD: KE1
|
||||
fileExtractionProblems: FileExtractionProblems,
|
||||
invocationTrapFile: String,
|
||||
*/
|
||||
*/
|
||||
invocationTrapFile: String,
|
||||
fileDiagnosticTrapWriter: FileTrapWriter,
|
||||
loggerBase: LoggerBase,
|
||||
checkTrapIdentical: Boolean,
|
||||
@@ -510,31 +508,35 @@ private fun doFile(
|
||||
// Now elevate to a SourceFileTrapWriter, and populate the
|
||||
// file information
|
||||
val sftw = tw.makeSourceFileTrapWriter(srcFile, true)
|
||||
/*
|
||||
OLD: KE1
|
||||
val externalDeclExtractor =
|
||||
ExternalDeclExtractor(
|
||||
logger,
|
||||
compression,
|
||||
invocationTrapFile,
|
||||
srcFilePath,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
globalExtensionState,
|
||||
fileDiagnosticTrapWriter.getDiagnosticTrapWriter()
|
||||
)
|
||||
val linesOfCode = LinesOfCode(logger, sftw, srcFile)
|
||||
*/
|
||||
val externalDeclExtractor =
|
||||
ExternalDeclExtractor(
|
||||
logger,
|
||||
compression,
|
||||
invocationTrapFile,
|
||||
srcFilePath,
|
||||
/*
|
||||
OLD: KE1
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
globalExtensionState,
|
||||
*/
|
||||
fileDiagnosticTrapWriter.getDiagnosticTrapWriter()
|
||||
)
|
||||
/* OLD: KE1
|
||||
val linesOfCode = LinesOfCode(logger, sftw, srcFile)
|
||||
*/
|
||||
|
||||
val fileExtractor =
|
||||
KotlinFileExtractor(
|
||||
logger,
|
||||
sftw,
|
||||
externalDeclExtractor,
|
||||
/*
|
||||
OLD: KE1
|
||||
linesOfCode,
|
||||
srcFilePath,
|
||||
null,
|
||||
externalDeclExtractor,
|
||||
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
KotlinFileExtractor.DeclarationStack(),
|
||||
@@ -543,9 +545,6 @@ private fun doFile(
|
||||
)
|
||||
|
||||
fileExtractor.extractFileContents(srcFile, sftw.fileId)
|
||||
/*
|
||||
OLD: KE1
|
||||
externalDeclExtractor.extractExternalClasses()
|
||||
*/
|
||||
externalDeclExtractor.extractExternalClasses()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,12 @@ context (KaSession)
|
||||
open class KotlinFileExtractor(
|
||||
override val logger: FileLogger,
|
||||
override val tw: FileTrapWriter,
|
||||
externalClassExtractor: ExternalDeclExtractor,
|
||||
/*
|
||||
OLD: KE1
|
||||
val linesOfCode: LinesOfCode?,
|
||||
val filePath: String,
|
||||
dependencyCollector: OdasaOutput.TrapFileManager?,
|
||||
externalClassExtractor: ExternalDeclExtractor,
|
||||
primitiveTypeMapping: PrimitiveTypeMapping,
|
||||
pluginContext: IrPluginContext,
|
||||
val declarationStack: DeclarationStack,
|
||||
@@ -101,10 +101,10 @@ open class KotlinFileExtractor(
|
||||
KotlinUsesExtractor(
|
||||
logger,
|
||||
tw,
|
||||
externalClassExtractor,
|
||||
/*
|
||||
OLD: KE1
|
||||
dependencyCollector,
|
||||
externalClassExtractor,
|
||||
primitiveTypeMapping,
|
||||
pluginContext,
|
||||
globalExtensionState
|
||||
@@ -117,14 +117,11 @@ open class KotlinFileExtractor(
|
||||
val metaAnnotationSupport = MetaAnnotationSupport(logger, pluginContext, this)
|
||||
*/
|
||||
|
||||
inline fun <T> with(kind: String, element: KtElement, f: () -> T): T {
|
||||
val name =
|
||||
when (element) {
|
||||
is KtFile -> element.virtualFilePath
|
||||
is KtNamed -> element.getNameAsName()?.asString() ?: "<missing name>"
|
||||
else -> "<no name>"
|
||||
}
|
||||
val loc = tw.getLocationString(element)
|
||||
inline fun <T> with(kind: String, element: PsiElement, f: () -> T) = with(kind, PsiElementOrSymbol.of(element), f)
|
||||
inline fun <T> with(kind: String, element: KaSymbol, f: () -> T) = with(kind, PsiElementOrSymbol.of(element), f)
|
||||
inline fun <T> with(kind: String, element: PsiElementOrSymbol, f: () -> T): T {
|
||||
val name = element.getName()
|
||||
val loc = element.getLocationString(tw)
|
||||
val context = logger.loggerState.extractorContextStack
|
||||
context.push(ExtractorContext(kind, element, name, loc))
|
||||
try {
|
||||
@@ -299,7 +296,7 @@ open class KotlinFileExtractor(
|
||||
extractAnnotations: Boolean
|
||||
*/
|
||||
) {
|
||||
with("declaration", declaration.psiSafe() ?: TODO()) {
|
||||
with("declaration", declaration) {
|
||||
/*
|
||||
OLD: KE1
|
||||
if (!shouldExtractDecl(declaration, extractPrivateMembers)) return
|
||||
|
||||
@@ -42,10 +42,11 @@ context (KaSession)
|
||||
open class KotlinUsesExtractor(
|
||||
open val logger: Logger,
|
||||
open val tw: TrapWriter,
|
||||
val externalClassExtractor: ExternalDeclExtractor,
|
||||
/*
|
||||
OLD: KE1
|
||||
val dependencyCollector: OdasaOutput.TrapFileManager?,
|
||||
val externalClassExtractor: ExternalDeclExtractor,
|
||||
|
||||
val primitiveTypeMapping: PrimitiveTypeMapping,
|
||||
val pluginContext: IrPluginContext,
|
||||
val globalExtensionState: KotlinExtractorGlobalState
|
||||
@@ -317,12 +318,6 @@ open class KotlinUsesExtractor(
|
||||
return UseClassInstanceResult(classTypeResult, extractClass)
|
||||
}
|
||||
|
||||
private fun extractClassLaterIfExternal(c: IrClass) {
|
||||
if (isExternalDeclaration(c)) {
|
||||
extractExternalClassLater(c)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractExternalEnclosingClassLater(d: IrDeclaration) {
|
||||
when (val parent = d.parent) {
|
||||
is IrClass -> extractExternalClassLater(parent)
|
||||
@@ -516,15 +511,6 @@ open class KotlinUsesExtractor(
|
||||
} ?: f
|
||||
}
|
||||
|
||||
private fun tryReplaceType(
|
||||
cBeforeReplacement: IrClass,
|
||||
argsIncludingOuterClassesBeforeReplacement: List<IrTypeArgument>?
|
||||
): Pair<IrClass, List<IrTypeArgument>?> {
|
||||
val c = tryReplaceAndroidSyntheticClass(cBeforeReplacement)
|
||||
val p = tryReplaceParcelizeRawType(c)
|
||||
return Pair(p?.first ?: c, p?.second ?: argsIncludingOuterClassesBeforeReplacement)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -1313,58 +1299,6 @@ open class KotlinUsesExtractor(
|
||||
|
||||
data class ClassLabelResults(val classLabel: String /* TODO , val shortName: String */)
|
||||
|
||||
|
||||
/*
|
||||
OLD: KE1
|
||||
fun getTypeParameterParentLabel(param: IrTypeParameter) =
|
||||
param.parent.let {
|
||||
when (it) {
|
||||
is IrClass -> useClassSource(it)
|
||||
is IrFunction ->
|
||||
(if (this is KotlinFileExtractor)
|
||||
this.declarationStack
|
||||
.findOverriddenAttributes(it)
|
||||
?.takeUnless {
|
||||
// When extracting the `static fun f$default(...)` that accompanies
|
||||
// `fun <T> f(val x: T? = defaultExpr, ...)`,
|
||||
// `f$default` has no type parameters, and so there is no
|
||||
// `f$default::T` to refer to.
|
||||
// We have no good way to extract references to `T` in
|
||||
// `defaultExpr`, so we just fall back on describing it
|
||||
// in terms of `f::T`, even though that type variable ought to be
|
||||
// out of scope here.
|
||||
attribs ->
|
||||
attribs.typeParameters?.isEmpty() == true
|
||||
}
|
||||
?.id
|
||||
else null) ?: useFunction(it, noReplace = true)
|
||||
else -> {
|
||||
logger.error("Unexpected type parameter parent $it")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getTypeParameterLabel(param: IrTypeParameter): String {
|
||||
// Use this instead of `useDeclarationParent` so we can use useFunction with noReplace =
|
||||
// true,
|
||||
// ensuring that e.g. a method-scoped type variable declared on kotlin.String.transform <R>
|
||||
// gets
|
||||
// a different name to the corresponding java.lang.String.transform <R>, even though
|
||||
// useFunction
|
||||
// will usually replace references to one function with the other.
|
||||
val parentLabel = getTypeParameterParentLabel(param)
|
||||
return "@\"typevar;{$parentLabel};${param.name}\""
|
||||
}
|
||||
|
||||
private fun useTypeParameter(param: IrTypeParameter) =
|
||||
TypeResult(
|
||||
tw.getLabelFor<DbTypevariable>(getTypeParameterLabel(param)),
|
||||
useType(eraseTypeParameter(param)).javaResult.signature,
|
||||
param.name.asString()
|
||||
)
|
||||
|
||||
*/
|
||||
private fun extractModifier(m: String): Label<DbModifier> {
|
||||
val modifierLabel = "@\"modifier;$m\""
|
||||
val id: Label<DbModifier> = tw.getLabelFor(modifierLabel, { tw.writeModifiers(it, m) })
|
||||
|
||||
@@ -304,27 +304,24 @@ abstract class TrapWriter(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
OLD: KE1
|
||||
/**
|
||||
* Gets a FileTrapWriter like this one (using the same label manager, writer etc), but using the
|
||||
* given `filePath` for locations.
|
||||
*/
|
||||
fun makeFileTrapWriter(filePath: String, populateFileTables: Boolean) =
|
||||
FileTrapWriter(
|
||||
basicLogger,
|
||||
lm,
|
||||
bw,
|
||||
this.getDiagnosticTrapWriter(),
|
||||
filePath,
|
||||
populateFileTables
|
||||
)
|
||||
/**
|
||||
* Gets a FileTrapWriter like this one (using the same label manager, writer etc), but using the
|
||||
* given `filePath` for locations.
|
||||
*/
|
||||
fun makeFileTrapWriter(filePath: String, populateFileTables: Boolean) =
|
||||
FileTrapWriter(
|
||||
basicLogger,
|
||||
lm,
|
||||
bw,
|
||||
this.getDiagnosticTrapWriter(),
|
||||
filePath,
|
||||
populateFileTables
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets a FileTrapWriter like this one (using the same label manager, writer etc), but using the
|
||||
* given `IrFile` for locations.
|
||||
*/
|
||||
*/
|
||||
/**
|
||||
* Gets a FileTrapWriter like this one (using the same label manager, writer etc), but using the
|
||||
* given `IrFile` for locations.
|
||||
*/
|
||||
fun makeSourceFileTrapWriter(file: KtFile, populateFileTables: Boolean) =
|
||||
SourceFileTrapWriter(
|
||||
basicLogger,
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaClassType
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
context(KaSession)
|
||||
@OptIn(KaExperimentalApi::class)
|
||||
@@ -19,7 +20,7 @@ fun KotlinFileExtractor.extractClassSource(
|
||||
extractFunctionBodies: Boolean
|
||||
*/
|
||||
): Label<out DbClassorinterface> {
|
||||
with("class source", c.psiSafe() ?: TODO()) {
|
||||
with("class source", c) {
|
||||
// OLD: KE1: DeclarationStackAdjuster(c).use {
|
||||
val id = useClassSource(c)
|
||||
val pkg = c.classId?.packageFqName?.asString() ?: ""
|
||||
@@ -40,8 +41,8 @@ fun KotlinFileExtractor.extractClassSource(
|
||||
kind != KaClassKind.CLASS &&
|
||||
kind != KaClassKind.OBJECT //&&
|
||||
//OLD KE1: kind != ClassKind.ENUM_ENTRY
|
||||
) {
|
||||
logger.warnElement("Unrecognised class kind $kind", c.psiSafe() ?: TODO())
|
||||
) else {
|
||||
logger.warnElement("Unrecognised class kind $kind", c)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -56,7 +57,7 @@ fun KotlinFileExtractor.extractClassSource(
|
||||
}
|
||||
}
|
||||
|
||||
val locId = tw.getLocation(c.psiSafe() ?: TODO())
|
||||
val locId = PsiElementOrSymbol.of(c).getLocation(tw)
|
||||
tw.writeHasLocation(id, locId)
|
||||
|
||||
// OLD: KE1
|
||||
@@ -73,7 +74,7 @@ fun KotlinFileExtractor.extractClassSource(
|
||||
if (getter == null) {
|
||||
logger.warnElement(
|
||||
"Expected an annotation property to have a getter",
|
||||
it.psiSafe() ?: TODO()
|
||||
it
|
||||
)
|
||||
} else {
|
||||
extractFunction(
|
||||
@@ -187,12 +188,10 @@ fun KotlinFileExtractor.extractClassSource(
|
||||
// `args` can be null to describe a raw generic type.
|
||||
// For non-generic types it will be zero-length list.
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun KotlinUsesExtractor.getClassLabel(
|
||||
c: KaClassSymbol,
|
||||
/*
|
||||
OLD: KE1
|
||||
argsIncludingOuterClasses: List<IrTypeArgument>?
|
||||
*/
|
||||
argsIncludingOuterClasses: List<Nothing>?
|
||||
): ClassLabelResults {
|
||||
val unquotedLabel = getUnquotedClassLabel(c /* TODO , argsIncludingOuterClasses */)
|
||||
return ClassLabelResults("@\"class;${unquotedLabel.classLabel}\"" /* TODO , unquotedLabel.shortName */)
|
||||
@@ -201,30 +200,67 @@ private fun KotlinUsesExtractor.getClassLabel(
|
||||
context(KaSession)
|
||||
fun KotlinUsesExtractor.useClassSource(c: KaClassSymbol): Label<out DbClassorinterface> {
|
||||
// For source classes, the label doesn't include any type arguments
|
||||
val id = addClassLabel(c)
|
||||
val id = addClassLabel(c, listOf())
|
||||
return id
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a replacement class name and type arguments for cBeforeReplacement<argsIncludingOuterClassesBeforeReplacement>
|
||||
*
|
||||
* These could include replacing Android synthetic classes or Parcelize raw types that shouldn't appear in the database
|
||||
* as they appear to the Kotlin compiler.
|
||||
*
|
||||
* TODO: verify how these sorts of classes appear via the analysis API
|
||||
*/
|
||||
private fun tryReplaceType(
|
||||
cBeforeReplacement: KaClassSymbol,
|
||||
argsIncludingOuterClassesBeforeReplacement: List<Nothing>?
|
||||
): Pair<KaClassSymbol, List<Nothing>?> {
|
||||
return Pair(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
|
||||
/*
|
||||
OLD: KE1
|
||||
val c = tryReplaceAndroidSyntheticClass(cBeforeReplacement)
|
||||
val p = tryReplaceParcelizeRawType(c)
|
||||
return Pair(p?.first ?: c, p?.second ?: argsIncludingOuterClassesBeforeReplacement)
|
||||
*/
|
||||
}
|
||||
|
||||
private fun isExternalDeclaration(d: KaSymbol): Boolean {
|
||||
return d.origin == KaSymbolOrigin.LIBRARY || d.origin == KaSymbolOrigin.JAVA_LIBRARY
|
||||
}
|
||||
|
||||
private fun KotlinUsesExtractor.extractExternalClassLater(c: KaClassSymbol) {
|
||||
/* OLD: KE1
|
||||
dependencyCollector?.addDependency(c)
|
||||
*/
|
||||
externalClassExtractor.extractLater(c)
|
||||
}
|
||||
|
||||
|
||||
private fun KotlinUsesExtractor.extractClassLaterIfExternal(c: KaClassSymbol) {
|
||||
if (isExternalDeclaration(c)) {
|
||||
extractExternalClassLater(c)
|
||||
}
|
||||
}
|
||||
|
||||
// `typeArgs` can be null to describe a raw generic type.
|
||||
// For non-generic types it will be zero-length list.
|
||||
// TODO: Should this be private?
|
||||
context(KaSession)
|
||||
fun KotlinUsesExtractor.addClassLabel(
|
||||
c: KaClassSymbol, // TODO cBeforeReplacement: IrClass,
|
||||
cBeforeReplacement: KaClassSymbol, // TODO cBeforeReplacement: IrClass,
|
||||
argsIncludingOuterClassesBeforeReplacement: List<Nothing>?,
|
||||
/*
|
||||
OLD: KE1
|
||||
argsIncludingOuterClassesBeforeReplacement: List<IrTypeArgument>?,
|
||||
inReceiverContext: Boolean = false
|
||||
*/
|
||||
): Label<out DbClassorinterface> {
|
||||
/*
|
||||
OLD: KE1
|
||||
val replaced =
|
||||
tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
|
||||
val replacedClass = replaced.first
|
||||
val replacedArgsIncludingOuterClasses = replaced.second
|
||||
val replaced =
|
||||
tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
|
||||
val replacedClass = replaced.first
|
||||
val replacedArgsIncludingOuterClasses = replaced.second
|
||||
|
||||
*/
|
||||
val classLabelResult = getClassLabel(c /* TODO replacedClass, replacedArgsIncludingOuterClasses */)
|
||||
val classLabelResult = getClassLabel(replacedClass, replacedArgsIncludingOuterClasses)
|
||||
|
||||
/*
|
||||
OLD: KE1
|
||||
@@ -237,16 +273,8 @@ fun KotlinUsesExtractor.addClassLabel(
|
||||
OLD: KE1
|
||||
instanceSeenBefore = false
|
||||
|
||||
extractClassLaterIfExternal(replacedClass)
|
||||
*/
|
||||
// TODO: This shouldn't be done here, but keeping it simple for now
|
||||
val classId = c.classId
|
||||
if (classId == null) {
|
||||
TODO() // this is a local class
|
||||
} else {
|
||||
val pkgId = extractPackage(classId.packageFqName.asString())
|
||||
tw.writeClasses_or_interfaces(it, classId.relativeClassName.asString(), pkgId, it)
|
||||
}
|
||||
extractClassLaterIfExternal(replacedClass)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -297,6 +325,7 @@ OLD: KE1
|
||||
* it will be zero-length list.
|
||||
*/
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun KotlinUsesExtractor.getUnquotedClassLabel(
|
||||
c: KaClassSymbol,
|
||||
/*
|
||||
@@ -309,25 +338,32 @@ private fun KotlinUsesExtractor.getUnquotedClassLabel(
|
||||
TODO() // This is a local class
|
||||
}
|
||||
val pkg = classId.packageFqName.asString()
|
||||
val cls = classId.relativeClassName.asString()
|
||||
val cls = classId.shortClassName.asString()
|
||||
val label =
|
||||
/*
|
||||
OLD: KE1
|
||||
if (c.isAnonymousObject) "{${useAnonymousClass(c).javaResult.id}}"
|
||||
else
|
||||
when (val parent = c.parent) {
|
||||
is IrClass -> {
|
||||
"${getUnquotedClassLabel(parent, listOf()).classLabel}\$$cls"
|
||||
}
|
||||
is IrFunction -> {
|
||||
"{${useFunction<DbMethod>(parent)}}.$cls"
|
||||
}
|
||||
is IrField -> {
|
||||
"{${useField(parent)}}.$cls"
|
||||
}
|
||||
else -> {
|
||||
*/
|
||||
if (pkg.isEmpty()) cls else "$pkg.$cls"
|
||||
/* OLD: KE1
|
||||
if (c.isAnonymousObject) "{${useAnonymousClass(c).javaResult.id}}"
|
||||
else
|
||||
*/
|
||||
when (val parent = c.containingSymbol) {
|
||||
is KaClassSymbol -> {
|
||||
"${getUnquotedClassLabel(parent).classLabel}\$$cls"
|
||||
}
|
||||
|
||||
is KaFunctionSymbol -> {
|
||||
"{${useFunction<DbMethod>(parent)}}.$cls"
|
||||
}
|
||||
|
||||
is KaPropertySymbol -> {
|
||||
TODO()
|
||||
/* OLD: KE1
|
||||
"{${useField(parent)}}.$cls"
|
||||
*/
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (pkg.isEmpty()) cls else "$pkg.$cls"
|
||||
}
|
||||
}
|
||||
/*
|
||||
OLD: KE1
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ OLD: KE1
|
||||
}
|
||||
*/
|
||||
|
||||
context(KaSession)
|
||||
fun KotlinFileExtractor.extractConstantInteger(
|
||||
text: String,
|
||||
t: KaType,
|
||||
@@ -1576,6 +1577,7 @@ private fun KotlinFileExtractor.extractVariableAccess(
|
||||
}
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
private fun KotlinFileExtractor.extractVariableAccess(
|
||||
variable: Label<out DbVariable>?,
|
||||
type: KaType,
|
||||
|
||||
@@ -409,7 +409,7 @@ private fun KotlinFileExtractor.forceExtractFunction(
|
||||
overriddenAttributes: OverriddenFunctionAttributes? = null
|
||||
*/
|
||||
): Label<out DbCallable> {
|
||||
with("function", f.psiSafe() ?: TODO()) {
|
||||
with("function", f) {
|
||||
/*
|
||||
OLD: KE1
|
||||
DeclarationStackAdjuster(f, overriddenAttributes).use {
|
||||
@@ -508,8 +508,9 @@ OLD: KE1
|
||||
} ?: adjustedReturnType
|
||||
*/
|
||||
val functionSyntax = f.psi as? KtDeclarationWithBody
|
||||
val locId =
|
||||
val locId = functionSyntax?.let {
|
||||
tw.getLocation(functionSyntax ?: TODO())
|
||||
} ?: tw.getWholeFileLocation()
|
||||
/*
|
||||
OLD: KE1
|
||||
overriddenAttributes?.sourceLoc
|
||||
@@ -640,6 +641,7 @@ OLD: KE1
|
||||
}
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
fun KotlinFileExtractor.extractValueParameter(
|
||||
id: Label<out DbParam>,
|
||||
t: KaType,
|
||||
@@ -672,6 +674,7 @@ fun KotlinFileExtractor.extractValueParameter(
|
||||
}
|
||||
|
||||
// TODO: Can this be inlined?
|
||||
context(KaSession)
|
||||
private fun KotlinFileExtractor.extractMethod(
|
||||
id: Label<out DbMethod>,
|
||||
/*
|
||||
@@ -724,6 +727,16 @@ private fun KotlinFileExtractor.extractMethod(
|
||||
*/
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
fun <T : DbCallable> KotlinUsesExtractor.useFunction(
|
||||
f: KaFunctionSymbol,
|
||||
/*
|
||||
OLD: KE1
|
||||
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
|
||||
noReplace: Boolean = false
|
||||
*/
|
||||
): Label<out T> = useFunction(f, useDeclarationParentOf(f, true)!! /* TODO */)
|
||||
|
||||
context(KaSession)
|
||||
fun <T : DbCallable> KotlinUsesExtractor.useFunction(
|
||||
f: KaFunctionSymbol,
|
||||
|
||||
@@ -216,6 +216,7 @@ private val nullableAnyArrayType: KaType
|
||||
* Adds a function named `invoke` with the specified parameter types and return type to the
|
||||
* class identified by `parentId`.
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun KotlinFileExtractor.addFunctionInvoke(
|
||||
methodId: Label<DbMethod>,
|
||||
parameterTypes: List<KaType>,
|
||||
@@ -237,6 +238,7 @@ private fun KotlinFileExtractor.addFunctionInvoke(
|
||||
* Extracts a function with the given name, parameter types, return type, containing type, and
|
||||
* location.
|
||||
*/
|
||||
context(KaSession)
|
||||
private fun KotlinFileExtractor.addFunctionManual(
|
||||
methodId: Label<DbMethod>,
|
||||
name: String,
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaClassType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaFlexibleType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaType
|
||||
import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType
|
||||
|
||||
context(KaSession)
|
||||
private fun KotlinUsesExtractor.useClassType(
|
||||
c: KaClassType
|
||||
): TypeResults {
|
||||
// TODO: this cast is unsafe; .symbol is actually a KaClassLikeSymbol
|
||||
val classId = addClassLabel(c.symbol as KaClassSymbol)
|
||||
val classId = addClassLabel(c.symbol as KaClassSymbol, listOf<Nothing>())
|
||||
val javaResult = TypeResult(classId /* , TODO, TODO */)
|
||||
val kotlinTypeId =
|
||||
tw.getLabelFor<DbKt_class_type>("@\"kt_class;{$classId}\"") {
|
||||
@@ -19,6 +23,60 @@ private fun KotlinUsesExtractor.useClassType(
|
||||
return TypeResults(javaResult, kotlinResult)
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
fun KotlinUsesExtractor.getTypeParameterParentLabel(param: KaTypeParameterType) =
|
||||
param.symbol.containingSymbol?.let {
|
||||
when (it) {
|
||||
is KaClassSymbol -> useClassSource(it)
|
||||
is KaFunctionSymbol ->
|
||||
/* OLD: KE1
|
||||
(if (this is KotlinFileExtractor)
|
||||
this.declarationStack
|
||||
.findOverriddenAttributes(it)
|
||||
?.takeUnless {
|
||||
// When extracting the `static fun f$default(...)` that accompanies
|
||||
// `fun <T> f(val x: T? = defaultExpr, ...)`,
|
||||
// `f$default` has no type parameters, and so there is no
|
||||
// `f$default::T` to refer to.
|
||||
// We have no good way to extract references to `T` in
|
||||
// `defaultExpr`, so we just fall back on describing it
|
||||
// in terms of `f::T`, even though that type variable ought to be
|
||||
// out of scope here.
|
||||
attribs ->
|
||||
attribs.typeParameters?.isEmpty() == true
|
||||
}
|
||||
?.id
|
||||
else null) ?:
|
||||
*/
|
||||
useFunction(it, useDeclarationParentOf(it, true) ?: TODO() /* OLD: KE1 noReplace = true */)
|
||||
else -> {
|
||||
logger.error("Unexpected type parameter parent $it")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
fun KotlinUsesExtractor.getTypeParameterLabel(param: KaTypeParameterType): String {
|
||||
// Use this instead of `useDeclarationParent` so we can use useFunction with noReplace = true,
|
||||
// ensuring that e.g. a method-scoped type variable declared on kotlin.String.transform<R> gets
|
||||
// a different name to the corresponding java.lang.String.transform<R>, even though
|
||||
// useFunction will usually replace references to one function with the other.
|
||||
val parentLabel = getTypeParameterParentLabel(param)
|
||||
return "@\"typevar;{$parentLabel};${param.name}\""
|
||||
}
|
||||
|
||||
context(KaSession)
|
||||
private fun KotlinUsesExtractor.useTypeParameterType(param: KaTypeParameterType) =
|
||||
TypeResult(
|
||||
tw.getLabelFor<DbTypevariable>(getTypeParameterLabel(param)),
|
||||
/* OLD: KE1
|
||||
useType(eraseTypeParameter(param)).javaResult.signature,
|
||||
param.name.asString()
|
||||
*/
|
||||
)
|
||||
|
||||
context(KaSession)
|
||||
fun KotlinUsesExtractor.useType(t: KaType?, context: TypeContext = TypeContext.OTHER): TypeResults {
|
||||
val tr = when (t) {
|
||||
null -> {
|
||||
@@ -27,6 +85,7 @@ fun KotlinUsesExtractor.useType(t: KaType?, context: TypeContext = TypeContext.O
|
||||
}
|
||||
is KaClassType -> useClassType(t)
|
||||
is KaFlexibleType -> useType(t.lowerBound) // TODO: take a more reasoned choice here
|
||||
is KaTypeParameterType -> TypeResults(useTypeParameterType(t), extractErrorType().kotlinResult /* TODO */)
|
||||
else -> TODO()
|
||||
}
|
||||
val javaResult = tr.javaResult
|
||||
@@ -34,7 +93,7 @@ fun KotlinUsesExtractor.useType(t: KaType?, context: TypeContext = TypeContext.O
|
||||
val abbreviation = t.abbreviatedType
|
||||
val kotlinResultAlias = if (abbreviation == null) kotlinResultBase else {
|
||||
// TODO: this cast is unsafe; .symbol is actually a KaClassLikeSymbol
|
||||
val classId = addClassLabel(abbreviation.symbol as KaClassSymbol)
|
||||
val classId = addClassLabel(abbreviation.symbol as KaClassSymbol, listOf<Nothing>() /* TODO */)
|
||||
val kotlinBaseTypeId = kotlinResultBase.id
|
||||
val kotlinAliasTypeId =
|
||||
tw.getLabelFor<DbKt_type_alias>("@\"kt_type_alias;{$classId};{$kotlinBaseTypeId}\"") {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.github.codeql
|
||||
|
||||
/*
|
||||
OLD: KE1
|
||||
// Functions copied from stdlib/jdk7/src/kotlin/AutoCloseable.kt, which is not available within
|
||||
// kotlinc,
|
||||
// but allows the `.use` pattern to be applied to JDK7 AutoCloseables:
|
||||
@@ -47,4 +45,3 @@ fun AutoCloseable?.closeFinallyAC(cause: Throwable?) =
|
||||
cause.addSuppressed(closeException)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaFileSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaNamedClassSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KaNamedSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
/*
|
||||
@@ -19,15 +27,18 @@ import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
|
||||
*/
|
||||
|
||||
// Adapted from Kotlin's interpreter/Utils.kt function 'internalName'
|
||||
// Translates class names into their JLS section 13.1 binary name,
|
||||
// and declarations within them into the parent class' JLS 13.1 name as
|
||||
// specified above, followed by a `$` separator and then the short name
|
||||
// for `that`.
|
||||
private fun getName(d: IrDeclarationWithName) =
|
||||
private fun getName(d: KaNamedSymbol) =
|
||||
d.name.identifier
|
||||
/* OLD: KE1
|
||||
(d as? IrAnnotationContainer)?.let { getJvmName(it) } ?: d.name.asString()
|
||||
*/
|
||||
*/
|
||||
|
||||
fun getFileClassName(f: KtFile): String =
|
||||
null /* OLD: KE1: getJvmName(f) */
|
||||
@@ -36,27 +47,31 @@ fun getFileClassName(f: KtFile): String =
|
||||
.replaceFirst(Regex("""\.kt$"""), "")
|
||||
.replaceFirstChar { it.uppercase() }) + "Kt")
|
||||
|
||||
/*
|
||||
OLD: KE1
|
||||
fun getIrElementBinaryName(that: IrElement): String {
|
||||
if (that is IrFile) {
|
||||
private fun getBinaryName(cid: ClassId): String =
|
||||
(cid.outerClassId?.let { ocid -> "${getBinaryName(ocid)}${'$'}" } ?: "${cid.packageFqName}.") + cid.shortClassName
|
||||
|
||||
fun getSymbolBinaryName(that: KaSymbol): String {
|
||||
if (that is KaFileSymbol) {
|
||||
return "TODO"
|
||||
/* OLD: KE1
|
||||
val shortName = getFileClassName(that)
|
||||
val pkg = that.packageFqName.asString()
|
||||
return if (pkg.isEmpty()) shortName else "$pkg.$shortName"
|
||||
*/
|
||||
}
|
||||
|
||||
/* OLD: KE1
|
||||
if (that !is IrDeclaration) {
|
||||
return "(unknown-name)"
|
||||
}
|
||||
*/
|
||||
|
||||
val shortName =
|
||||
when (that) {
|
||||
is IrDeclarationWithName -> getName(that)
|
||||
else -> "(unknown-name)"
|
||||
}
|
||||
val internalName =
|
||||
(that as? KaNamedClassSymbol)?.classId?.let { getBinaryName(it) }
|
||||
?: "(unknown-binary-name)"
|
||||
/* OLD: KE1
|
||||
if (that !is KaClassSymbol) {
|
||||
|
||||
val internalName = StringBuilder(shortName)
|
||||
if (that !is IrClass) {
|
||||
val parent = that.parent
|
||||
if (parent is IrFile) {
|
||||
// Note we'll fall through and do the IrPackageFragment case as well, since IrFile <:
|
||||
@@ -64,22 +79,14 @@ fun getIrElementBinaryName(that: IrElement): String {
|
||||
internalName.insert(0, getFileClassName(parent) + "$")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
generateSequence(that.parent) { (it as? IrDeclaration)?.parent }
|
||||
.forEach {
|
||||
when (it) {
|
||||
is IrClass -> internalName.insert(0, getName(it) + "$")
|
||||
is IrPackageFragment ->
|
||||
it.packageFqName
|
||||
.asString()
|
||||
.takeIf { fqName -> fqName.isNotEmpty() }
|
||||
?.let { fqName -> internalName.insert(0, "$fqName.") }
|
||||
}
|
||||
}
|
||||
return internalName.toString()
|
||||
return internalName
|
||||
}
|
||||
|
||||
fun getIrClassVirtualFile(irClass: IrClass): VirtualFile? {
|
||||
fun getIrClassVirtualFile(c: KaClassSymbol): VirtualFile? {
|
||||
return c.psi?.containingFile?.virtualFile
|
||||
/* OLD: KE1
|
||||
val cSource = irClass.source
|
||||
// Don't emit a location for multi-file classes until we're sure we can cope with different
|
||||
// declarations
|
||||
@@ -113,10 +120,11 @@ fun getIrClassVirtualFile(irClass: IrClass): VirtualFile? {
|
||||
}
|
||||
}
|
||||
return null
|
||||
*/
|
||||
}
|
||||
|
||||
private fun getRawIrClassBinaryPath(irClass: IrClass) =
|
||||
getIrClassVirtualFile(irClass)?.let {
|
||||
private fun getRawClassSymbolBinaryPath(c: KaClassSymbol) =
|
||||
getIrClassVirtualFile(c)?.let {
|
||||
val path = it.path
|
||||
if (it.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL)
|
||||
// For JRT files, which we assume to be the JDK, hide the containing JAR path to match the
|
||||
@@ -125,16 +133,18 @@ private fun getRawIrClassBinaryPath(irClass: IrClass) =
|
||||
else path
|
||||
}
|
||||
|
||||
fun getIrClassBinaryPath(irClass: IrClass): String {
|
||||
return getRawIrClassBinaryPath(irClass)
|
||||
fun getClassSymbolBinaryPath(c: KaClassSymbol): String {
|
||||
return getRawClassSymbolBinaryPath(c)
|
||||
// Otherwise, make up a fake location:
|
||||
?: getUnknownBinaryLocation(getIrElementBinaryName(irClass))
|
||||
?: getUnknownBinaryLocation(getSymbolBinaryName(c))
|
||||
}
|
||||
|
||||
fun getIrDeclarationBinaryPath(d: IrDeclaration): String? {
|
||||
if (d is IrClass) {
|
||||
return getIrClassBinaryPath(d)
|
||||
fun getSymbolBinaryPath(d: KaSymbol): String? {
|
||||
if (d is KaClassSymbol) {
|
||||
return getClassSymbolBinaryPath(d)
|
||||
}
|
||||
/*
|
||||
OLD: KE1
|
||||
val parentClass = d.parentClassOrNull
|
||||
if (parentClass != null) {
|
||||
return getIrClassBinaryPath(parentClass)
|
||||
@@ -146,6 +156,7 @@ fun getIrDeclarationBinaryPath(d: IrDeclaration): String? {
|
||||
return getUnknownBinaryLocation(fqName.asString())
|
||||
}
|
||||
}
|
||||
*/
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -153,6 +164,7 @@ private fun getUnknownBinaryLocation(s: String): String {
|
||||
return "/!unknown-binary-location/${s.replace(".", "/")}.class"
|
||||
}
|
||||
|
||||
/* OLD: KE1
|
||||
fun getJavaEquivalentClassId(c: IrClass) =
|
||||
c.fqNameWhenAvailable?.toUnsafe()?.let { JavaToKotlinClassMap.mapKotlinToJava(it) }
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.markers.KaNamedSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.psiSafe
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamed
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
@@ -99,9 +105,38 @@ class LogMessage(private val kind: String, private val message: String) {
|
||||
}
|
||||
}
|
||||
|
||||
sealed class PsiElementOrSymbol {
|
||||
abstract fun getLocationString(ftw: FileTrapWriter): String
|
||||
abstract fun getLocation(ftw: FileTrapWriter): Label<DbLocation>
|
||||
abstract fun getName(): String
|
||||
companion object {
|
||||
fun of(e: PsiElement) = PsiElementWrapper(e)
|
||||
fun of(e: KaSymbol) = e.psiSafe<KtElement>()?.let { of(it) } ?: SymbolWrapper(e)
|
||||
}
|
||||
}
|
||||
|
||||
data class PsiElementWrapper(val e: PsiElement) : PsiElementOrSymbol() {
|
||||
override fun getLocationString(ftw: FileTrapWriter) = ftw.getLocationString(e)
|
||||
override fun getLocation(ftw: FileTrapWriter) = ftw.getLocation(e)
|
||||
override fun getName() = when (e) {
|
||||
is KtFile -> e.virtualFilePath
|
||||
is KtNamed -> e.nameAsName?.asString() ?: "<missing name>"
|
||||
else -> "<no name>"
|
||||
}
|
||||
}
|
||||
|
||||
data class SymbolWrapper(val e: KaSymbol) : PsiElementOrSymbol() {
|
||||
override fun getLocationString(ftw: FileTrapWriter) = "file://${ftw.filePath}"
|
||||
override fun getLocation(ftw: FileTrapWriter) = ftw.getWholeFileLocation()
|
||||
override fun getName() = when (e) {
|
||||
is KaNamedSymbol -> e.name.asString()
|
||||
else -> "<no name>"
|
||||
}
|
||||
}
|
||||
|
||||
data class ExtractorContext(
|
||||
val kind: String,
|
||||
val element: PsiElement,
|
||||
val element: PsiElementOrSymbol,
|
||||
val name: String,
|
||||
val loc: String
|
||||
)
|
||||
@@ -286,6 +321,10 @@ class LoggerBase(val diagnosticCounter: DiagnosticCounter) : BasicLogger {
|
||||
error(dtw, msg, extraInfo, null)
|
||||
}
|
||||
|
||||
fun error(dtw: DiagnosticTrapWriter, msg: String, exn: Throwable) {
|
||||
error(dtw, msg, exn.stackTraceToString())
|
||||
}
|
||||
|
||||
fun error(dtw: DiagnosticTrapWriter, msg: String, extraInfo: String?, loggerState: LoggerState?) {
|
||||
if (verbosity >= 1) {
|
||||
diagnostic(dtw, Severity.Error, msg, extraInfo, loggerState)
|
||||
@@ -385,7 +424,7 @@ open class Logger(val loggerBase: LoggerBase, val dtw: DiagnosticTrapWriter) : B
|
||||
}
|
||||
|
||||
fun error(msg: String, exn: Throwable) {
|
||||
error(msg, exn.stackTraceToString())
|
||||
loggerBase.error(dtw, msg, exn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,9 +443,9 @@ class FileLogger(loggerBase: LoggerBase, val ftw: FileTrapWriter, fileNumber: In
|
||||
loggerBase.warn(dtw, msg, extraInfo, loggerState)
|
||||
}
|
||||
|
||||
fun warnElement(msg: String, element: PsiElement/* TODO , exn: Throwable? = null */) {
|
||||
val locationString = ftw.getLocationString(element)
|
||||
val mkLocationId = { ftw.getLocation(element) }
|
||||
fun warnElement(msg: String, element: PsiElementOrSymbol /* TODO , exn: Throwable? = null */) {
|
||||
val locationString = element.getLocationString(ftw)
|
||||
val mkLocationId = { element.getLocation(ftw) }
|
||||
loggerBase.diagnostic(
|
||||
ftw.getDiagnosticTrapWriter(),
|
||||
Severity.Warn,
|
||||
@@ -418,13 +457,16 @@ class FileLogger(loggerBase: LoggerBase, val ftw: FileTrapWriter, fileNumber: In
|
||||
)
|
||||
}
|
||||
|
||||
fun warnElement(msg: String, element: PsiElement /* TODO , exn: Throwable? = null */) = warnElement(msg, PsiElementOrSymbol.of(element))
|
||||
fun warnElement(msg: String, element: KaSymbol /* TODO , exn: Throwable? = null */) = warnElement(msg, PsiElementOrSymbol.of(element))
|
||||
|
||||
override fun error(dtw: DiagnosticTrapWriter, msg: String, extraInfo: String?) {
|
||||
loggerBase.error(dtw, msg, extraInfo, loggerState)
|
||||
}
|
||||
|
||||
fun errorElement(msg: String, element: PsiElement /* TODO , exn: Throwable? = null */) {
|
||||
val locationString = ftw.getLocationString(element)
|
||||
val mkLocationId = { ftw.getLocation(element) }
|
||||
fun errorElement(msg: String, element: PsiElementOrSymbol /* TODO , exn: Throwable? = null */) {
|
||||
val locationString = element.getLocationString(ftw)
|
||||
val mkLocationId = { element.getLocation(ftw) }
|
||||
loggerBase.diagnostic(
|
||||
ftw.getDiagnosticTrapWriter(),
|
||||
Severity.Error,
|
||||
@@ -435,4 +477,7 @@ class FileLogger(loggerBase: LoggerBase, val ftw: FileTrapWriter, fileNumber: In
|
||||
mkLocationId
|
||||
)
|
||||
}
|
||||
|
||||
fun errorElement(msg: String, element: PsiElement /* TODO , exn: Throwable? = null */) = errorElement(msg, PsiElementOrSymbol.of(element))
|
||||
fun errorElement(msg: String, element: KaSymbol /* TODO , exn: Throwable? = null */) = errorElement(msg, PsiElementOrSymbol.of(element))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user