floor icon indicating copy to clipboard operation
floor copied to clipboard

[Floor 1.1.0] Impossible to generate database dart file since latest update

Open istornz opened this issue 3 years ago • 12 comments

Hello, I'm trying to update my project with the latest version of Floor.

My project was not migrated to null safety at the moment, so to continue generating database dart file, I added // @dart=2.12 at the beginning of all my dao files like (workaround found here #507).

Now, when I try to generate my database with flutter packages pub run build_runner build --delete-conflicting-outputs, I get 8 errors like this:

[SEVERE] floor_generator:floor_generator on lib/database.dart:
An error `FormatterException` occurred while formatting the generated source for
  `package:my_project/database.dart`
which was output to
  `lib/database.floor.g.part`.
This may indicate an issue in the generator, the input source code, or in the
source formatter.
Could not format because the source could not be parsed:

line 295, column 11 of .: Methods must have an explicit list of parameters.
    ╷
295 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
    │           ^^^^^^
    ╵
line 282, column 18 of .: Expected to find '>'.
    ╷
282 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasThemes', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
Complete log
[SEVERE] floor_generator:floor_generator on lib/database.dart:
An error `FormatterException` occurred while formatting the generated source for
`package:my_project/database.dart`
which was output to
`lib/database.floor.g.part`.
This may indicate an issue in the generator, the input source code, or in the
source formatter.
Could not format because the source could not be parsed:

line 295, column 11 of .: Methods must have an explicit list of parameters.
  ╷
295 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │           ^^^^^^
  ╵
line 282, column 18 of .: Expected to find '>'.
  ╷
282 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasThemes', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                  ^^^^
  ╵
line 295, column 36 of .: A function body must be provided.
  ╷
295 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                                    ^
  ╵
line 269, column 11 of .: Methods must have an explicit list of parameters.
  ╷
269 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │           ^^^^^^
  ╵
line 269, column 36 of .: Methods must have an explicit list of parameters.
  ╷
269 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                                    ^
  ╵
line 282, column 38 of .: A function body must be provided.
  ╷
282 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasThemes', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                                      ^^^^^^^^^^^^^
  ╵
line 295, column 18 of .: Expected to find '>'.
  ╷
295 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                  ^^^^
  ╵
line 269, column 36 of .: Operator declarations must be preceded by the keyword 'operator'.
  ╷
269 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasGenres', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                                    ^
  ╵
line 282, column 36 of .: A function body must be provided.
  ╷
282 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasThemes', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                                    ^
  ╵
line 282, column 36 of .: Methods must have an explicit list of parameters.
  ╷
282 │ @override Future<List<GameGenre*>*>* findAllGenres() async  { return _queryAdapter.queryList('SELECT * FROM GameHasThemes', mapper: (Map<String, Object?> row) => GameGenre(identifier: row['identifier'] as int?, name: row['name'] as String?, url: row['url'] as String?, slug: row['slug'] as String?)); }
  │                                    ^
  ╵
(8 more errors...)
[INFO] Running build completed, took 14.8s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 82ms

[SEVERE] Failed after 14.9s

NOTE: Before upgrading v1.0.0, I was able to generate database file, so my schemas seem to be valid.

Is it related to #531 & #551 ?

Any help would be greatly appreciated! Thanks in advance 👍

istornz avatar May 01 '21 09:05 istornz

Future<List<GameGenre*>*>*

Why the *s? Maybe I don't know this feature but dart has no pointer type afaik. What did you try to express?

