mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-06 15:49:59 +00:00
fix(resolver): Kotlin extension receivers take standard subtypes; vararg need not be last
An extension on Iterable or Collection applies to a List, a Set or a project Iterable, and to nothing else. Past a vararg, position no longer says which parameter an argument lands on, so it no longer excludes. A project class is never an Array. resolver_oo: 7 pass, 0 fail. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
a401ffa979
commit
95053d34e7
4 files changed
+73
-3
No files matched your search
@@ -1176,6 +1176,10 @@ static bool oo_argument_excludes(oo_walk_t *w, const oo_callable_t *callable,
|
||||
return true;
|
||||
if (callable->max_args < 0 && index == callable->param_count - 1)
|
||||
continue; /* Variadic element versus array conversion is not modelled. */
|
||||
/* Past a vararg, position no longer says which parameter an argument lands on. */
|
||||
if (callable->vararg_position && i >= callable->vararg_position - 1 &&
|
||||
!args[i].function_literal)
|
||||
continue;
|
||||
if (oo_ref_is_tparam(callable, callable->params[index]))
|
||||
continue;
|
||||
oo_value_t parameter = oo_value_from_ref(w, callable->params[index], &scope, subst);
|
||||
@@ -1189,6 +1193,10 @@ static bool oo_argument_excludes(oo_walk_t *w, const oo_callable_t *callable,
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
/* Array is final: a project class is never one. */
|
||||
if (parameter.external && !strcmp(oo_leaf(parameter.external), "Array") && argument->type &&
|
||||
oo_hierarchy_complete(w->ix, argument->type))
|
||||
return true;
|
||||
if (parameter.fn_ref) {
|
||||
/* A value whose whole hierarchy is known is not a function. */
|
||||
if (argument->type && oo_hierarchy_complete(w->ix, argument->type))
|
||||
|
||||
@@ -94,6 +94,7 @@ typedef struct oo_callable {
|
||||
const oo_file_t *file;
|
||||
uint32_t flags;
|
||||
int min_args, max_args; /* max_args -1: variadic. */
|
||||
int vararg_position; /* Kotlin: 1 + the vararg's index; it need not be last. */
|
||||
const oo_typeref_t **params;
|
||||
int param_count;
|
||||
const oo_typeref_t *ret;
|
||||
|
||||
@@ -362,6 +362,8 @@ static void kh_parameters(const kh_t *h, oo_callable_t *callable, TSNode paramet
|
||||
if (refs)
|
||||
refs[total] = kt_typeref(&h->text, type);
|
||||
total++;
|
||||
if (pending_vararg && !vararg)
|
||||
callable->vararg_position = total;
|
||||
vararg |= pending_vararg;
|
||||
pending_vararg = false;
|
||||
/* A class parameter keeps its default inside its own node. */
|
||||
@@ -797,6 +799,41 @@ static bool kt_explicit_external(oo_walk_t *w, const oo_scope_t *scope, const ch
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The standard library's collection and text subtyping, for extension receivers. */
|
||||
static bool kt_external_conforms(const char *actual, const char *declared) {
|
||||
static const struct {
|
||||
const char *actual, *supertypes;
|
||||
} TABLE[] = {
|
||||
{"List", " Collection Iterable "},
|
||||
{"Set", " Collection Iterable "},
|
||||
{"Collection", " Iterable "},
|
||||
{"MutableList", " List Collection MutableCollection Iterable MutableIterable "},
|
||||
{"MutableSet", " Set Collection MutableCollection Iterable MutableIterable "},
|
||||
{"MutableCollection", " Collection Iterable MutableIterable "},
|
||||
{"ArrayList", " MutableList List Collection MutableCollection Iterable "},
|
||||
{"LinkedHashSet", " MutableSet Set Collection MutableCollection Iterable "},
|
||||
{"HashSet", " MutableSet Set Collection MutableCollection Iterable "},
|
||||
{"MutableMap", " Map "},
|
||||
{"HashMap", " MutableMap Map "},
|
||||
{"LinkedHashMap", " MutableMap Map HashMap "},
|
||||
{"String", " CharSequence Comparable "},
|
||||
{"Int", " Number Comparable "},
|
||||
{"Long", " Number Comparable "},
|
||||
{"Double", " Number Comparable "},
|
||||
{"Float", " Number Comparable "},
|
||||
{"Short", " Number Comparable "},
|
||||
{"Byte", " Number Comparable "},
|
||||
};
|
||||
char word[64];
|
||||
int written = snprintf(word, sizeof(word), " %s ", declared);
|
||||
if (written <= 0 || (size_t)written >= sizeof(word))
|
||||
return false;
|
||||
for (size_t i = 0; i < sizeof(TABLE) / sizeof(TABLE[0]); i++)
|
||||
if (!strcmp(TABLE[i].actual, actual))
|
||||
return strstr(TABLE[i].supertypes, word) != NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Does an extension's declared receiver accept the value? */
|
||||
static bool kt_ref_accepts(oo_walk_t *w, const oo_typeref_t *ref, oo_scope_t scope,
|
||||
const oo_value_t *value) {
|
||||
@@ -823,11 +860,15 @@ static bool kt_ref_accepts(oo_walk_t *w, const oo_typeref_t *ref, oo_scope_t sco
|
||||
declared = declared ? declared + 1 : receiver.external;
|
||||
if (!strcmp(declared, "Any"))
|
||||
return value->type || value->external;
|
||||
if (!value->external)
|
||||
return false;
|
||||
/* A project collection reaches Iterable through its declared supertypes. */
|
||||
if (!value->external) {
|
||||
oo_value_t element =
|
||||
!strcmp(declared, "Iterable") ? oo_element_of(w, value) : oo_value_unknown();
|
||||
return oo_value_known(&element);
|
||||
}
|
||||
const char *actual = strrchr(value->external, '.');
|
||||
actual = actual ? actual + 1 : value->external;
|
||||
return !strcmp(declared, actual);
|
||||
return !strcmp(declared, actual) || kt_external_conforms(actual, declared);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -320,6 +320,12 @@ static const OOFile KOTLIN_FILES[] = {
|
||||
"private fun fileOnly(): Int = 5\n"
|
||||
"val topProperty: Store = Store()\n"
|
||||
"val Store.extProp: Store get() = this\n"
|
||||
"fun <T> Iterable<T>.onIterable(): Int = 1\n"
|
||||
"fun Collection<Int>.onCollection(): Int = 1\n"
|
||||
"class Rows : Iterable<Store> {\n"
|
||||
" override fun iterator(): Iterator<Store> = TODO()\n"
|
||||
"}\n"
|
||||
"fun <T> middleVararg(first: Int, vararg rest: Store, block: () -> Unit): Int = 1\n"
|
||||
"fun String.shout(): String = this\n"
|
||||
"fun Int.onlyInt(): Int = this\n"},
|
||||
{"core/Tx.kt",
|
||||
@@ -378,6 +384,12 @@ static const OOFile KOTLIN_FILES[] = {
|
||||
" fun externalExtension() { text?.shout() }\n"
|
||||
" fun externalWrongReceiver() { text?.onlyInt() }\n"
|
||||
" fun extensionProperty(s: Store) { s.extProp.get() }\n"
|
||||
" fun listIsIterable(l: List<Int>) { l.onIterable() }\n"
|
||||
" fun projectIsIterable(r: Rows) { r.onIterable() }\n"
|
||||
" fun setIsCollection(v: Set<Int>) { v.onCollection() }\n"
|
||||
" fun stringIsNoCollection(t: String) { t.onCollection() }\n"
|
||||
" fun storeIsNoIterable(s: Store) { s.onIterable() }\n"
|
||||
" fun varargInTheMiddle(s: Store) { middleVararg<Int>(1, s, s) { } }\n"
|
||||
" fun wrongExtensionProperty(s: Store) { s.otherProp }\n"
|
||||
" fun objectValue() { val t = object : Store() { fun own() = 1 }; t.get(); t.ext() }\n"
|
||||
" fun objectOwnMember() {\n"
|
||||
@@ -495,6 +507,14 @@ static int oo_kotlin_check(bool parallel) {
|
||||
/* An extension on a value whose type lies outside the project. */
|
||||
OO_EXPECT("CALLS", "Main.externalExtension", "Store.shout", 1);
|
||||
OO_EXPECT("CALLS", "Main.externalWrongReceiver", "Store.onlyInt", 0);
|
||||
/* An extension's receiver takes the standard library's subtypes, and only those. */
|
||||
OO_EXPECT("CALLS", "Main.listIsIterable", "Store.onIterable", 1);
|
||||
OO_EXPECT("CALLS", "Main.projectIsIterable", "Store.onIterable", 1);
|
||||
OO_EXPECT("CALLS", "Main.setIsCollection", "Store.onCollection", 1);
|
||||
OO_EXPECT("CALLS", "Main.stringIsNoCollection", "Store.onCollection", 0);
|
||||
OO_EXPECT("CALLS", "Main.storeIsNoIterable", "Store.onIterable", 0);
|
||||
/* Past a vararg, position does not say which parameter an argument lands on. */
|
||||
OO_EXPECT("CALLS", "Main.varargInTheMiddle", "Store.middleVararg", 1);
|
||||
/* Extension properties: only the receiver they are declared on. */
|
||||
OO_EXPECT("USAGE", "Main.extensionProperty", "Store.extProp", 1);
|
||||
OO_EXPECT("CALLS", "Main.extensionProperty", "Store.Store.get", 1);
|
||||
|
||||
Reference in new issue
Block a user