From c20ee76826e10318f8f39c6fae901b743c2e8254 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 3 Nov 2021 11:21:18 +0000 Subject: [PATCH] Kotlin: Give fields a Kotlin type This meant refactoring the EnumEntry extraction a bit. The IR doesn't give us a type for fields, so we have to make it up based on the parent. --- .../main/kotlin/KotlinExtractorExtension.kt | 64 +++++++++++-------- java/ql/lib/config/semmlecode.dbscheme | 1 + java/ql/lib/semmle/code/Location.qll | 2 +- java/ql/lib/semmle/code/java/Element.qll | 2 +- java/ql/lib/semmle/code/java/Member.qll | 9 ++- java/ql/lib/semmle/code/java/Type.qll | 6 +- 6 files changed, 50 insertions(+), 34 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index afab9b32b45..c51451d54b6 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -389,6 +389,31 @@ open class KotlinUsesExtractor( return id } + fun useSimpleTypeClass(c: IrClass, args: List, hasQuestionMark: Boolean): TypeResults { + val classInstanceResult = useClassInstance(c, args) + val javaClassId = classInstanceResult.classLabel + val kotlinQualClassName = getUnquotedClassLabel(c, args) + val javaQualClassName = classInstanceResult.javaClass.fqNameForIrSerialization.asString() + val javaSignature = javaQualClassName // TODO: Is this right? + val javaResult = TypeResult(javaClassId, javaSignature) + val kotlinResult = if (hasQuestionMark) { + val kotlinSignature = "$kotlinQualClassName?" // TODO: Is this right? + val kotlinLabel = "@\"kt_type;nullable;$kotlinQualClassName\"" + val kotlinId: Label = tw.getLabelFor(kotlinLabel, { + tw.writeKt_nullable_types(it, javaClassId) + }) + TypeResult(kotlinId, kotlinSignature) + } else { + val kotlinSignature = kotlinQualClassName // TODO: Is this right? + val kotlinLabel = "@\"kt_type;notnull;$kotlinQualClassName\"" + val kotlinId: Label = tw.getLabelFor(kotlinLabel, { + tw.writeKt_notnull_types(it, javaClassId) + }) + TypeResult(kotlinId, kotlinSignature) + } + return TypeResults(javaResult, kotlinResult) + } + fun useSimpleType(s: IrSimpleType, canReturnPrimitiveTypes: Boolean): TypeResults { // We use this when we don't actually have an IrClass for a class // we want to refer to @@ -514,28 +539,7 @@ class X { val classifier: IrClassifierSymbol = s.classifier val cls: IrClass = classifier.owner as IrClass - val classInstanceResult = useClassInstance(cls, s.arguments) - val javaClassId = classInstanceResult.classLabel - val kotlinQualClassName = getUnquotedClassLabel(cls, s.arguments) - val javaQualClassName = classInstanceResult.javaClass.fqNameForIrSerialization.asString() - val javaSignature = javaQualClassName // TODO: Is this right? - val javaResult = TypeResult(javaClassId, javaSignature) - val kotlinResult = if (s.hasQuestionMark) { - val kotlinSignature = "$kotlinQualClassName?" // TODO: Is this right? - val kotlinLabel = "@\"kt_type;nullable;$kotlinQualClassName\"" - val kotlinId: Label = tw.getLabelFor(kotlinLabel, { - tw.writeKt_nullable_types(it, javaClassId) - }) - TypeResult(kotlinId, kotlinSignature) - } else { - val kotlinSignature = kotlinQualClassName // TODO: Is this right? - val kotlinLabel = "@\"kt_type;notnull;$kotlinQualClassName\"" - val kotlinId: Label = tw.getLabelFor(kotlinLabel, { - tw.writeKt_notnull_types(it, javaClassId) - }) - TypeResult(kotlinId, kotlinSignature) - } - return TypeResults(javaResult, kotlinResult) + return useSimpleTypeClass(cls, s.arguments, s.hasQuestionMark) } s.classifier.owner is IrTypeParameter -> { val javaId = useTypeParameter(s.classifier.owner as IrTypeParameter) @@ -1006,8 +1010,8 @@ open class KotlinFileExtractor( } else { val id = useProperty(p) val locId = tw.getLocation(p) - val typeId = useTypeOld(bf.type) - tw.writeFields(id, p.name.asString(), typeId, parentId, id) + val type = useType(bf.type) + tw.writeFields(id, p.name.asString(), type.javaResult.id, type.kotlinResult.id, parentId, id) tw.writeHasLocation(id, locId) } } @@ -1015,8 +1019,16 @@ open class KotlinFileExtractor( fun extractEnumEntry(ee: IrEnumEntry, parentId: Label) { val id = useEnumEntry(ee) val locId = tw.getLocation(ee) - tw.writeFields(id, ee.name.asString(), parentId, parentId, id) - tw.writeHasLocation(id, locId) + val parent = ee.parent + if(parent !is IrClass) { + logger.warnElement(Severity.ErrorSevere, "Enum entry with unexpected parent: " + parent.javaClass, ee) + } else if (!parent.typeParameters.isEmpty()) { + logger.warnElement(Severity.ErrorSevere, "Enum entry parent class has type parameters: " + parent.name, ee) + } else { + val type = useSimpleTypeClass(parent, emptyList(), false) + tw.writeFields(id, ee.name.asString(), type.javaResult.id, type.kotlinResult.id, parentId, id) + tw.writeHasLocation(id, locId) + } } fun extractBody(b: IrBody, callable: Label) { diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index 17f4bc7afc0..8c5ce712137 100755 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -353,6 +353,7 @@ fields( unique int id: @field, string nodeName: string ref, int typeid: @type ref, + int kttypeid: @kt_type ref, int parentid: @reftype ref, int sourceid: @field ref ); diff --git a/java/ql/lib/semmle/code/Location.qll b/java/ql/lib/semmle/code/Location.qll index 8e51cf610af..4ecebc226fc 100755 --- a/java/ql/lib/semmle/code/Location.qll +++ b/java/ql/lib/semmle/code/Location.qll @@ -20,7 +20,7 @@ predicate hasName(Element e, string name) { or methods(e, name, _, _, _, _) or - fields(e, name, _, _, _) + fields(e, name, _, _, _, _) or packages(e, name) or diff --git a/java/ql/lib/semmle/code/java/Element.qll b/java/ql/lib/semmle/code/java/Element.qll index 6bb6a23adae..a4e68d56516 100755 --- a/java/ql/lib/semmle/code/java/Element.qll +++ b/java/ql/lib/semmle/code/java/Element.qll @@ -63,7 +63,7 @@ private predicate hasChildElement(Element parent, Element e) { or params(e, _, _, parent, _) or - fields(e, _, _, parent, _) + fields(e, _, _, _, parent, _) or typeVars(e, _, _, _, parent) } diff --git a/java/ql/lib/semmle/code/java/Member.qll b/java/ql/lib/semmle/code/java/Member.qll index a8896e934c1..ea53c76da4b 100755 --- a/java/ql/lib/semmle/code/java/Member.qll +++ b/java/ql/lib/semmle/code/java/Member.qll @@ -598,10 +598,13 @@ class FieldDeclaration extends ExprParent, @fielddecl, Annotatable { /** A class or instance field. */ class Field extends Member, ExprParent, @field, Variable { /** Gets the declared type of this field. */ - override Type getType() { fields(this, _, result, _, _) } + override Type getType() { fields(this, _, result, _, _, _) } + + /** Gets the Kotlin type of this field. */ + KotlinType getKotlinType() { fields(this, _, _, result, _, _) } /** Gets the type in which this field is declared. */ - override RefType getDeclaringType() { fields(this, _, _, result, _) } + override RefType getDeclaringType() { fields(this, _, _, _, result, _) } /** * Gets the field declaration in which this field is declared. @@ -631,7 +634,7 @@ class Field extends Member, ExprParent, @field, Variable { * * For all other fields, the source declaration is the field itself. */ - Field getSourceDeclaration() { fields(this, _, _, _, result) } + Field getSourceDeclaration() { fields(this, _, _, _, _, result) } /** Holds if this field is the same as its source declaration. */ predicate isSourceDeclaration() { this.getSourceDeclaration() = this } diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index a6977e954e1..11515f9cc7a 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -319,7 +319,7 @@ predicate declaresMember(Type t, @member m) { or constrs(m, _, _, _, t, _) or - fields(m, _, _, t, _) + fields(m, _, _, _, t, _) or enclInReftype(m, t) and // Since the type `@member` in the dbscheme includes all `@reftype`s, @@ -1109,11 +1109,11 @@ class EnumType extends Class { /** Gets the enum constant with the specified name. */ EnumConstant getEnumConstant(string name) { - fields(result, _, _, this, _) and result.hasName(name) + fields(result, _, _, _, this, _) and result.hasName(name) } /** Gets an enum constant declared in this enum type. */ - EnumConstant getAnEnumConstant() { fields(result, _, _, this, _) } + EnumConstant getAnEnumConstant() { fields(result, _, _, _, this, _) } override predicate isFinal() { // JLS 8.9: An enum declaration is implicitly `final` unless it contains