(or is this just in the error message? What is the dao method you've provided?)

mqus avatar May 01 '21 11:05 mqus

@mqus thanks for the fast reply! I don't have any pointer in my code... This is very strange, it seems floor add it when generating :/ This is on the error message.

Here is my dao:

@dao
abstract class GenreDao {
  @Query('SELECT * FROM Genre')
  Future<List<GameGenre>> findAllGenres();

  // [...]
}

istornz avatar May 01 '21 11:05 istornz

right. I investigated a bit more and * is just darts representation of "I don't know if this is nullable or not" to represent legacy code. I think I found the bug (it is unrelated to escaping), floor inserts ? into the entity constructor for types that it determines are nullable (like all legacy types). But the compiler does not understand ? if nullsafety is off. Other places use a library for adding nullability modifiers, which can (?) consider those differences (by e.g. adding a *) and we should use it there, too. I'll look into it.

I'm actually not sure which symbol confuses your compiler, if it is ?, * or both. Could you try removing them each and see if the compiler still complains?

A quick workaround could then be to remove the symbols in the generated code (not very ergonomic) and you could also try to add the // @dart=2.12 comment on the file with the @Database class. I currently don't have any old code lying around so I can't test this myself, sorry. In the longterm, you should really migrate your code to nullsafety. It really benefits your code and I'm not sure we can provide complete compatibility for non-nullsafe code.

mqus avatar May 01 '21 12:05 mqus

(does it generate some output at least or is there nothing?)

mqus avatar May 01 '21 12:05 mqus

@mqus Thanks for your explanation.

Here is what I tried:

  • Adding // @dart=2.12 at the top of my @Database trigger me a lot of errors because of non null safety code in here (should take more time to convert it).
  • Removing symbols in the generated code doesn't work because no code was generated :/ In fact, when I execute flutter packages pub run build_runner build --delete-conflicting-outputs: no file was created (--delete-conflicting-outputs doesn't affect the output). Currently, my app is launching because the older database.g.dart file was saved by git :p.
  • It's on my plan to do the migration, I just want to go it step by step (upgrade all my libs, then migrate).

Thanks again for your answer 👍

istornz avatar May 01 '21 13:05 istornz

Can you simply add * or ? to one of your methods (just any one where //@dart=2.12 is not present and see what dartanalyzer/the fullter linter returns? I added something that may fix your function but I don't know ;)

mqus avatar May 01 '21 13:05 mqus

@mqus I added in a random file (without //@dart=2.12 at the beginning)

Before

  Future<List<Game>> aFunction() async {
     // [...]
  }

After

  Future<List<Game>?> aFunction() async {
     // [...]
  }

When I run the generation of the database I had:

This builder requires Dart inputs without syntax errors.
However, package:my_project/services/game_service.dart (or an existing part) contains the following errors.
game_service.dart:138:20: This requires the 'non-nullable' language feature to be enabled.
game_service.dart:36:20: This requires the 'non-nullable' language feature to be enabled.

I tried in a DAO, it's the same, in VSCode, the ? was underlined and error disappear when I had //@dart=2.12.

istornz avatar May 01 '21 13:05 istornz

does the same happen for *?

mqus avatar May 01 '21 15:05 mqus

@mqus Not the same as the ? one:

This builder requires Dart inputs without syntax errors.
However, package:my_project/services/game_service.dart (or an existing part) contains the following errors.
game_service.dart:138:19: Expected to find '>'.
game_service.dart:36:19: Expected to find '>'.

istornz avatar May 01 '21 17:05 istornz

Thank you!

hmm. This means that #559 will probably not solve this issue. Then only the workaround mentioned above(setting the dart version for the database/the generated file) will work. I don't think we can maintain a branch that works for/generates non-nullsafe code. The switch happened with 1.0.0 so this is why you're seeing this error now and not with the 0.x versions.

mqus avatar May 01 '21 20:05 mqus

@mqus Thanks ! Ok no problem, I will wait for all my dependencies to be ready for null safety then migrate my project 😁

istornz avatar May 01 '21 20:05 istornz

hii ,, is it possible to makel floor db with flutter without build_runner i mean build runner generate abc.g.dart file right , so i want to implement that by my self but i am not able to do it is there any resources that can help me to build that file manually .

khushal-chothani avatar Mar 29 '23 05:03 khushal-chothani