diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 9a5149a392a..1e8965f73e8 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -778,19 +778,22 @@ open class KotlinFileExtractor( with("field", f) { DeclarationStackAdjuster(f).use { declarationStack.push(f) - return extractField(useField(f), f.name.asString(), f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f)) + return extractField(useField(f), f.name.asString(), f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal) } } } - private fun extractField(id: Label, name: String, type: IrType, parentId: Label, locId: Label, visibility: DescriptorVisibility, errorElement: IrElement, isExternalDeclaration: Boolean): Label { + private fun extractField(id: Label, name: String, type: IrType, parentId: Label, locId: Label, visibility: DescriptorVisibility, errorElement: IrElement, isExternalDeclaration: Boolean, isFinal: Boolean): Label { val t = useType(type) tw.writeFields(id, name, t.javaResult.id, parentId, id) tw.writeFieldsKotlinType(id, t.kotlinResult.id) tw.writeHasLocation(id, locId) extractVisibility(errorElement, id, visibility) + if (isFinal) { + addModifiers(id, "final") + } if (!isExternalDeclaration) { val fieldDeclarationId = tw.getFreshIdLabel() @@ -2949,12 +2952,12 @@ open class KotlinFileExtractor( // only one of the following can be non-null: if (dispatchReceiver != null) { - extractField(dispatchFieldId!!, "", receiverType!!, classId, locId, DescriptorVisibilities.PRIVATE, callableReferenceExpr, false) + extractField(dispatchFieldId!!, "", receiverType!!, classId, locId, DescriptorVisibilities.PRIVATE, callableReferenceExpr, isExternalDeclaration = false, isFinal = true) extractParameterToFieldAssignmentInConstructor("", dispatchReceiver.type, dispatchFieldId, 0, firstAssignmentStmtIdx) } if (extensionReceiver != null) { - extractField(extensionFieldId!!, "", receiverType!!, classId, locId, DescriptorVisibilities.PRIVATE, callableReferenceExpr, false) + extractField(extensionFieldId!!, "", receiverType!!, classId, locId, DescriptorVisibilities.PRIVATE, callableReferenceExpr, isExternalDeclaration = false, isFinal = true) extractParameterToFieldAssignmentInConstructor( "", extensionReceiver.type, extensionFieldId, 0 + extensionParameterIndex, firstAssignmentStmtIdx + extensionParameterIndex) } } @@ -4005,7 +4008,7 @@ open class KotlinFileExtractor( // add field val fieldId = tw.getFreshIdLabel() - extractField(fieldId, "", functionType, classId, locId, DescriptorVisibilities.PRIVATE, e, false) + extractField(fieldId, "", functionType, classId, locId, DescriptorVisibilities.PRIVATE, e, isExternalDeclaration = false, isFinal = true) // adjust constructor helper.extractParameterToFieldAssignmentInConstructor("", functionType, fieldId, 0, 1) diff --git a/java/ql/test/kotlin/library-tests/field-initializer-flow/test.expected b/java/ql/test/kotlin/library-tests/field-initializer-flow/test.expected new file mode 100644 index 00000000000..ac60487f5bf --- /dev/null +++ b/java/ql/test/kotlin/library-tests/field-initializer-flow/test.expected @@ -0,0 +1,4 @@ +isFinalField +| test.kt:3:3:3:18 | x | +#select +| test.kt:3:3:3:18 | this.x | test.kt:6:10:6:10 | getX(...) | diff --git a/java/ql/test/kotlin/library-tests/field-initializer-flow/test.kt b/java/ql/test/kotlin/library-tests/field-initializer-flow/test.kt new file mode 100644 index 00000000000..a8598658da4 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/field-initializer-flow/test.kt @@ -0,0 +1,11 @@ +class Test { + + val x = "Source" + + fun test() { + sink(x) + } + + fun sink(s: String) { } + +} diff --git a/java/ql/test/kotlin/library-tests/field-initializer-flow/test.ql b/java/ql/test/kotlin/library-tests/field-initializer-flow/test.ql new file mode 100644 index 00000000000..0dead2fb5e1 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/field-initializer-flow/test.ql @@ -0,0 +1,22 @@ +import java +import semmle.code.java.dataflow.DataFlow + +class Config extends DataFlow::Configuration { + + Config() { this = "dlkjhfgsdjgfhd2" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(CompileTimeConstantExpr).getStringValue() = "Source" + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().getName() = "sink" + } + +} + +query predicate isFinalField(Field f) { exists(FieldDeclaration f2 | f = f2.getAField()) and f.isFinal() } + +from DataFlow::Node source, DataFlow::Node sink +where any(Config c).hasFlow(source, sink) +select source, sink