def collect_fields_and_class_vars(
        self, model_config: ModelConfigData, is_root_model: bool
    ) -> tuple[list[PydanticModelField] | None, list[PydanticModelClassVar] | None]:
        """Collects the fields for the model, accounting for parent classes."""
        cls = self._cls

        # First, collect fields and ClassVars belonging to any class in the MRO, ignoring duplicates.
        #
        # We iterate through the MRO in reverse because attrs defined in the parent must appear
        # earlier in the attributes list than attrs defined in the child. See:
        # https://docs.python.org/3/library/dataclasses.html#inheritance
        #
        # However, we also want fields defined in the subtype to override ones defined
        # in the parent. We can implement this via a dict without disrupting the attr order
        # because dicts preserve insertion order in Python 3.7+.
        found_fields: dict[str, PydanticModelField] = {}
        found_class_vars: dict[str, PydanticModelClassVar] = {}
        for info in reversed(cls.info.mro[1:-1]):  # 0 is the current class, -2 is BaseModel, -1 is object
            # if BASEMODEL_METADATA_TAG_KEY in info.metadata and BASEMODEL_METADATA_KEY not in info.metadata:
            #     # We haven't processed the base class yet. Need another pass.
            #     return None, None
            if METADATA_KEY not in info.metadata:
                continue

            # Each class depends on the set of attributes in its dataclass ancestors.
            self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname))

            for name, data in info.metadata[METADATA_KEY]['fields'].items():
                field = PydanticModelField.deserialize(info, data, self._api)
                # (The following comment comes directly from the dataclasses plugin)
                # TODO: We shouldn't be performing type operations during the main
                #       semantic analysis pass, since some TypeInfo attributes might
                #       still be in flux. This should be performed in a later phase.
                field.expand_typevar_from_subtype(cls.info, self._api)
                found_fields[name] = field

                sym_node = cls.info.names.get(name)
                if sym_node and sym_node.node and not isinstance(sym_node.node, Var):
                    self._api.fail(
                        'BaseModel field may only be overridden by another field',
                        sym_node.node,
                    )
            # Collect ClassVars
            for name, data in info.metadata[METADATA_KEY]['class_vars'].items():
                found_class_vars[name] = PydanticModelClassVar.deserialize(data)

        # Second, collect fields and ClassVars belonging to the current class.
        current_field_names: set[str] = set()
        current_class_vars_names: set[str] = set()
        for stmt in self._get_assignment_statements_from_block(cls.defs):
            maybe_field = self.collect_field_or_class_var_from_stmt(stmt, model_config, found_class_vars)
            if maybe_field is None:
                continue

            lhs = stmt.lvalues[0]
            assert isinstance(lhs, NameExpr)  # collect_field_or_class_var_from_stmt guarantees this
            if isinstance(maybe_field, PydanticModelField):
                if is_root_model and lhs.name != 'root':
                    error_extra_fields_on_root_model(self._api, stmt)
                else:
                    current_field_names.add(lhs.name)
                    found_fields[lhs.name] = maybe_field
            elif isinstance(maybe_field, PydanticModelClassVar):
                current_class_vars_names.add(lhs.name)
                found_class_vars[lhs.name] = maybe_field

        return list(found_fields.values()), list(found_class_vars.values())
Collects the fields for the model, accounting for parent classes.
pydantic/mypy.py