Repository: pydantic/pydantic

Issue:
`Field` metadata from `Annotated` is lost after a call to `update_forward_refs`
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.8.2
            pydantic compiled: False
                 install path: /usr/lib/python3.9/site-packages/pydantic
               python version: 3.9.7 (default, Aug 31 2021, 13:28:12)  [GCC 11.1.0]
                     platform: Linux-5.14.8-arch1-1-x86_64-with-glibc2.33
     optional deps. installed: ['email-validator', 'typing-extensions']
```
```py
from __future__ import annotations
from typing import Annotated

from pydantic import BaseModel, Field


class Works(BaseModel):
    foo: list[Foo] = Field(..., alias="bar")


class Fails(BaseModel):
    foo: Annotated[list[Foo], Field(alias="bar")]


class Foo(BaseModel):
    a: int


Works.update_forward_refs()
Fails.update_forward_refs()

json = '{"bar":[{"a":0}]}'

Works.parse_raw(json)
Fails.parse_raw(json)
```
With the postponed evaluation of annotations enabled, the `Field` metadata seems to be lost after the `update_forward_refs` call. Apart from that it seemed to be working fine, it parsed the JSON if the field name was `foo`. I also tried the `allow_mutation` argument, it was not working as well.

Without the `from __future__ import annotations` import, all of the following worked:
```py
foo: "list[Foo]" = Field(..., alias="bar")
foo: List["Foo"] = Field(..., alias="bar")
foo: list[ForwardRef("Foo")] = Field(..., alias="bar")
foo: Annotated["list[Foo]", Field(alias="bar")]
foo: Annotated[List["Foo"], Field(alias="bar")]
foo: Annotated[list[ForwardRef("Foo")], Field(alias="bar")]
```


Discussion:
I reproduced your bug and debugged a little, and I 100% agree: the root of the problem is that the `update_forward_refs` method doesn't correctly parse the information from the `FieldInfo` of `Annoted` types.

I tried to propose a fix here: https://github.com/samuelcolvin/pydantic/pull/3283
It looks like this is now fixed in both v1 and v2
Whoa! Why was this issue closed without merging the fix? 
I find it is still reproducing at 
```
pydantic==2.4.2
pydantic_core==2.10.1
```

reproducer is exactly the same:
```
from __future__ import annotations

class Foo(BaseModel):
    bar: Annotated[Bar | None, Field(alias="_bar")] = None

class Bar(BaseModel):
    foo: int | None = None


def test_model():
    data= '{"_bar": {"foo": 42}}'
    model = Foo.model_validate_json(data)
    assert model.bar.foo == 42
```

throws `AttributeError: 'NoneType' object has no attribute 'foo'`

but if we change the order of declarations, validation successfully loads the alias and test succeeds:

```

class Bar(BaseModel):
    foo: int | None = None

class Foo(BaseModel):
    bar: Annotated[Bar | None, Field(alias="_bar")] = None


def test_model():
    data= '{"_bar": {"foo": 42}}'
    model = Foo.model_validate_json(data)
    assert model.bar.foo == 42
```

@dmontagu could you please reopen this issue, or should I probably file a new one?
I'm not sure why I closed it, clearly it's still an issue. Apologies for that.

Resolving patch:
diff --git a/pydantic/_internal/_generate_schema.py b/pydantic/_internal/_generate_schema.py
--- a/pydantic/_internal/_generate_schema.py
+++ b/pydantic/_internal/_generate_schema.py
@@ -897,7 +897,9 @@ def _generate_dc_field_schema(
             metadata=common_field['metadata'],
         )
 
-    def _common_field_schema(self, name: str, field_info: FieldInfo, decorators: DecoratorInfos) -> _CommonField:
+    def _common_field_schema(  # noqa C901
+        self, name: str, field_info: FieldInfo, decorators: DecoratorInfos
+    ) -> _CommonField:
         # Update FieldInfo annotation if appropriate:
         if has_instance_in_type(field_info.annotation, (ForwardRef, str)):
             types_namespace = self._types_namespace
@@ -910,6 +912,14 @@ def _common_field_schema(self, name: str, field_info: FieldInfo, decorators: Dec
             if evaluated is not field_info.annotation and not has_instance_in_type(evaluated, PydanticRecursiveRef):
                 field_info.annotation = evaluated
 
+                # Handle any field info attributes that may have been obtained from now-resolved annotations
+                new_field_info = FieldInfo.from_annotation(evaluated)
+                for k, v in new_field_info._attributes_set.items():
+                    # If an attribute is already set, it means it was set by assigning to a call to Field (or just a
+                    # default value), and that should take the highest priority. So don't overwrite existing attributes.
+                    if k not in field_info._attributes_set:
+                        setattr(field_info, k, v)
+
         source_type, annotations = field_info.annotation, field_info.metadata
 
         def set_discriminator(schema: CoreSchema) -> CoreSchema:


Test patch:
diff --git a/tests/test_annotated.py b/tests/test_annotated.py
--- a/tests/test_annotated.py
+++ b/tests/test_annotated.py
@@ -373,3 +373,30 @@ def test_predicate_error_python() -> None:
             'input': -1,
         }
     ]
+
+
+def test_annotated_field_info_not_lost_from_forwardref():
+    from pydantic import BaseModel
+
+    class ForwardRefAnnotatedFieldModel(BaseModel):
+        foo: 'Annotated[Integer, Field(alias="bar", default=1)]' = 2
+        foo2: 'Annotated[Integer, Field(alias="bar2", default=1)]' = Field(default=2, alias='baz')
+
+    Integer = int
+
+    ForwardRefAnnotatedFieldModel.model_rebuild()
+
+    assert ForwardRefAnnotatedFieldModel(bar=3).foo == 3
+    assert ForwardRefAnnotatedFieldModel(baz=3).foo2 == 3
+
+    with pytest.raises(ValidationError) as exc_info:
+        ForwardRefAnnotatedFieldModel(bar='bar')
+    assert exc_info.value.errors() == [
+        {
+            'input': 'bar',
+            'loc': ('bar',),
+            'msg': 'Input should be a valid integer, unable to parse string as an integer',
+            'type': 'int_parsing',
+            'url': 'https://errors.pydantic.dev/2.4/v/int_parsing',
+        }
+    ]