Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing dependencies - but only sometimes #491

Open
holzgeist opened this issue Jan 20, 2025 · 2 comments
Open

Missing dependencies - but only sometimes #491

holzgeist opened this issue Jan 20, 2025 · 2 comments

Comments

@holzgeist
Copy link
Contributor

Hi there,

Since the latest release I get warnings about missing dependencies like this:

Missing dependencies in our_app/dependencies.dart

[ServiceA] depends on unregistered type [ServiceB] from package:app/services/service_b.dart

Did you forget to annotate the above class(s) or their implementation with @injectable? 
or add the right environment keys?

However, it's not 100% reproducible and I haven't found a minimal reproducible example yet. For our app it happens in ~50% of runs of dart run build_runner build locally and 100% runs on the same repo/checkout in Github Actions. Also, it's not always the same services affected. And sometimes, it just runs through correctly.

With bisecting I could narrow it down to the last commit in this repo 9aa237b

Despite the lack of a reproducible example, I have some more information on where stuff breaks:

  • the generated our_app/lib/dependencies.config.dart is broken:
    It contains an import in the top section looking like import 'package:our_app/services/service_b.dart' as _i119;.
    The registration of ServiceB doesn't use the alias though: gh.singleton<ServiceB>(() => ServiceB());
    The registration of ServiceA does use the alias: gh.singleton<_i698.ServiceA>(() => _i698.ServiceA(gh<_i119.ServiceB>());
    Because ServiceB is not defined, anything that imports dependencies.config.dart won't compile.
  • the cached json in .dart_tool/build/generated/our_app/lib/services/service_b.injectable.json contains the following (note the null import):
[
  {
    "type": {
      "import": null,
      "name": "ServiceB",
      "isNullable": false,
      "isRecordType": false,
      "nameInRecord": null
    },
    "typeImpl": {
      "import": null,
      "name": "ServiceB",
      "isNullable": false,
      "isRecordType": false,
      "nameInRecord": null
    },
    "isAsync": false,
    "postConstructReturnsSelf": false,
    "preResolve": false,
    "canBeConst": false,
    "injectableType": 1,
    "dependsOn": [],
    "environments": [],
    "dependencies": [],
    "constructorName": "",
    "orderPosition": 0
  }
]

However, the same file for ServiceA looks like this (simplified, it has more dependencies in our app; note the valid import in the dependency to service b):

[
  {
    "type": {
      "import": "package:our_app/services/service_a.dart",
      "name": "ServiceA",
      "isNullable": false,
      "isRecordType": false,
      "nameInRecord": null
    },
    "typeImpl": {
      "import": "package:our_app/services/service_a.dart",
      "name": "ServiceA",
      "isNullable": false,
      "isRecordType": false,
      "nameInRecord": null
    },
    "isAsync": false,
    "postConstructReturnsSelf": false,
    "preResolve": false,
    "canBeConst": false,
    "injectableType": 1,
    "dependsOn": [],
    "environments": [],
    "dependencies": [
      {
        "type": {
          "import": "package:our_app/services/service_b.dart",
          "name": "ServiceB",
          "isNullable": false,
          "isRecordType": false,
          "nameInRecord": null
        },
        "instanceName": null,
        "paramName": "_serviceB",
        "isFactoryParam": false,
        "isPositional": true
      }
    ],
    "constructorName": "",
    "orderPosition": 0
  }
]

both ServiceA and ServiceB are annotated with @singleton.

Is this enough information to narrow down the problem? I can provide more data if you tell me how to get it :)

@holzgeist
Copy link
Contributor Author

Update:
I traced the issue being in this line:

lib.exportNamespace.definedNames.values.contains(element)) {

The condition is false even if the service is defined in the given file.

Therefore I extended the bisect on dart analyzer, which resulted in this commit being the offending one dart-lang/sdk@6494efb which fits the problem perfectly

Basically dart analyzer relies on identical now, which is only true if the "... two object references are to the same object" according to the docs

I traced the call stack as far up as I could and it looks like lib in the above call site originates from the buildStep argument in the builder entrypoint and element originates from library:

FutureOr<String?> generate(LibraryReader library, BuildStep buildStep) async {

My assumtion:
It looks like those arguments are sometimes re-created by build_runner (?) and sometimes passed through.
Does that ring a bell? 😇

@holzgeist
Copy link
Contributor Author

Reported in dart analyzer: dart-lang/sdk#59947

Now I'll wait for someone more familiar with the matter to have a look, I'm way out of my comfort zone with dart 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant