sdk icon indicating copy to clipboard operation
sdk copied to clipboard

[Macro] [Analyzer] [Error] [Invalid argument(s): Unresolved identifier: int

Open microtears opened this issue 1 year ago • 7 comments
trafficstars

Macro crashes on this code: param.type.code.

Macro Example

@FunctionalWidget()
void myComponent(int a) {}

import 'package:macros/macros.dart';

macro class FunctionalWidget implements FunctionTypesMacro {
  const FunctionalWidget();
  @override
  Future<void> buildTypesForFunction(
      FunctionDeclaration function, TypeBuilder builder) async {
    builder.declareType(
      'Component',
      DeclarationCode.fromParts([
        "class Component {\n",
        for (var param in function.positionalParameters)
          DeclarationCode.fromParts([
            'final ',
            // 'dynamic',
            // TODO(re): Use the actual type of the parameter
            param.type.code,
            ' ',
            param.identifier.name,
            ';\n',
          ]),
        "}",
      ]),
    );
  }
}

Crash Log

[11:26:15] [Analyzer] [Error] [Invalid argument(s): Unresolved identifier: int
#0      DeclarationBuilder.resolveIdentifier (package:analyzer/src/summary2/macro_declarations.dart:343:9)
#1      _Builder._buildIdentifier (package:_macros/src/executor/augmentation_library.dart:73:53)
#2      _Builder._buildCode (package:_macros/src/executor/augmentation_library.dart:133:9)
#3      _Builder._buildCode (package:_macros/src/executor/augmentation_library.dart:131:9)
#4      _Builder._buildCode (package:_macros/src/executor/augment…
[11:26:15] [Analyzer] [Info] ==> Content-Length: 189
[11:26:15] [Analyzer] [Info] ==> {"jsonrpc":"2.0","id":9,"method":"textDocument/codeLens","params":{"textDocument":{"uri":"file:///Users/anya/tmp/dart_package_1/lib/dart_package_1.dart"}},"clientRequestTime":1724383575689}
[11:26:15] [Analyzer] [Info] Spawning /Users/anya/fvm/versions/master/bin/cache/dart-sdk/bin/dart with args ["language-server","--protocol=lsp","--port=9278","--client-id=VS-Code","--client-version=3.95.20240821"]
[11:26:15] [Analyzer] [Info]     PID: 35747
[11:26:15] [Analyzer] [Info] ==> Content-Length: 6580
[11:26:15] [Analyzer] [Info] ==> {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":35712,"clientInfo":{"name":"Visual Studio Code","version":"1.92.2"},"locale":"en","rootPath":"/Users/anya/tmp/dart_package_1","rootUri":"file:///Users/anya/tmp/dart_package_1","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional","normalizesLineEndings":true,"changeAnnotationSupport":{"groupsOnLabel":tr…

Dart info

dart info

If providing this information as part of reporting a bug, please review the information
below to ensure it only contains things you're comfortable posting publicly.

#### General info

- Dart 3.6.0-167.0.dev (dev) (Wed Aug 21 09:03:19 2024 -0700) on "macos_arm64"
- on macos / Version 14.1 (Build 23B74)
- locale is zh-Hans-CN

#### Process info

| Memory |  CPU | Elapsed time | Command line                                                                               |
| -----: | ---: | -----------: | ------------------------------------------------------------------------------------------ |
|  21 MB | 0.0% |        10:55 | dart devtools --machine --allow-embedding --dtd-uri ws:<path>/zYcPhgAawn0hN8pc --port 9277 |
|  49 MB | 0.0% |        10:55 | dart language-server --protocol=lsp --port=9278 --client-id=VS-Code --client-version=3.95.20240821 |
|  23 MB | 0.0% |        10:55 | dart tooling-daemon --machine                                                              |
|  23 MB | 0.0% |        02:50 | dart tooling-daemon --machine                                                              |
|  44 MB | 0.0% |        10:55 | flutter_tools.snapshot daemon                                                              |

microtears avatar Aug 23 '24 03:08 microtears

@scheglov Do you have a moment to look at the problem?

microtears avatar Aug 23 '24 03:08 microtears

Summary: The macro crashes when trying to access the code property of a parameter's type. The analyzer throws an error because it cannot resolve the int identifier within the macro's generated code.

dart-github-bot avatar Aug 23 '24 03:08 dart-github-bot

I'm not sure if this can work. Theoretically, the macro code generator cannot invoke resolveIdentifier during the types phase. We don't have all types yet, so we cannot resolve them. We resolve types only after the types phase.

@jakemac53 should we allow param.type.code at all during the types phase?

scheglov avatar Aug 24 '24 17:08 scheglov

Does this mean that the current FunctionTypesMacro cannot determine the field type of a new type based on the type of a function parameter as described in the example? I have also tried FunctionDeclarationsMacro and FunctionDefinitionMacro, but still cannot change the field type at this stage. Is there any other way to achieve this goal?

microtears avatar Aug 25 '24 19:08 microtears

@scheglov This is expected to be able to work - although I get that it is weird. But, technically this resolveIdentifier call is happening after all types phase macros have ran. This is in between the types/declarations phases.

You should know the names of all the types that were defined (this is why we make you give the name as a string, along with the declaration). So you know everything that has been added in this phase. And afaik, should be able to resolve any types given just that information.

jakemac53 avatar Aug 26 '24 20:08 jakemac53

Interesting. You are probably right that theoretically we know MacroExecutionResult.newTypeNames from all macro applications in all libraries of the library cycle, and so we could know the location of each type identifier, and report it through

      case InterfaceElement():
        return macro.ResolvedIdentifier(
          kind: macro.IdentifierKind.topLevelMember,
          name: element.name,
          uri: element.library.source.uri,
          staticScope: null,
        );

Practically this seems complicated to do, because currently we compute name scopes using actual elements, consider exports, etc. And in between the types/declarations phases we don't have yet newly declared classes, we are just generating code for them!

So, in the analyzer we will have either generalize this to use not only elements, but also something like FutureElement.

Or maybe add stubs in form of synthetic ClassElement for any newTypeNames entry, build scopes as we currently do, resolve types, build macro generated code, build actual elements, patch any reference to synthetic ClassElements to reference the actual element (not necessary even ClassElement).

Or maybe use a different implementation of resolveIdentifier(Identifier) during the types phase, not necessary very performant, just look around in the identifier library, and use fast lookup for imported libraries from outside of the current library cycle, and slower checks for newTypeNames when probing libraries in the current library cycle.

Do we support adding macro applications to macro generated types? Currently we execute the types phase iteratively, adding new partial macro generated parts, and looking for new macro applications in them, including the types phase. If we rely on the knowledge of all type names inside resolveIdentifier(), we should not generate code during the types phase file by file, iteratively; we can do only one shot.

scheglov avatar Aug 26 '24 21:08 scheglov

Do we support adding macro applications to macro generated types? Currently we execute the types phase iteratively, adding new partial macro generated parts, and looking for new macro applications in them, including the types phase. If we rely on the knowledge of all type names inside resolveIdentifier(), we should not generate code during the types phase file by file, iteratively; we can do only one shot.

Yes, we do allow that. And I think they can even be applied in the types phase. So, it seems my statement was incorrect that the types phase would always be fully completed.

Most of the time, a bare identifier would work here as well. Especially if it is just a part of a type annotation, where it can't be referring to something in the surrounding textual scope (that isn't repeated in the augmentation like a type parameter). So, I am very tempted to just say we should not call resolveIdentifier at all for types, and simply emit them unprefixed. However, an augmentation could end up shadowing the type name with a static member I think, which would break this:

class A {
  int x;
}

augment class A {
  augment int x = 1; // error, int isn't a type 
  static bool int = true; 
}

I think that is basically the only situation (plus type parameter names), that actually breaks, if we assume we always use prefixed imports in augmentation libraries. But, we still probably can't do it because of those scenarios :/

jakemac53 avatar Aug 26 '24 21:08 jakemac53

Any updates on this?

feduke-nukem avatar Sep 15 '24 17:09 feduke-nukem