Repository: rrousselGit/freezed

Issue:
code generator reparses same inputs files again and again
I became interested in `freezed` / `build_runner` performance when I saw [this reddit thread](https://www.reddit.com/r/FlutterDev/comments/1bu7wre/genq_a_dart_code_generator_for_data_classes_which/). 

I took @jankuss original benchmark   https://github.com/jankuss/genq/tree/00b0ea50143a1e813d7a1010dc172ccc09483cd1/benchmarks/freezed_benchmark and profiled build_runner invocation. To my surprise most of the time was spent in parsing. So I added a `print('parsing $uri at ${StackTrace.current}')` in `FileState.parse` in analyzer. That revealed that we are just continuously parsing the same files again and again. I then looked at stack traces: that revealed things like [`documentationOfParameter` and `tryGetAstNodeForElement`](https://github.com/rrousselGit/freezed/blob/6e8a14ebcd1c8e41f6da878c5af54510fce6eb47/packages/freezed/lib/src/utils.dart#L8-L46). It seems that for each parameter it calls `getParsedLibraryByElement` which causes the input file to be reparsed. This seems suboptimal and certainly will result in rather poor performance. 

I am by no means a `build` expert, but it seems that `getParsedLibraryByElement` should not be used like that or it should do more caching and not reparse input file again and again. /cc @natebosch @jakemac53

I think currently each input file is reparsed for each constructor and each parameter in constructor? That's a lot of reparsing. So you can probably make freezed run 2 orders of magnitude faster on this benchmark. 


Discussion: > To my surprise most of the time was spent in parsing

How did you know that?
I'll look into it this w-e. Thanks for the heads up.
> How did you know that?

I profiled it with `perf` and then looked at the flame graph.

Is there no way to parse the AST across code-generators?

I have two code-generators that need the AST. Sounds like if someone uses both, all their files will be parsed twice.
> Is there no way to parse the AST across code-generators?

I am not sure. @natebosch @jakemac53 would know. 

I am a bit puzzled why there does not seem to be any caching/sharing - maybe due to mutability of underlying AST?  

Then I would at least expect some documentation highlighting the cost of these methods. 

But again - today was the first time I am even looking at this code. 

I think as long as each code-generator parses input files small amount of times things are going to have acceptable performance, but calling these methods repeatedly for each element seems to be a no-no which would lead to really bad performance. 
Afaik it's a question of "ast is too complex" and "ast costs a lot of memory".

I asked for some improvements there before (as I need the Ast in all of my generators) and this was the TL;DR from what I remember
> I am not sure. @natebosch @jakemac53 would know.
> 
> I am a bit puzzled why there does not seem to be any caching/sharing - maybe due to mutability of underlying AST?

We don't cache at the build system level because it leads to problems like https://github.com/dart-lang/build/issues/3657

> Afaik it's a question of "ast is too complex" and "ast costs a lot of memory".
> 
> I asked for some improvements there before (as I need the Ast in all of my generators) and this was the TL;DR from what I remember

This is the answer for why we don't preemptively parse ASTs for all libraries so they can be read synchronously. The reason we don't cache is the behavior of the analyzer when we reuse objects after adding more files to the analysis context.
build_runner does have the [Resource](https://pub.dev/documentation/build/latest/build/Resource-class.html) API, which can be used to cache things across build steps. But, I don't think it would work well to cache analyzer things using that, because they tend to get invalidated often (which can make them unusable). The resolved AST might not do this but I really don't know. It is better to cache some result which was derived from the analyzer result.

But in general, yes it is known that going from Element model => AST node causes a full re-parse, AST nodes are not cached. You could instead ask for the resolved AST of a library once, and then work from that, instead of the element model?

ultimately, @scheglov is probably the best one to weigh in there.

Fix patch:
diff --git a/benchmarks/lib/src/copy_with.freezed.dart b/benchmarks/lib/src/copy_with.freezed.dart
index 8f1b5a9f..e593549c 100644
--- a/benchmarks/lib/src/copy_with.freezed.dart
+++ b/benchmarks/lib/src/copy_with.freezed.dart
@@ -1,7 +1,7 @@
 // coverage:ignore-file
 // GENERATED CODE - DO NOT MODIFY BY HAND
 // ignore_for_file: type=lint
-// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
 
 part of 'copy_with.dart';
 
@@ -55,18 +55,21 @@ class _$ModelCopyWithImpl<$Res, $Val extends Model>
 }
 
 /// @nodoc
-abstract class _$$_ModelCopyWith<$Res> implements $ModelCopyWith<$Res> {
-  factory _$$_ModelCopyWith(_$_Model value, $Res Function(_$_Model) then) =
-      __$$_ModelCopyWithImpl<$Res>;
+abstract class _$$ModelImplCopyWith<$Res> implements $ModelCopyWith<$Res> {
+  factory _$$ModelImplCopyWith(
+          _$ModelImpl value, $Res Function(_$ModelImpl) then) =
+      __$$ModelImplCopyWithImpl<$Res>;
   @override
   @useResult
   $Res call({int counter});
 }
 
 /// @nodoc
-class __$$_ModelCopyWithImpl<$Res> extends _$ModelCopyWithImpl<$Res, _$_Model>
-    implements _$$_ModelCopyWith<$Res> {
-  __$$_ModelCopyWithImpl(_$_Model _value, $Res Function(_$_Model) _then)
+class __$$ModelImplCopyWithImpl<$Res>
+    extends _$ModelCopyWithImpl<$Res, _$ModelImpl>
+    implements _$$ModelImplCopyWith<$Res> {
+  __$$ModelImplCopyWithImpl(
+      _$ModelImpl _value, $Res Function(_$ModelImpl) _then)
       : super(_value, _then);
 
   @pragma('vm:prefer-inline')
@@ -74,7 +77,7 @@ class __$$_ModelCopyWithImpl<$Res> extends _$ModelCopyWithImpl<$Res, _$_Model>
   $Res call({
     Object? counter = null,
   }) {
-    return _then(_$_Model(
+    return _then(_$ModelImpl(
       counter: null == counter
           ? _value.counter
           : counter // ignore: cast_nullable_to_non_nullable
@@ -85,44 +88,43 @@ class __$$_ModelCopyWithImpl<$Res> extends _$ModelCopyWithImpl<$Res, _$_Model>
 
 /// @nodoc
 
-class _$_Model implements _Model {
-  _$_Model({required this.counter});
+class _$ModelImpl implements _Model {
+  _$ModelImpl({required this.counter});
 
   @override
   final int counter;
 
   @override
   String toString() {
-    return 'Model(counter: $counter)';
+    return 'Model.Model(counter: $counter)';
   }
 
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     return identical(this, other) ||
         (other.runtimeType == runtimeType &&
-            other is _$_Model &&
-            const DeepCollectionEquality().equals(other.counter, counter));
+            other is _$ModelImpl &&
+            (identical(other.counter, counter) || other.counter == counter));
   }
 
   @override
-  int get hashCode =>
-      Object.hash(runtimeType, const DeepCollectionEquality().hash(counter));
+  int get hashCode => Object.hash(runtimeType, counter);
 
   @JsonKey(ignore: true)
   @override
   @pragma('vm:prefer-inline')
-  _$$_ModelCopyWith<_$_Model> get copyWith =>
-      __$$_ModelCopyWithImpl<_$_Model>(this, _$identity);
+  _$$ModelImplCopyWith<_$ModelImpl> get copyWith =>
+      __$$ModelImplCopyWithImpl<_$ModelImpl>(this, _$identity);
 }
 
 abstract class _Model implements Model {
-  factory _Model({required final int counter}) = _$_Model;
+  factory _Model({required final int counter}) = _$ModelImpl;
 
   @override
   int get counter;
   @override
   @JsonKey(ignore: true)
-  _$$_ModelCopyWith<_$_Model> get copyWith =>
+  _$$ModelImplCopyWith<_$ModelImpl> get copyWith =>
       throw _privateConstructorUsedError;
 }
 
@@ -179,11 +181,11 @@ class _$ModelWrapperCopyWithImpl<$Res, $Val extends ModelWrapper>
 }
 
 /// @nodoc
-abstract class _$$_ModelWrapperCopyWith<$Res>
+abstract class _$$ModelWrapperImplCopyWith<$Res>
     implements $ModelWrapperCopyWith<$Res> {
-  factory _$$_ModelWrapperCopyWith(
-          _$_ModelWrapper value, $Res Function(_$_ModelWrapper) then) =
-      __$$_ModelWrapperCopyWithImpl<$Res>;
+  factory _$$ModelWrapperImplCopyWith(
+          _$ModelWrapperImpl value, $Res Function(_$ModelWrapperImpl) then) =
+      __$$ModelWrapperImplCopyWithImpl<$Res>;
   @override
   @useResult
   $Res call({Model model});
@@ -193,11 +195,11 @@ abstract class _$$_ModelWrapperCopyWith<$Res>
 }
 
 /// @nodoc
-class __$$_ModelWrapperCopyWithImpl<$Res>
-    extends _$ModelWrapperCopyWithImpl<$Res, _$_ModelWrapper>
-    implements _$$_ModelWrapperCopyWith<$Res> {
-  __$$_ModelWrapperCopyWithImpl(
-      _$_ModelWrapper _value, $Res Function(_$_ModelWrapper) _then)
+class __$$ModelWrapperImplCopyWithImpl<$Res>
+    extends _$ModelWrapperCopyWithImpl<$Res, _$ModelWrapperImpl>
+    implements _$$ModelWrapperImplCopyWith<$Res> {
+  __$$ModelWrapperImplCopyWithImpl(
+      _$ModelWrapperImpl _value, $Res Function(_$ModelWrapperImpl) _then)
       : super(_value, _then);
 
   @pragma('vm:prefer-inline')
@@ -205,7 +207,7 @@ class __$$_ModelWrapperCopyWithImpl<$Res>
   $Res call({
     Object? model = null,
   }) {
-    return _then(_$_ModelWrapper(
+    return _then(_$ModelWrapperImpl(
       model: null == model
           ? _value.model
           : model // ignore: cast_nullable_to_non_nullable
@@ -216,43 +218,42 @@ class __$$_ModelWrapperCopyWithImpl<$Res>
 
 /// @nodoc
 
-class _$_ModelWrapper implements _ModelWrapper {
-  _$_ModelWrapper({required this.model});
+class _$ModelWrapperImpl implements _ModelWrapper {
+  _$ModelWrapperImpl({required this.model});
 
   @override
   final Model model;
 
   @override
   String toString() {
-    return 'ModelWrapper(model: $model)';
+    return 'ModelWrapper.ModelWrapper(model: $model)';
   }
 
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     return identical(this, other) ||
         (other.runtimeType == runtimeType &&
-            other is _$_ModelWrapper &&
-            const DeepCollectionEquality().equals(other.model, model));
+            other is _$ModelWrapperImpl &&
+            (identical(other.model, model) || other.model == model));
   }
 
   @override
-  int get hashCode =>
-      Object.hash(runtimeType, const DeepCollectionEquality().hash(model));
+  int get hashCode => Object.hash(runtimeType, model);
 
   @JsonKey(ignore: true)
   @override
   @pragma('vm:prefer-inline')
-  _$$_ModelWrapperCopyWith<_$_ModelWrapper> get copyWith =>
-      __$$_ModelWrapperCopyWithImpl<_$_ModelWrapper>(this, _$identity);
+  _$$ModelWrapperImplCopyWith<_$ModelWrapperImpl> get copyWith =>
+      __$$ModelWrapperImplCopyWithImpl<_$ModelWrapperImpl>(this, _$identity);
 }
 
 abstract class _ModelWrapper implements ModelWrapper {
-  factory _ModelWrapper({required final Model model}) = _$_ModelWrapper;
+  factory _ModelWrapper({required final Model model}) = _$ModelWrapperImpl;
 
   @override
   Model get model;
   @override
   @JsonKey(ignore: true)
-  _$$_ModelWrapperCopyWith<_$_ModelWrapper> get copyWith =>
+  _$$ModelWrapperImplCopyWith<_$ModelWrapperImpl> get copyWith =>
       throw _privateConstructorUsedError;
 }
diff --git a/benchmarks/lib/src/equal.freezed.dart b/benchmarks/lib/src/equal.freezed.dart
index f29bc219..3f5284fd 100644
--- a/benchmarks/lib/src/equal.freezed.dart
+++ b/benchmarks/lib/src/equal.freezed.dart
@@ -1,7 +1,7 @@
 // coverage:ignore-file
 // GENERATED CODE - DO NOT MODIFY BY HAND
 // ignore_for_file: type=lint
-// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
 
 part of 'equal.dart';
 
@@ -63,22 +63,22 @@ class _$ModelWithListCopyWithImpl<$Res, $Val extends ModelWithList>
 }
 
 /// @nodoc
-abstract class _$$_ModelWithListCopyWith<$Res>
+abstract class _$$ModelWithListImplCopyWith<$Res>
     implements $ModelWithListCopyWith<$Res> {
-  factory _$$_ModelWithListCopyWith(
-          _$_ModelWithList value, $Res Function(_$_ModelWithList) then) =
-      __$$_ModelWithListCopyWithImpl<$Res>;
+  factory _$$ModelWithListImplCopyWith(
+          _$ModelWithListImpl value, $Res Function(_$ModelWithListImpl) then) =
+      __$$ModelWithListImplCopyWithImpl<$Res>;
   @override
   @useResult
   $Res call({List<int> someList, int counter});
 }
 
 /// @nodoc
-class __$$_ModelWithListCopyWithImpl<$Res>
-    extends _$ModelWithListCopyWithImpl<$Res, _$_ModelWithList>
-    implements _$$_ModelWithListCopyWith<$Res> {
-  __$$_ModelWithListCopyWithImpl(
-      _$_ModelWithList _value, $Res Function(_$_ModelWithList) _then)
+class __$$ModelWithListImplCopyWithImpl<$Res>
+    extends _$ModelWithListCopyWithImpl<$Res, _$ModelWithListImpl>
+    implements _$$ModelWithListImplCopyWith<$Res> {
+  __$$ModelWithListImplCopyWithImpl(
+      _$ModelWithListImpl _value, $Res Function(_$ModelWithListImpl) _then)
       : super(_value, _then);
 
   @pragma('vm:prefer-inline')
@@ -87,7 +87,7 @@ class __$$_ModelWithListCopyWithImpl<$Res>
     Object? someList = null,
     Object? counter = null,
   }) {
-    return _then(_$_ModelWithList(
+    return _then(_$ModelWithListImpl(
       someList: null == someList
           ? _value._someList
           : someList // ignore: cast_nullable_to_non_nullable
@@ -102,14 +102,16 @@ class __$$_ModelWithListCopyWithImpl<$Res>
 
 /// @nodoc
 
-class _$_ModelWithList implements _ModelWithList {
-  _$_ModelWithList({final List<int> someList = const <int>[], this.counter = 0})
+class _$ModelWithListImpl implements _ModelWithList {
+  _$ModelWithListImpl(
+      {final List<int> someList = const <int>[], this.counter = 0})
       : _someList = someList;
 
   final List<int> _someList;
   @override
   @JsonKey()
   List<int> get someList {
+    if (_someList is EqualUnmodifiableListView) return _someList;
     // ignore: implicit_dynamic_type
     return EqualUnmodifiableListView(_someList);
   }
@@ -120,34 +122,32 @@ class _$_ModelWithList implements _ModelWithList {
 
   @override
   String toString() {
-    return 'ModelWithList(someList: $someList, counter: $counter)';
+    return 'ModelWithList.ModelWithList(someList: $someList, counter: $counter)';
   }
 
   @override
-  bool operator ==(dynamic other) {
+  bool operator ==(Object other) {
     return identical(this, other) ||
         (other.runtimeType == runtimeType &&
-            other is _$_ModelWithList &&
+            other is _$ModelWithListImpl &&
             const DeepCollectionEquality().equals(other._someList, _someList) &&
-            const DeepCollectionEquality().equals(other.counter, counter));
+            (identical(other.counter, counter) || other.counter == counter));
   }
 
   @override
   int get hashCode => Object.hash(
-      runtimeType,
-      const DeepCollectionEquality().hash(_someList),
-      const DeepCollectionEquality().hash(counter));
+      runtimeType, const DeepCollectionEquality().hash(_someList), counter);
 
   @JsonKey(ignore: true)
   @override
   @pragma('vm:prefer-inline')
-  _$$_ModelWithListCopyWith<_$_ModelWithList> get copyWith =>
-      __$$_ModelWithListCopyWithImpl<_$_ModelWithList>(this, _$identity);
+  _$$ModelWithListImplCopyWith<_$ModelWithListImpl> get copyWith =>
+      __$$ModelWithListImplCopyWithImpl<_$ModelWithListImpl>(this, _$identity);
 }
 
 abstract class _ModelWithList implements ModelWithList {
   factory _ModelWithList({final List<int> someList, final int counter}) =
-      _$_ModelWithList;
+      _$ModelWithListImpl;
 
   @override
   List<int> get someList;
@@ -155,6 +155,6 @@ abstract class _ModelWithList implements ModelWithList {
   int get counter;
   @override
   @JsonKey(ignore: true)
-  _$$_ModelWithListCopyWith<_$_ModelWithList> get copyWith =>
+  _$$ModelWithListImplCopyWith<_$ModelWithListImpl> get copyWith =>
       throw _privateConstructorUsedError;
 }
diff --git a/packages/freezed/lib/src/ast.dart b/packages/freezed/lib/src/ast.dart
new file mode 100644
index 00000000..01443194
--- /dev/null
+++ b/packages/freezed/lib/src/ast.dart
@@ -0,0 +1,88 @@
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+
+extension AstX on AstNode {
+  String get documentation {
+    final builder = StringBuffer();
+
+    for (Token? token = beginToken.precedingComments;
+        token != null;
+        token = token.next) {
+      builder.writeln(token);
+    }
+
+    return builder.toString();
+  }
+}
+
+extension ClassX on ClassDeclaration {
+  bool get hasCustomToString {
+    final element = declaredElement!;
+
+    for (final type in [
+      element,
+      ...element.allSupertypes
+          .where((e) => !e.isDartCoreObject)
+          .map((e) => e.element)
+    ]) {
+      for (final method in type.methods) {
+        if (method.name == 'toString') {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  bool get hasCustomEquals {
+    final element = declaredElement!;
+
+    for (final type in [
+      element,
+      ...element.allSupertypes
+          .where((e) => !e.isDartCoreObject)
+          .map((e) => e.element)
+    ]) {
+      for (final method in type.methods.where((e) => e.isOperator)) {
+        if (method.name == '==') {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
+
+extension ConstructorX on ConstructorDeclaration {
+  String get fullName {
+    final classElement = declaredElement!.enclosingElement;
+
+    var generics =
+        classElement.typeParameters.map((e) => '\$${e.name}').join(', ');
+    if (generics.isNotEmpty) {
+      generics = '<$generics>';
+    }
+
+    final className = classElement.enclosingElement.name;
+
+    return name == null ? '$className$generics' : '$className$generics.$name';
+  }
+
+  String get escapedName {
+    final classElement = declaredElement!.enclosingElement;
+
+    var generics =
+        classElement.typeParameters.map((e) => '\$${e.name}').join(', ');
+    if (generics.isNotEmpty) {
+      generics = '<$generics>';
+    }
+
+    final escapedElementName = classElement.name.replaceAll(r'$', r'\$');
+    final escapedConstructorName = name?.lexeme.replaceAll(r'$', r'\$');
+
+    return escapedConstructorName == null
+        ? '$escapedElementName$generics'
+        : '$escapedElementName$generics.$escapedConstructorName';
+  }
+}
diff --git a/packages/freezed/lib/src/freezed_generator.dart b/packages/freezed/lib/src/freezed_generator.dart
index 9cc18de2..0f1faec1 100644
--- a/packages/freezed/lib/src/freezed_generator.dart
+++ b/packages/freezed/lib/src/freezed_generator.dart
@@ -1,17 +1,18 @@
-import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:build/build.dart';
 import 'package:collection/collection.dart';
+import 'package:freezed/src/ast.dart';
+import 'package:freezed/src/string.dart';
 import 'package:freezed/src/templates/assert.dart';
 import 'package:freezed/src/templates/copy_with.dart';
 import 'package:freezed/src/templates/parameter_template.dart';
 import 'package:freezed/src/templates/properties.dart';
 import 'package:freezed/src/templates/prototypes.dart';
 import 'package:freezed/src/tools/type.dart';
-import 'package:freezed/src/utils.dart';
 import 'package:freezed_annotation/freezed_annotation.dart'
     show
         Assert,
@@ -58,6 +59,12 @@ class CommonProperties {
   final List<Property> cloneableProperties = [];
 }
 
+extension on ClassDeclaration {
+  Iterable<ConstructorDeclaration> get constructors {
+    return members.whereType<ConstructorDeclaration>();
+  }
+}
+
 @immutable
 class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
   FreezedGenerator(this._buildYamlConfigs);
@@ -65,31 +72,32 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
   final Freezed _buildYamlConfigs;
 
   @override
-  Future<Data> parseElement(
+  Future<Data> parseDeclaration(
     BuildStep buildStep,
     GlobalData globalData,
-    Element element,
+    Declaration declaration,
+    DartObject annotation,
   ) async {
-    if (element is! ClassElement) {
+    if (declaration is! ClassDeclaration) {
       throw InvalidGenerationSourceError(
-        '@freezed can only be applied on classes. Failing element: ${element.name}',
-        element: element,
+        '@freezed can only be applied on classes.',
+        element: declaration.declaredElement,
       );
     }
 
-    final shouldUseExtends = element.constructors.any((ctor) {
-      return ctor.name == '_' && !ctor.isFactory && !ctor.isAbstract;
+    final shouldUseExtends = declaration.constructors.any((ctor) {
+      return ctor.name?.lexeme == '_' && ctor.factoryKeyword == null;
     });
 
-    final configs = _parseConfig(element);
+    final configs = _parseConfig(annotation);
 
-    for (final field in element.fields) {
+    for (final field in declaration.declaredElement!.fields) {
       _assertValidFieldUsage(field, shouldUseExtends: shouldUseExtends);
     }
 
     final constructorsNeedsGeneration = await _parseConstructorsNeedsGeneration(
       buildStep,
-      element,
+      declaration,
       configs,
     );
 
@@ -102,17 +110,17 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
     late final needsJsonSerializable = _needsJsonSerializable(
       buildStep,
       globalData,
-      element,
+      declaration,
     );
 
     return Data(
-      name: element.name,
+      name: declaration.name.lexeme,
       shouldUseExtends: shouldUseExtends,
       unionKey: configs.unionKey!,
       generateToString:
-          configs.toStringOverride ?? !_hasCustomToString(element),
+          configs.toStringOverride ?? !declaration.hasCustomToString,
       generateCopyWith: configs.copyWith!,
-      generateEqual: configs.equal ?? !_hasCustomEquals(element),
+      generateEqual: configs.equal ?? !declaration.hasCustomEquals,
       generateFromJson: configs.fromJson ?? await needsJsonSerializable,
       generateToJson: configs.toJson ?? await needsJsonSerializable,
       genericArgumentFactories: configs.genericArgumentFactories,
@@ -129,45 +137,51 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
       makeCollectionsImmutable: configs.makeCollectionsUnmodifiable!,
       constructors: constructorsNeedsGeneration,
       concretePropertiesName: [
-        for (final p in element.fields)
+        for (final p in declaration.declaredElement!.fields)
           if (!p.isStatic) p.name,
       ],
-      genericsDefinitionTemplate:
-          GenericsDefinitionTemplate.fromGenericElement(element.typeParameters),
-      genericsParameterTemplate:
-          GenericsParameterTemplate.fromGenericElement(element.typeParameters),
+      genericsDefinitionTemplate: GenericsDefinitionTemplate.fromGenericElement(
+        declaration.declaredElement!.typeParameters,
+      ),
+      genericsParameterTemplate: GenericsParameterTemplate.fromGenericElement(
+        declaration.declaredElement!.typeParameters,
+      ),
     );
   }
 
   void _assertValidNormalConstructorUsage(
-    ConstructorElement constructor, {
+    ConstructorDeclaration constructor, {
     required String className,
   }) {
-    if (!constructor.isFactory &&
-        (constructor.name != '_' || constructor.parameters.isNotEmpty)) {
+    if (constructor.factoryKeyword == null &&
+        (constructor.name?.lexeme != '_' ||
+            constructor.parameters.parameters.isNotEmpty)) {
       throw InvalidGenerationSourceError(
         'Classes decorated with @freezed can only have a single non-factory'
         ', without parameters, and named MyClass._()',
-        element: constructor,
+        element: constructor.declaredElement,
       );
     }
   }
 
   void _assertValidFreezedConstructorUsage(
-    ConstructorElement constructor, {
+    ConstructorDeclaration constructor, {
     required String className,
   }) {
-    for (final parameter in constructor.parameters) {
-      if (parameter.type.nullabilitySuffix != NullabilitySuffix.question &&
-          parameter.isOptional &&
-          parameter.defaultValue == null &&
-          !parameter.type.isDynamic2) {
-        final ctorName =
-            constructor.isDefaultConstructor ? '' : '.${constructor.name}';
+    for (final parameter in constructor.parameters.parameters) {
+      final parameterElement = parameter.declaredElement;
+      if (parameterElement == null) continue;
+
+      if (parameterElement.type.nullabilitySuffix !=
+              NullabilitySuffix.question &&
+          parameterElement.isOptional &&
+          parameterElement.defaultValue == null &&
+          !parameterElement.type.isDynamic2) {
+        final ctorName = constructor.name == null ? '' : '.${constructor.name}';
 
         throw InvalidGenerationSourceError(
           'The parameter `${parameter.name}` of `$className$ctorName` is non-nullable but is neither required nor marked with @Default',
-          element: parameter,
+          element: parameter.declaredElement,
         );
       }
     }
@@ -209,14 +223,14 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
   Future<bool> _needsJsonSerializable(
     BuildStep buildStep,
     GlobalData globalData,
-    ClassElement element,
+    ClassDeclaration declaration,
   ) async {
     if (!globalData.hasJson) return false;
 
-    for (final constructor in element.constructors) {
-      if (constructor.isFactory && constructor.name == 'fromJson') {
-        final ast = await tryGetAstNodeForElement(constructor, buildStep);
-        return ast.endToken.stringValue == ';';
+    for (final constructor in declaration.constructors) {
+      if (constructor.factoryKeyword != null &&
+          constructor.name?.lexeme == 'fromJson') {
+        return constructor.body is ExpressionFunctionBody;
       }
     }
 
@@ -350,43 +364,49 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
 
   Future<List<ConstructorDetails>> _parseConstructorsNeedsGeneration(
     BuildStep buildStep,
-    ClassElement element,
+    ClassDeclaration declaration,
     Freezed options,
   ) async {
     final result = <ConstructorDetails>[];
-    for (final constructor in element.constructors) {
-      if (!constructor.isFactory || constructor.name == 'fromJson') {
+    for (final constructor in declaration.constructors) {
+      if (constructor.factoryKeyword == null ||
+          constructor.name?.lexeme == 'fromJson') {
         _assertValidNormalConstructorUsage(
           constructor,
-          className: element.name,
+          className: declaration.name.lexeme,
         );
         continue;
       }
 
       final redirectedName =
-          await _getConstructorRedirectedName(constructor, buildStep);
+          constructor.redirectedConstructor?.type.name2.lexeme;
 
       if (redirectedName == null) {
         _assertValidNormalConstructorUsage(
           constructor,
-          className: element.name,
+          className: declaration.name.lexeme,
         );
         continue;
       }
 
-      _assertValidFreezedConstructorUsage(constructor, className: element.name);
+      _assertValidFreezedConstructorUsage(
+        constructor,
+        className: declaration.name.lexeme,
+      );
 
       result.add(
         ConstructorDetails(
           asserts: _parseAsserts(constructor).toList(),
-          name: constructor.name,
-          unionValue: constructor.unionValue(options.unionValueCase),
-          isConst: constructor.isConst,
-          fullName: _fullName(element, constructor),
-          escapedName: _escapedName(element, constructor),
+          name: constructor.name?.lexeme ?? '',
+          unionValue: constructor.declaredElement!.unionValue(
+            options.unionValueCase,
+          ),
+          isConst: constructor.constKeyword != null,
+          fullName: constructor.fullName,
+          escapedName: constructor.escapedName,
           impliedProperties: [
-            for (final parameter in constructor.parameters)
-              await Property.fromParameterElement(
+            for (final parameter in constructor.parameters.parameters)
+              await Property.fromFormalParameter(
                 parameter,
                 buildStep,
                 addImplicitFinal: options.addImplicitFinal,
@@ -406,18 +426,23 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
               })
               .map((e) => e.toSource())
               .toList(),
-          withDecorators: _withDecorationTypes(constructor).toSet().toList(),
+          withDecorators: _withDecorationTypes(constructor.declaredElement!)
+              .toSet()
+              .toList(),
           implementsDecorators:
-              _implementsDecorationTypes(constructor).toSet().toList(),
-          isDefault: isDefaultConstructor(constructor),
-          hasJsonSerializable: constructor.hasJsonSerializable,
-          isFallback: constructor.isFallbackUnion(options.fallbackUnion),
+              _implementsDecorationTypes(constructor.declaredElement!)
+                  .toSet()
+                  .toList(),
+          isDefault: isDefaultConstructor(constructor.declaredElement!),
+          hasJsonSerializable: constructor.declaredElement!.hasJsonSerializable,
+          isFallback: constructor.declaredElement!
+              .isFallbackUnion(options.fallbackUnion),
           cloneableProperties: _cloneableProperties(
             buildStep,
-            element,
-            constructor,
+            declaration.declaredElement!,
+            constructor.declaredElement!,
           ).toList(),
-          parameters: await ParametersTemplate.fromParameterElements(
+          parameters: await ParametersTemplate.fromParameterList(
             buildStep,
             constructor.parameters,
             addImplicitFinal: options.addImplicitFinal,
@@ -429,22 +454,22 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
 
     if (result.isEmpty) {
       throw InvalidGenerationSourceError(
-        'Marked ${element.name} with @freezed, but freezed has nothing to generate',
-        element: element,
+        'Marked ${declaration.name} with @freezed, but freezed has nothing to generate',
+        element: declaration.declaredElement,
       );
     }
 
     if (result.length > 1 && result.any((c) => c.name.startsWith('_'))) {
       throw InvalidGenerationSourceError(
         'A freezed union cannot have private constructors',
-        element: element,
+        element: declaration.declaredElement,
       );
     }
 
     if (options.fallbackUnion != null && result.none((c) => c.isFallback)) {
       throw InvalidGenerationSourceError(
         'Fallback union was specified but no ${options.fallbackUnion} constructor exists.',
-        element: element,
+        element: declaration.declaredElement,
       );
     }
 
@@ -502,12 +527,15 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
       final typeElement = parameterType.element;
       if (typeElement is! ClassElement) continue;
 
-      final freezedAnnotation = typeChecker.firstAnnotationOf(typeElement);
+      final freezedAnnotation = typeChecker.firstAnnotationOf(
+        typeElement,
+        throwOnUnresolved: false,
+      );
 
       /// Handles classes annotated with Freezed
       if (freezedAnnotation == null) continue;
 
-      final configs = _parseConfig(typeElement);
+      final configs = _parseConfig(freezedAnnotation);
       // copyWith not enabled, so the property is not cloneable
       if (configs.copyWith != true) continue;
 
@@ -546,10 +574,7 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
     }
   }
 
-  Freezed _parseConfig(Element element) {
-    final annotation = const TypeChecker.fromRuntime(Freezed)
-        .firstAnnotationOf(element, throwOnUnresolved: false)!;
-
+  Freezed _parseConfig(DartObject annotation) {
     return Freezed(
       copyWith: annotation.decodeField(
         'copyWith',
@@ -658,9 +683,12 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
     );
   }
 
-  Iterable<AssertTemplate> _parseAsserts(ConstructorElement constructor) sync* {
-    for (final meta
-        in const TypeChecker.fromRuntime(Assert).annotationsOf(constructor)) {
+  Iterable<AssertTemplate> _parseAsserts(
+    ConstructorDeclaration constructor,
+  ) sync* {
+    for (final meta in const TypeChecker.fromRuntime(Assert).annotationsOf(
+      constructor.declaredElement!,
+    )) {
       yield AssertTemplate(
         eval: meta.getField('eval')!.toStringValue(),
         message: meta.getField('message')!.toStringValue(),
@@ -737,119 +765,14 @@ class FreezedGenerator extends ParserGenerator<GlobalData, Data, Freezed> {
   }
 
   @override
-  GlobalData parseGlobalData(LibraryElement library) {
+  GlobalData parseGlobalData(CompilationUnit unit) {
+    final library = unit.declaredElement!.library;
+
     return GlobalData(
       hasJson: library.importsJsonSerializable,
       hasDiagnostics: library.importsDiagnosticable,
     );
   }
-
-  bool _hasCustomToString(ClassElement element) {
-    for (final type in [
-      element,
-      ...element.allSupertypes
-          .where((e) => !e.isDartCoreObject)
-          .map((e) => e.element)
-    ]) {
-      for (final method in type.methods) {
-        if (method.name == 'toString') {
-          return true;
-        }
-      }
-    }
-
-    return false;
-  }
-
-  bool _hasCustomEquals(ClassElement element) {
-    for (final type in [
-      element,
-      ...element.allSupertypes
-          .where((e) => !e.isDartCoreObject)
-          .map((e) => e.element)
-    ]) {
-      for (final method in type.methods.where((e) => e.isOperator)) {
-        if (method.name == '==') {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
-  String _fullName(ClassElement element, ConstructorElement constructor) {
-    var generics = element.typeParameters.map((e) {
-      return '\$${e.name}';
-    }).join(', ');
-    if (generics.isNotEmpty) {
-      generics = '<$generics>';
-    }
-
-    return constructor.name.isEmpty
-        ? '${element.name}$generics'
-        : '${element.name}$generics.${constructor.name}';
-  }
-
-  String _escapedName(ClassElement element, ConstructorElement constructor) {
-    var generics = element.typeParameters.map((e) {
-      return '\$${e.name}';
-    }).join(', ');
-    if (generics.isNotEmpty) {
-      generics = '<$generics>';
-    }
-
-    final escapedElementName = element.name.replaceAll(r'$', r'\$');
-    final escapedConstructorName = constructor.name.replaceAll(r'$', r'\$');
-
-    return escapedConstructorName.isEmpty
-        ? '$escapedElementName$generics'
-        : '$escapedElementName$generics.$escapedConstructorName';
-  }
-
-  /// For:
-  ///
-  /// ```dart
-  /// Constructor.named() = _Redirection<T>;
-  /// ```
-  ///
-  /// returns `_Redirection`;
-  Future<String?> _getConstructorRedirectedName(
-    ConstructorElement constructor,
-    BuildStep buildStep,
-  ) async {
-    final ast = await tryGetAstNodeForElement(constructor, buildStep);
-
-    if (ast.endToken.stringValue != ';') return null;
-
-    Token? equalToken = ast.endToken;
-    while (true) {
-      if (equalToken == null ||
-          equalToken.charOffset < constructor.nameOffset) {
-        return null;
-      }
-      if (equalToken.stringValue == '=>') return null;
-      if (equalToken.stringValue == '=') {
-        break;
-      }
-
-      equalToken = equalToken.previous;
-    }
-
-    var genericOrEndToken = equalToken;
-    while (genericOrEndToken.stringValue != '<' &&
-        genericOrEndToken.stringValue != ';') {
-      genericOrEndToken = genericOrEndToken.next!;
-    }
-
-    final source = constructor.source.contents.data;
-    final redirectedName = source
-        .substring(equalToken.charOffset + 1, genericOrEndToken.charOffset)
-        .trim();
-
-    if (redirectedName.isEmpty) return null;
-
-    return redirectedName;
-  }
 }
 
 extension on LibraryElement {
@@ -896,13 +819,13 @@ extension on ConstructorElement {
       case FreezedUnionCase.none:
         return constructorName;
       case FreezedUnionCase.kebab:
-        return kebabCase(constructorName);
+        return constructorName.kebabCase;
       case FreezedUnionCase.pascal:
-        return pascalCase(constructorName);
+        return constructorName.pascalCase;
       case FreezedUnionCase.snake:
-        return snakeCase(constructorName);
+        return constructorName.snakeCase;
       case FreezedUnionCase.screamingSnake:
-        return screamingSnake(constructorName);
+        return constructorName.screamingSnake;
     }
   }
 }
diff --git a/packages/freezed/lib/src/parse_generator.dart b/packages/freezed/lib/src/parse_generator.dart
index 37529e0e..c10b2ca6 100644
--- a/packages/freezed/lib/src/parse_generator.dart
+++ b/packages/freezed/lib/src/parse_generator.dart
@@ -1,7 +1,12 @@
 import 'dart:async';
 
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:build/build.dart';
+import 'package:collection/collection.dart';
 import 'package:source_gen/source_gen.dart';
 
 abstract class ParserGenerator<GlobalData, Data, Annotation>
@@ -11,34 +16,52 @@ abstract class ParserGenerator<GlobalData, Data, Annotation>
     LibraryReader oldLibrary,
     BuildStep buildStep,
   ) async {
-    final assetId =
-        await buildStep.resolver.assetIdForElement(oldLibrary.element);
-    if (await buildStep.resolver.isLibrary(assetId).then((value) => !value)) {
-      return '';
+    late SomeResolvedUnitResult unit;
+    for (var i = 0; i < 10; i++) {
+      try {
+        unit = await oldLibrary.element.session.getResolvedUnit(
+          oldLibrary.element.source.fullName,
+        );
+        break;
+      } on InconsistentAnalysisException {
+        // retry
+      }
     }
 
-    final library = await buildStep.resolver.libraryFor(assetId);
+    if (unit is! ResolvedUnitResult) return '';
 
     final values = StringBuffer();
-
-    final globalData = parseGlobalData(library);
+    final globalData = parseGlobalData(unit.unit);
 
     var hasGeneratedGlobalCode = false;
 
-    for (var element in library.topLevelElements.where(
-        (e) => typeChecker.hasAnnotationOf(e, throwOnUnresolved: false))) {
+    for (var declaration in unit.unit.declarations) {
+      final declaredElement = declaration.declaredElement;
+      if (declaredElement == null) continue;
+
+      final annotation = typeChecker.firstAnnotationOf(
+        declaredElement,
+        throwOnUnresolved: false,
+      );
+
+      if (annotation == null) continue;
+
       if (!hasGeneratedGlobalCode) {
         hasGeneratedGlobalCode = true;
-        for (final value
-            in generateForAll(globalData).map((e) => e.toString())) {
+        for (final value in generateForAll(globalData)) {
           values.writeln(value);
         }
       }
 
-      final data = await parseElement(buildStep, globalData, element);
+      final data = await parseDeclaration(
+        buildStep,
+        globalData,
+        declaration,
+        annotation,
+      );
       if (data == null) continue;
-      for (final value
-          in generateForData(globalData, data).map((e) => e.toString())) {
+
+      for (final value in generateForData(globalData, data)) {
         values.writeln(value);
       }
     }
@@ -48,12 +71,13 @@ abstract class ParserGenerator<GlobalData, Data, Annotation>
 
   Iterable<Object> generateForAll(GlobalData globalData) sync* {}
 
-  GlobalData parseGlobalData(LibraryElement library);
+  GlobalData parseGlobalData(CompilationUnit unit);
 
-  FutureOr<Data> parseElement(
+  FutureOr<Data> parseDeclaration(
     BuildStep buildStep,
     GlobalData globalData,
-    Element element,
+    Declaration declaration,
+    DartObject annotation,
   );
 
   Iterable<Object> generateForData(GlobalData globalData, Data data);
@@ -64,9 +88,32 @@ abstract class ParserGenerator<GlobalData, Data, Annotation>
     ConstantReader annotation,
     BuildStep buildStep,
   ) async* {
+    final annotation = typeChecker.firstAnnotationOf(
+      element,
+      throwOnUnresolved: false,
+    );
+    if (annotation == null) return;
+
+    final unit = await element.session!.getResolvedUnit(
+      element.source!.fullName,
+    );
+    unit as ResolvedUnitResult;
+    final Object? ast = unit.unit.declarations.firstWhereOrNull(
+      (declaration) => declaration.declaredElement?.name == element.name!,
+    );
+    if (ast == null) {
+      throw InvalidGenerationSourceError('Ast not found', element: element);
+    }
+    if (ast is! Declaration) {
+      throw InvalidGenerationSourceError(
+        'Ast is not a Declaration',
+        element: element,
+      );
+    }
+
     // implemented for source_gen_test – otherwise unused
-    final globalData = parseGlobalData(element.library!);
-    final data = parseElement(buildStep, globalData, element);
+    final globalData = parseGlobalData(unit.unit);
+    final data = parseDeclaration(buildStep, globalData, ast, annotation);
 
     if (data == null) return;
 
diff --git a/packages/freezed/lib/src/string.dart b/packages/freezed/lib/src/string.dart
new file mode 100644
index 00000000..e42002a9
--- /dev/null
+++ b/packages/freezed/lib/src/string.dart
@@ -0,0 +1,25 @@
+final _upperCase = RegExp('[A-Z]');
+
+extension StringX on String {
+  String get kebabCase => _fixCase('-');
+
+  String get snakeCase => _fixCase('_');
+
+  String get screamingSnake => snakeCase.toUpperCase();
+
+  String get pascalCase {
+    if (isEmpty) return '';
+
+    return this[0].toUpperCase() + substring(1);
+  }
+
+  String _fixCase(String separator) => replaceAllMapped(_upperCase, (match) {
+        var lower = match.group(0)!.toLowerCase();
+
+        if (match.start > 0) {
+          lower = '$separator$lower';
+        }
+
+        return lower;
+      });
+}
diff --git a/packages/freezed/lib/src/templates/parameter_template.dart b/packages/freezed/lib/src/templates/parameter_template.dart
index 18d94223..31dbb8cc 100644
--- a/packages/freezed/lib/src/templates/parameter_template.dart
+++ b/packages/freezed/lib/src/templates/parameter_template.dart
@@ -1,10 +1,11 @@
+import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:build/build.dart';
+import 'package:freezed/src/ast.dart';
 import 'package:freezed/src/templates/concrete_template.dart';
 import 'package:freezed/src/templates/properties.dart';
 import 'package:freezed/src/templates/prototypes.dart';
 import 'package:freezed/src/tools/type.dart';
-import 'package:freezed/src/utils.dart';
 
 class GenericsDefinitionTemplate {
   GenericsDefinitionTemplate(this.typeParameters);
@@ -61,13 +62,15 @@ class ParametersTemplate {
     this.namedParameters = const [],
   });
 
-  static Future<ParametersTemplate> fromParameterElements(
+  static Future<ParametersTemplate> fromParameterList(
     BuildStep buildStep,
-    List<ParameterElement> parameters, {
+    FormalParameterList parameters, {
     bool isAssignedToThis = false,
     required bool addImplicitFinal,
   }) async {
-    Future<Parameter> asParameter(ParameterElement e) async {
+    Future<Parameter> asParameter(FormalParameter p) async {
+      final e = p.declaredElement!;
+
       final value = Parameter(
         name: e.name,
         isNullable: e.type.isNullable,
@@ -79,7 +82,7 @@ class ParametersTemplate {
         isFinal: addImplicitFinal || e.isFinal,
         type: parseTypeSource(e),
         decorators: parseDecorators(e.metadata),
-        doc: await documentationOfParameter(e, buildStep),
+        doc: p.documentation,
         isPossiblyDartCollection: e.type.isPossiblyDartCollection,
         showDefaultValue: true,
         parameterElement: e,
@@ -91,13 +94,17 @@ class ParametersTemplate {
 
     return ParametersTemplate(
       await Future.wait(
-        parameters.where((p) => p.isRequiredPositional).map(asParameter),
+        parameters.parameters
+            .where((p) => p.isRequiredPositional)
+            .map(asParameter),
       ),
       optionalPositionalParameters: await Future.wait(
-        parameters.where((p) => p.isOptionalPositional).map(asParameter),
+        parameters.parameters
+            .where((p) => p.isOptionalPositional)
+            .map(asParameter),
       ),
       namedParameters: await Future.wait(
-        parameters.where((p) => p.isNamed).map(asParameter),
+        parameters.parameters.where((p) => p.isNamed).map(asParameter),
       ),
     );
   }
diff --git a/packages/freezed/lib/src/templates/properties.dart b/packages/freezed/lib/src/templates/properties.dart
index e68e8864..a925718a 100644
--- a/packages/freezed/lib/src/templates/properties.dart
+++ b/packages/freezed/lib/src/templates/properties.dart
@@ -1,11 +1,12 @@
+import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:build/build.dart';
+import 'package:freezed/src/ast.dart';
 import 'package:freezed/src/templates/parameter_template.dart';
 import 'package:freezed/src/tools/type.dart';
 import 'package:source_gen/source_gen.dart';
 
-import '../utils.dart';
 import 'concrete_template.dart';
 import 'prototypes.dart';
 
@@ -51,11 +52,13 @@ class Property {
           hasJsonKey: false,
         );
 
-  static Future<Property> fromParameterElement(
-    ParameterElement element,
+  static Future<Property> fromFormalParameter(
+    FormalParameter parameter,
     BuildStep buildStep, {
     required bool addImplicitFinal,
   }) async {
+    final element = parameter.declaredElement!;
+
     final defaultValue = element.defaultValue;
     if (defaultValue != null &&
         (element.hasRequired || element.isRequiredPositional)) {
@@ -72,7 +75,7 @@ class Property {
       isDartMap: element.type.isDartCoreMap,
       isDartSet: element.type.isDartCoreSet,
       isFinal: addImplicitFinal || element.isFinal,
-      doc: await documentationOfParameter(element, buildStep),
+      doc: parameter.documentation,
       type: parseTypeSource(element),
       decorators: parseDecorators(element.metadata),
       defaultValueSource: defaultValue,
diff --git a/packages/freezed/lib/src/utils.dart b/packages/freezed/lib/src/utils.dart
deleted file mode 100644
index 39687221..00000000
--- a/packages/freezed/lib/src/utils.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:build/build.dart';
-
-Future<String> documentationOfParameter(
-  ParameterElement parameter,
-  BuildStep buildStep,
-) async {
-  final builder = StringBuffer();
-
-  final astNode = await tryGetAstNodeForElement(parameter, buildStep);
-
-  for (Token? token = astNode.beginToken.precedingComments;
-      token != null;
-      token = token.next) {
-    builder.writeln(token);
-  }
-
-  return builder.toString();
-}
-
-Future<AstNode> tryGetAstNodeForElement(
-  Element element,
-  BuildStep buildStep,
-) async {
-  var library = element.library!;
-
-  while (true) {
-    try {
-      final result = library.session.getParsedLibraryByElement(library)
-          as ParsedLibraryResult?;
-
-      return result!.getElementDeclaration(element)!.node;
-    } on InconsistentAnalysisException {
-      final assetId =
-          await buildStep.resolver.assetIdForElement(element.library!);
-      if (await buildStep.resolver.isLibrary(assetId).then((value) => !value)) {
-        rethrow;
-      }
-      library = await buildStep.resolver.libraryFor(assetId);
-    }
-  }
-}
-
-final _upperCase = RegExp('[A-Z]');
-
-String kebabCase(String input) => _fixCase(input, '-');
-
-String snakeCase(String input) => _fixCase(input, '_');
-
-String screamingSnake(String input) => snakeCase(input).toUpperCase();
-
-String pascalCase(String input) {
-  if (input.isEmpty) {
-    return '';
-  }
-
-  return input[0].toUpperCase() + input.substring(1);
-}
-
-String _fixCase(String input, String separator) =>
-    input.replaceAllMapped(_upperCase, (match) {
-      var lower = match.group(0)!.toLowerCase();
-
-      if (match.start > 0) {
-        lower = '$separator$lower';
-      }
-
-      return lower;
-    });
diff --git a/packages/freezed/pubspec.yaml b/packages/freezed/pubspec.yaml
index f8c354e0..48f90cbd 100644
--- a/packages/freezed/pubspec.yaml
+++ b/packages/freezed/pubspec.yaml
@@ -15,7 +15,7 @@ dependencies:
   build_config: ^1.1.0
   collection: ^1.15.0
   meta: ^1.9.1
-  source_gen: ^1.2.3
+  source_gen: ^1.4.0
   freezed_annotation: ^2.4.1
   json_annotation: ^4.6.0
 
@@ -25,5 +25,5 @@ dev_dependencies:
   build_runner: ^2.3.3
   test: ^1.21.0
   matcher: ^0.12.14
-  source_gen_test: ^1.0.4
-  expect_error: ^1.0.4
+  source_gen_test: ^1.0.6
+  expect_error: ^1.0.5


Test patch:
diff --git a/packages/freezed/test/source_gen_src.dart b/packages/freezed/test/source_gen_src.dart
index c10cbca5..0369afa1 100644
--- a/packages/freezed/test/source_gen_src.dart
+++ b/packages/freezed/test/source_gen_src.dart
@@ -3,9 +3,9 @@ import 'package:freezed_annotation/freezed_annotation.dart';
 // ignore: import_of_legacy_library_into_null_safe
 import 'package:source_gen_test/annotations.dart';
 
-@ShouldThrow('@freezed can only be applied on classes. Failing element: foo')
+@ShouldThrow('@freezed can only be applied on classes.')
 @freezed
-Object? foo;
+enum Foo { a }
 
 @ShouldThrow(
   'Getters require a MyClass._() constructor',