Repository: edgedb/edgedb

Issue:
InternalServerError when trying to create a migration with a link or property containing `extending` without the `link`/`property` keyword
<!-- Please search existing issues to avoid creating duplicates. -->

<!--
For the EdgeDB Version: run `edgedb query 'select sys::get_version_as_str()'` from your project directory (or run `select sys::get_version_as_str();` in the EdgeDB interactive shell).
For the EdgeDB CLI Version: Run `edgedb --version` from anywhere
-->

- EdgeDB Version: 3.0-dev.7306+e94a60a
- EdgeDB CLI Version: 3.0.0-dev.939+7d55899
- OS Version: macOS Ventura 13.2.1

Steps to Reproduce:

1. Start a new project
2. Add the following schema definition in `default.esdl`:
 ```
   module default {
    abstract link friendship {
      property strength: float64;
      index on (__subject__@strength);
    }

    type User {
      multi friends extending friendship: User;
    }
}
```
3. Run `edgedb migration create`



Discussion:

Resolving patch:
diff --git a/edb/edgeql/declarative.py b/edb/edgeql/declarative.py
--- a/edb/edgeql/declarative.py
+++ b/edb/edgeql/declarative.py
@@ -199,6 +199,33 @@ def get_fq_name(
         return name, fq_name
 
 
+def ensure_pointer_kind(
+    node: qlast.CreateConcretePointer,
+    ctx: DepTraceContext | LayoutTraceContext,
+) -> qlast.CreateConcretePointer:
+
+    # If the link/property specifier was left off the SDL, fill it
+    # in here.
+    if isinstance(node, qlast.CreateConcreteUnknownPointer):
+        # I /think/ the parser shouldn't let through anything that
+        # violates this but...
+        if not isinstance(node.target, qlast.TypeExpr):
+            raise errors.SchemaError(
+                "declarations without link/property specify must have "
+                "an explicitly specified target type",
+                context=node.context,
+            )
+
+        typ = _resolve_type_expr(node.target, ctx=ctx)
+        cls = (
+            qlast.CreateConcreteLink
+            if typ.is_object_type() else qlast.CreateConcreteProperty
+        )
+        node = node.replace(__class__=cls)
+
+    return node
+
+
 def get_verbosename_from_fqname(
     fq_name: s_name.QualName,
     ctx: DepTraceContext,
@@ -621,6 +648,8 @@ def _trace_item_layout(
                 target = None
                 target_expr = decl.target
 
+            decl = ensure_pointer_kind(decl, ctx=ctx)
+
             pn = s_utils.ast_ref_to_unqualname(decl.name)
 
             PointerType = (
@@ -894,17 +923,6 @@ def trace_ConcretePointer(
     deps: List[Dependency] = []
     if isinstance(node.target, qlast.TypeExpr):
         deps.append(TypeDependency(texpr=node.target))
-
-        # If the link/property specifier was left off the SDL, fill it
-        # in here.
-        if isinstance(node, qlast.CreateConcreteUnknownPointer):
-            typ = _resolve_type_expr(node.target, ctx=ctx)
-            cls = (
-                qlast.CreateConcreteLink
-                if typ.is_object_type() else qlast.CreateConcreteProperty
-            )
-            node = node.replace(__class__=cls)
-
     elif isinstance(node.target, qlast.Expr):
         deps.append(ExprDependency(expr=node.target))
     elif node.target is None:
@@ -913,6 +931,7 @@ def trace_ConcretePointer(
         raise AssertionError(
             f'unexpected CreateConcretePointer.target: {node.target!r}')
 
+    node = ensure_pointer_kind(node, ctx=ctx)
     _register_item(
         node,
         hard_dep_exprs=deps,


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -3560,10 +3560,15 @@ def test_schema_pointer_kind_infer_01(self):
         tschema = r'''
         type Bar;
         scalar type scl extending str;
+        abstract link friendship {
+            property strength: float64;
+            index on (__subject__@strength);
+        };
         type Foo {
             name: str;
             foo: Foo;
             bar: Bar;
+            bar2 extending friendship: Bar;
             or_: Foo | Bar;
             array1: array<str>;
             array2: array<scl>;
@@ -3578,6 +3583,7 @@ def test_schema_pointer_kind_infer_01(self):
         obj.getptr(schema, s_name.UnqualName('array2'), type=s_props.Property)
         obj.getptr(schema, s_name.UnqualName('foo'), type=s_links.Link)
         obj.getptr(schema, s_name.UnqualName('bar'), type=s_links.Link)
+        obj.getptr(schema, s_name.UnqualName('bar2'), type=s_links.Link)
         obj.getptr(schema, s_name.UnqualName('or_'), type=s_links.Link)
 
     def test_schema_migrations_equivalence_01(self):