[pigeon] Documentation comments (#2578)

diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 71f7162..6f7531c 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 4.1.0
+
+* Adds documentation comment support for all currently supported languages.
+
 ## 4.0.3
 
 * [swift] Makes swift output work on macOS.
diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart
index ade2287..6db4a5e 100644
--- a/packages/pigeon/lib/ast.dart
+++ b/packages/pigeon/lib/ast.dart
@@ -33,6 +33,7 @@
     this.offset,
     this.objcSelector = '',
     this.taskQueueType = TaskQueueType.serial,
+    this.documentationComments = const <String>[],
   });
 
   /// The name of the method.
@@ -56,11 +57,18 @@
   /// Specifies how handlers are dispatched with respect to threading.
   TaskQueueType taskQueueType;
 
+  /// List of documentation comments, seperated by line.
+  ///
+  /// Lines should not include the comment marker itself, but should include any
+  /// leading whitespace, so that any indentation in the original comment is preserved.
+  /// For example: [" List of documentation comments, separated by line.", ...]
+  List<String> documentationComments;
+
   @override
   String toString() {
     final String objcSelectorStr =
         objcSelector.isEmpty ? '' : ' objcSelector:$objcSelector';
-    return '(Method name:$name returnType:$returnType arguments:$arguments isAsynchronous:$isAsynchronous$objcSelectorStr)';
+    return '(Method name:$name returnType:$returnType arguments:$arguments isAsynchronous:$isAsynchronous$objcSelectorStr documentationComments:$documentationComments)';
   }
 }
 
@@ -72,6 +80,7 @@
     required this.location,
     required this.methods,
     this.dartHostTestHandler,
+    this.documentationComments = const <String>[],
   });
 
   /// The name of the API.
@@ -86,9 +95,16 @@
   /// The name of the Dart test interface to generate to help with testing.
   String? dartHostTestHandler;
 
+  /// List of documentation comments, seperated by line.
+  ///
+  /// Lines should not include the comment marker itself, but should include any
+  /// leading whitespace, so that any indentation in the original comment is preserved.
+  /// For example: [" List of documentation comments, separated by line.", ...]
+  List<String> documentationComments;
+
   @override
   String toString() {
-    return '(Api name:$name location:$location methods:$methods)';
+    return '(Api name:$name location:$location methods:$methods documentationComments:$documentationComments)';
   }
 }
 
@@ -156,7 +172,12 @@
 /// Represents a named entity that has a type.
 class NamedType extends Node {
   /// Parametric constructor for [NamedType].
-  NamedType({required this.name, required this.type, this.offset});
+  NamedType({
+    required this.name,
+    required this.type,
+    this.offset,
+    this.documentationComments = const <String>[],
+  });
 
   /// The name of the entity.
   String name;
@@ -167,9 +188,16 @@
   /// The offset in the source file where the [NamedType] appears.
   int? offset;
 
+  /// List of documentation comments, seperated by line.
+  ///
+  /// Lines should not include the comment marker itself, but should include any
+  /// leading whitespace, so that any indentation in the original comment is preserved.
+  /// For example: [" List of documentation comments, separated by line.", ...]
+  List<String> documentationComments;
+
   @override
   String toString() {
-    return '(NamedType name:$name type:$type)';
+    return '(NamedType name:$name type:$type documentationComments:$documentationComments)';
   }
 }
 
@@ -179,6 +207,7 @@
   Class({
     required this.name,
     required this.fields,
+    this.documentationComments = const <String>[],
   });
 
   /// The name of the class.
@@ -187,9 +216,16 @@
   /// All the fields contained in the class.
   List<NamedType> fields;
 
+  /// List of documentation comments, seperated by line.
+  ///
+  /// Lines should not include the comment marker itself, but should include any
+  /// leading whitespace, so that any indentation in the original comment is preserved.
+  /// For example: [" List of documentation comments, separated by line.", ...]
+  List<String> documentationComments;
+
   @override
   String toString() {
-    return '(Class name:$name fields:$fields)';
+    return '(Class name:$name fields:$fields documentationComments:$documentationComments)';
   }
 }
 
@@ -199,6 +235,7 @@
   Enum({
     required this.name,
     required this.members,
+    this.documentationComments = const <String>[],
   });
 
   /// The name of the enum.
@@ -207,9 +244,16 @@
   /// All of the members of the enum.
   List<String> members;
 
+  /// List of documentation comments, seperated by line.
+  ///
+  /// Lines should not include the comment marker itself, but should include any
+  /// leading whitespace, so that any indentation in the original comment is preserved.
+  /// For example: [" List of documentation comments, separated by line.", ...]
+  List<String> documentationComments;
+
   @override
   String toString() {
-    return '(Enum name:$name members:$members)';
+    return '(Enum name:$name members:$members documentationComments:$documentationComments)';
   }
 }
 
diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart
index ca2c964..ec1b227 100644
--- a/packages/pigeon/lib/cpp_generator.dart
+++ b/packages/pigeon/lib/cpp_generator.dart
@@ -7,6 +7,13 @@
 import 'generator_tools.dart';
 import 'pigeon_lib.dart' show Error;
 
+/// General comment opening token.
+const String _commentPrefix = '//';
+
+/// Documentation comment spec.
+const DocumentCommentSpecification _docCommentSpec =
+    DocumentCommentSpecification(_commentPrefix);
+
 /// Options that control how C++ code will be generated.
 class CppOptions {
   /// Creates a [CppOptions] object
@@ -188,13 +195,21 @@
 void _writeDataClassDeclaration(Indent indent, Class klass, Root root,
     {String? testFriend}) {
   indent.addln('');
-  indent.writeln(
-      '/* Generated class from Pigeon that represents data sent in messages. */');
+
+  const List<String> generatedMessages = <String>[
+    ' Generated class from Pigeon that represents data sent in messages.'
+  ];
+
+  addDocumentationComments(indent, klass.documentationComments, _docCommentSpec,
+      generatorComments: generatedMessages);
+
   indent.write('class ${klass.name} ');
   indent.scoped('{', '};', () {
     indent.scoped(' public:', '', () {
       indent.writeln('${klass.name}();');
       for (final NamedType field in klass.fields) {
+        addDocumentationComments(
+            indent, field.documentationComments, _docCommentSpec);
         final HostDatatype baseDatatype = getFieldHostDatatype(
             field,
             root.classes,
@@ -261,7 +276,7 @@
       root.enums.map((Enum x) => x.name).toSet();
 
   indent.addln('');
-  indent.writeln('/* ${klass.name} */');
+  indent.writeln('$_commentPrefix ${klass.name}');
   indent.addln('');
 
   // Getters and setters.
@@ -398,8 +413,11 @@
 void _writeHostApiHeader(Indent indent, Api api, Root root) {
   assert(api.location == ApiLocation.host);
 
-  indent.writeln(
-      '/* Generated class from Pigeon that represents a handler of messages from Flutter. */');
+  const List<String> generatedMessages = <String>[
+    ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
+  ];
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+      generatorComments: generatedMessages);
   indent.write('class ${api.name} ');
   indent.scoped('{', '};', () {
     indent.scoped(' public:', '', () {
@@ -432,6 +450,10 @@
             return '$argType $argName';
           }));
         }
+
+        addDocumentationComments(
+            indent, method.documentationComments, _docCommentSpec);
+
         if (method.isAsynchronous) {
           argSignature.add('std::function<void($returnTypeName reply)> result');
           indent.writeln(
@@ -442,10 +464,10 @@
         }
       }
       indent.addln('');
-      indent.writeln('/** The codec used by ${api.name}. */');
+      indent.writeln('$_commentPrefix The codec used by ${api.name}.');
       indent.writeln('static const flutter::StandardMessageCodec& GetCodec();');
       indent.writeln(
-          '/** Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`. */');
+          '$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
       indent.writeln(
           'static void SetUp(flutter::BinaryMessenger* binary_messenger, ${api.name}* api);');
       indent.writeln(
@@ -464,13 +486,13 @@
 
   final String codecName = _getCodecName(api);
   indent.format('''
-/** The codec used by ${api.name}. */
+/// The codec used by ${api.name}.
 const flutter::StandardMessageCodec& ${api.name}::GetCodec() {
 \treturn flutter::StandardMessageCodec::GetInstance(&$codecName::GetInstance());
 }
 ''');
   indent.writeln(
-      '/** Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`. */');
+      '$_commentPrefix Sets up an instance of `${api.name}` to handle messages through the `binary_messenger`.');
   indent.write(
       'void ${api.name}::SetUp(flutter::BinaryMessenger* binary_messenger, ${api.name}* api) ');
   indent.scoped('{', '}', () {
@@ -679,8 +701,12 @@
 
 void _writeFlutterApiHeader(Indent indent, Api api) {
   assert(api.location == ApiLocation.flutter);
-  indent.writeln(
-      '/* Generated class from Pigeon that represents Flutter messages that can be called from C++. */');
+
+  const List<String> generatedMessages = <String>[
+    ' Generated class from Pigeon that represents Flutter messages that can be called from C++.'
+  ];
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+      generatorComments: generatedMessages);
   indent.write('class ${api.name} ');
   indent.scoped('{', '};', () {
     indent.scoped(' private:', '', () {
@@ -695,6 +721,8 @@
             ? 'void'
             : _nullSafeCppTypeForDartType(func.returnType);
         final String callback = 'std::function<void($returnType)>&& callback';
+        addDocumentationComments(
+            indent, func.documentationComments, _docCommentSpec);
         if (func.arguments.isEmpty) {
           indent.writeln('void ${func.name}($callback);');
         } else {
@@ -715,7 +743,7 @@
 void _writeFlutterApiSource(Indent indent, Api api) {
   assert(api.location == ApiLocation.flutter);
   indent.writeln(
-      '/* Generated class from Pigeon that represents Flutter messages that can be called from C++. */');
+      '$_commentPrefix Generated class from Pigeon that represents Flutter messages that can be called from C++.');
   indent.write(
       '${api.name}::${api.name}(flutter::BinaryMessenger* binary_messenger) ');
   indent.scoped('{', '}', () {
@@ -988,8 +1016,8 @@
   if (options.copyrightHeader != null) {
     addLines(indent, options.copyrightHeader!, linePrefix: '// ');
   }
-  indent.writeln('// $generatedCodeWarning');
-  indent.writeln('// $seeAlsoWarning');
+  indent.writeln('$_commentPrefix $generatedCodeWarning');
+  indent.writeln('$_commentPrefix $seeAlsoWarning');
   indent.addln('');
   final String guardName = _getGuardName(headerFileName, options.namespace);
   indent.writeln('#ifndef $guardName');
@@ -1024,10 +1052,12 @@
   }
 
   indent.addln('');
-  indent.writeln('/* Generated class from Pigeon. */');
+  indent.writeln('$_commentPrefix Generated class from Pigeon.');
 
   for (final Enum anEnum in root.enums) {
     indent.writeln('');
+    addDocumentationComments(
+        indent, anEnum.documentationComments, _docCommentSpec);
     indent.write('enum class ${anEnum.name} ');
     indent.scoped('{', '};', () {
       int index = 0;
@@ -1074,8 +1104,8 @@
   if (options.copyrightHeader != null) {
     addLines(indent, options.copyrightHeader!, linePrefix: '// ');
   }
-  indent.writeln('// $generatedCodeWarning');
-  indent.writeln('// $seeAlsoWarning');
+  indent.writeln('$_commentPrefix $generatedCodeWarning');
+  indent.writeln('$_commentPrefix $seeAlsoWarning');
   indent.addln('');
   indent.addln('#undef _HAS_EXCEPTIONS');
   indent.addln('');
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index f13c7e7..4b2ff9d 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -11,6 +11,13 @@
 import 'functional.dart';
 import 'generator_tools.dart';
 
+/// Documentation comment open symbol.
+const String _docCommentPrefix = '///';
+
+/// Documentation comment spec.
+const DocumentCommentSpecification _docCommentSpec =
+    DocumentCommentSpecification(_docCommentPrefix);
+
 /// Options that control how Dart code will be generated.
 class DartOptions {
   /// Constructor for DartOptions.
@@ -154,6 +161,7 @@
   _writeCodec(indent, codecName, api, root);
   indent.addln('');
   bool first = true;
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
   indent.write('class ${api.name} ');
   indent.scoped('{', '}', () {
     indent.format('''
@@ -173,6 +181,8 @@
       } else {
         first = false;
       }
+      addDocumentationComments(
+          indent, func.documentationComments, _docCommentSpec);
       String argSignature = '';
       String sendArgument = 'null';
       if (func.arguments.isNotEmpty) {
@@ -264,11 +274,16 @@
   assert(api.location == ApiLocation.flutter);
   final String codecName = _getCodecName(api);
   _writeCodec(indent, codecName, api, root);
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
+
   indent.write('abstract class ${api.name} ');
   indent.scoped('{', '}', () {
     indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
     indent.addln('');
     for (final Method func in api.methods) {
+      addDocumentationComments(
+          indent, func.documentationComments, _docCommentSpec);
+
       final bool isAsync = func.isAsynchronous;
       final String returnType = isAsync
           ? 'Future<${_addGenericTypesNullable(func.returnType)}>'
@@ -433,6 +448,8 @@
   void writeEnums() {
     for (final Enum anEnum in root.enums) {
       indent.writeln('');
+      addDocumentationComments(
+          indent, anEnum.documentationComments, _docCommentSpec);
       indent.write('enum ${anEnum.name} ');
       indent.scoped('{', '}', () {
         for (final String member in anEnum.members) {
@@ -555,11 +572,17 @@
       });
     }
 
+    addDocumentationComments(
+        indent, klass.documentationComments, _docCommentSpec);
+
     indent.write('class ${klass.name} ');
     indent.scoped('{', '}', () {
       writeConstructor();
       indent.addln('');
       for (final NamedType field in klass.fields) {
+        addDocumentationComments(
+            indent, field.documentationComments, _docCommentSpec);
+
         final String datatype = _addGenericTypesNullable(field.type);
         indent.writeln('$datatype ${field.name};');
       }
@@ -696,6 +719,7 @@
         methods: api.methods,
         location: ApiLocation.flutter,
         dartHostTestHandler: api.dartHostTestHandler,
+        documentationComments: api.documentationComments,
       );
       indent.writeln('');
       _writeFlutterApi(
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index a0607a9..0e6e659 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -9,7 +9,7 @@
 import 'ast.dart';
 
 /// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '4.0.3';
+const String pigeonVersion = '4.1.0';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
@@ -432,3 +432,59 @@
 /// Returns true if the [TypeDeclaration] represents an enum.
 bool isEnum(Root root, TypeDeclaration type) =>
     root.enums.map((Enum e) => e.name).contains(type.baseName);
+
+/// Describes how to format a document comment.
+class DocumentCommentSpecification {
+  /// Constructor for [DocumentationCommentSpecification]
+  const DocumentCommentSpecification(
+    this.openCommentToken, {
+    this.closeCommentToken = '',
+    this.blockContinuationToken = '',
+  });
+
+  /// Token that represents the open symbol for a documentation comment.
+  final String openCommentToken;
+
+  /// Token that represents the closing symbol for a documentation comment.
+  final String closeCommentToken;
+
+  /// Token that represents the continuation symbol for a block of documentation comments.
+  final String blockContinuationToken;
+}
+
+/// Formats documentation comments and adds them to current Indent.
+///
+/// The [comments] list is meant for comments written in the input dart file.
+/// The [generatorComments] list is meant for comments added by the generators.
+/// Include white space for all tokens when called, no assumptions are made.
+void addDocumentationComments(
+  Indent indent,
+  List<String> comments,
+  DocumentCommentSpecification commentSpec, {
+  List<String> generatorComments = const <String>[],
+}) {
+  final List<String> allComments = <String>[
+    ...comments,
+    if (comments.isNotEmpty && generatorComments.isNotEmpty) '',
+    ...generatorComments,
+  ];
+  String currentLineOpenToken = commentSpec.openCommentToken;
+  if (allComments.length > 1) {
+    if (commentSpec.closeCommentToken != '') {
+      indent.writeln(commentSpec.openCommentToken);
+      currentLineOpenToken = commentSpec.blockContinuationToken;
+    }
+    for (final String line in allComments) {
+      indent.writeln(
+        '$currentLineOpenToken$line',
+      );
+    }
+    if (commentSpec.closeCommentToken != '') {
+      indent.writeln(commentSpec.closeCommentToken);
+    }
+  } else if (allComments.length == 1) {
+    indent.writeln(
+      '$currentLineOpenToken${allComments.first}${commentSpec.closeCommentToken}',
+    );
+  }
+}
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index e414d36..f820036 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -7,6 +7,23 @@
 import 'generator_tools.dart';
 import 'pigeon_lib.dart' show TaskQueueType;
 
+/// Documentation open symbol.
+const String _docCommentPrefix = '/**';
+
+/// Documentation continuation symbol.
+const String _docCommentContinuation = ' *';
+
+/// Documentation close symbol.
+const String _docCommentSuffix = ' */';
+
+/// Documentation comment spec.
+const DocumentCommentSpecification _docCommentSpec =
+    DocumentCommentSpecification(
+  _docCommentPrefix,
+  closeCommentToken: _docCommentSuffix,
+  blockContinuationToken: _docCommentContinuation,
+);
+
 /// Options that control how Java code will be generated.
 class JavaOptions {
   /// Creates a [JavaOptions] object
@@ -159,6 +176,9 @@
           : _javaTypeForDartType(method.returnType);
       argSignature.add('Result<$resultType> result');
     }
+    addDocumentationComments(
+        indent, method.documentationComments, _docCommentSpec);
+
     indent.writeln('$returnType ${method.name}(${argSignature.join(', ')});');
   }
 
@@ -278,8 +298,12 @@
     });
   }
 
-  indent.writeln(
-      '/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/');
+  const List<String> generatedMessages = <String>[
+    ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
+  ];
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+      generatorComments: generatedMessages);
+
   indent.write('public interface ${api.name} ');
   indent.scoped('{', '}', () {
     api.methods.forEach(writeInterfaceMethod);
@@ -292,7 +316,7 @@
 }
 ''');
     indent.writeln(
-        '/** Sets up an instance of `${api.name}` to handle messages through the `binaryMessenger`. */');
+        '${_docCommentPrefix}Sets up an instance of `${api.name}` to handle messages through the `binaryMessenger`.$_docCommentSuffix');
     indent.write(
         'static void setup(BinaryMessenger binaryMessenger, ${api.name} api) ');
     indent.scoped('{', '}', () {
@@ -319,8 +343,12 @@
 /// }
 void _writeFlutterApi(Indent indent, Api api) {
   assert(api.location == ApiLocation.flutter);
-  indent.writeln(
-      '/** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/');
+  const List<String> generatedMessages = <String>[
+    ' Generated class from Pigeon that represents Flutter messages that can be called from Java.'
+  ];
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+      generatorComments: generatedMessages);
+
   indent.write('public static class ${api.name} ');
   indent.scoped('{', '}', () {
     indent.writeln('private final BinaryMessenger binaryMessenger;');
@@ -344,6 +372,8 @@
           ? 'Void'
           : _javaTypeForDartType(func.returnType);
       String sendArgument;
+      addDocumentationComments(
+          indent, func.documentationComments, _docCommentSpec);
       if (func.arguments.isEmpty) {
         indent.write('public void ${func.name}(Reply<$returnType> callback) ');
         sendArgument = 'null';
@@ -511,6 +541,9 @@
   }
 
   void writeEnum(Enum anEnum) {
+    addDocumentationComments(
+        indent, anEnum.documentationComments, _docCommentSpec);
+
     indent.write('public enum ${anEnum.name} ');
     indent.scoped('{', '}', () {
       int index = 0;
@@ -541,6 +574,9 @@
           (TypeDeclaration x) => _javaTypeForBuiltinDartType(x));
       final String nullability =
           field.type.isNullable ? '@Nullable' : '@NonNull';
+      addDocumentationComments(
+          indent, field.documentationComments, _docCommentSpec);
+
       indent.writeln(
           'private $nullability ${hostDatatype.datatype} ${field.name};');
       indent.writeln(
@@ -639,8 +675,13 @@
       });
     }
 
-    indent.writeln(
-        '/** Generated class from Pigeon that represents data sent in messages. */');
+    const List<String> generatedMessages = <String>[
+      ' Generated class from Pigeon that represents data sent in messages.'
+    ];
+    addDocumentationComments(
+        indent, klass.documentationComments, _docCommentSpec,
+        generatorComments: generatedMessages);
+
     indent.write('public static class ${klass.name} ');
     indent.scoped('{', '}', () {
       for (final NamedType field in klass.fields) {
@@ -652,7 +693,7 @@
           .map((NamedType e) => !e.type.isNullable)
           .any((bool e) => e)) {
         indent.writeln(
-            '/** Constructor is private to enforce null safety; use Builder. */');
+            '${_docCommentPrefix}Constructor is private to enforce null safety; use Builder.$_docCommentSuffix');
         indent.writeln('private ${klass.name}() {}');
       }
 
@@ -697,7 +738,8 @@
   indent.addln('');
   writeImports();
   indent.addln('');
-  indent.writeln('/** Generated class from Pigeon. */');
+  indent.writeln(
+      '${_docCommentPrefix}Generated class from Pigeon.$_docCommentSuffix');
   indent.writeln(
       '@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})');
   if (options.useGeneratedAnnotation ?? false) {
diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart
index 088f9e4..af8edd4 100644
--- a/packages/pigeon/lib/objc_generator.dart
+++ b/packages/pigeon/lib/objc_generator.dart
@@ -7,6 +7,13 @@
 import 'generator_tools.dart';
 import 'pigeon_lib.dart' show Error, TaskQueueType;
 
+/// Documentation comment open symbol.
+const String _docCommentPrefix = '///';
+
+/// Documentation comment spec.
+const DocumentCommentSpecification _docCommentSpec =
+    DocumentCommentSpecification(_docCommentPrefix);
+
 /// Options that control how Objective-C code will be generated.
 class ObjcOptions {
   /// Parametric constructor for ObjcOptions.
@@ -195,13 +202,16 @@
     Indent indent, List<Class> classes, List<Enum> enums, String? prefix) {
   final List<String> enumNames = enums.map((Enum x) => x.name).toList();
   for (final Class klass in classes) {
+    addDocumentationComments(
+        indent, klass.documentationComments, _docCommentSpec);
+
     indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject');
     if (klass.fields.isNotEmpty) {
       if (klass.fields
           .map((NamedType e) => !e.type.isNullable)
           .any((bool e) => e)) {
         indent.writeln(
-            '/// `init` unavailable to enforce nonnull fields, see the `make` class method.');
+            '$_docCommentPrefix `init` unavailable to enforce nonnull fields, see the `make` class method.');
         indent.writeln('- (instancetype)init NS_UNAVAILABLE;');
       }
       _writeInitializerDeclaration(indent, klass, classes, enums, prefix);
@@ -217,6 +227,8 @@
               ? (String x) => _className(prefix, x)
               : (String x) => '${_className(prefix, x)} *');
       late final String propertyType;
+      addDocumentationComments(
+          indent, field.documentationComments, _docCommentSpec);
       if (enumNames.contains(field.type.baseName)) {
         propertyType = 'assign';
       } else {
@@ -415,6 +427,8 @@
 void _writeHostApiDeclaration(
     Indent indent, Api api, ObjcOptions options, Root root) {
   final String apiName = _className(options.prefix, api.name);
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
+
   indent.writeln('@protocol $apiName');
   for (final Method func in api.methods) {
     final _ObjcPtr returnTypeName =
@@ -443,8 +457,12 @@
     if (!func.returnType.isNullable &&
         !func.returnType.isVoid &&
         !func.isAsynchronous) {
-      indent.writeln('/// @return `nil` only when `error != nil`.');
+      indent.writeln(
+          '$_docCommentPrefix @return `nil` only when `error != nil`.');
     }
+    addDocumentationComments(
+        indent, func.documentationComments, _docCommentSpec);
+
     final String signature = _makeObjcSignature(
       func: func,
       options: options,
@@ -473,6 +491,8 @@
 void _writeFlutterApiDeclaration(
     Indent indent, Api api, ObjcOptions options, Root root) {
   final String apiName = _className(options.prefix, api.name);
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec);
+
   indent.writeln('@interface $apiName : NSObject');
   indent.writeln(
       '- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;');
@@ -480,6 +500,9 @@
     final _ObjcPtr returnType =
         _objcTypeForDartType(options.prefix, func.returnType);
     final String callbackType = _callbackForType(func.returnType, returnType);
+    addDocumentationComments(
+        indent, func.documentationComments, _docCommentSpec);
+
     indent.writeln('${_makeObjcSignature(
       func: func,
       options: options,
@@ -518,6 +541,9 @@
 
   void writeEnum(Enum anEnum) {
     final String enumName = _className(options.prefix, anEnum.name);
+    addDocumentationComments(
+        indent, anEnum.documentationComments, _docCommentSpec);
+
     indent.write('typedef NS_ENUM(NSUInteger, $enumName) ');
     indent.scoped('{', '};', () {
       int index = 0;
@@ -553,7 +579,7 @@
 
   for (final Api api in root.apis) {
     indent.writeln(
-        '/// The codec used by ${_className(options.prefix, api.name)}.');
+        '${_docCommentPrefix}The codec used by ${_className(options.prefix, api.name)}.');
     indent.writeln(
         'NSObject<FlutterMessageCodec> *${_getCodecGetterName(options.prefix, api.name)}(void);');
     indent.addln('');
@@ -724,6 +750,9 @@
   indent.scoped('{', '}', () {
     for (final Method func in api.methods) {
       indent.write('');
+      addDocumentationComments(
+          indent, func.documentationComments, _docCommentSpec);
+
       indent.scoped('{', '}', () {
         String? taskQueue;
         if (func.taskQueueType != TaskQueueType.serial) {
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index 0e282c6..32c6122 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -17,6 +17,7 @@
 import 'package:analyzer/dart/ast/ast.dart' as dart_ast;
 import 'package:analyzer/dart/ast/syntactic_entity.dart'
     as dart_ast_syntactic_entity;
+import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/ast/visitor.dart' as dart_ast_visitor;
 import 'package:analyzer/error/error.dart' show AnalysisError;
 import 'package:args/args.dart';
@@ -866,22 +867,42 @@
           location: ApiLocation.host,
           methods: <Method>[],
           dartHostTestHandler: dartHostTestHandler,
+          documentationComments:
+              _documentationCommentsParser(node.documentationComment?.tokens),
         );
       } else if (_hasMetadata(node.metadata, 'FlutterApi')) {
         _currentApi = Api(
           name: node.name2.lexeme,
           location: ApiLocation.flutter,
           methods: <Method>[],
+          documentationComments:
+              _documentationCommentsParser(node.documentationComment?.tokens),
         );
       }
     } else {
-      _currentClass = Class(name: node.name2.lexeme, fields: <NamedType>[]);
+      _currentClass = Class(
+        name: node.name2.lexeme,
+        fields: <NamedType>[],
+        documentationComments:
+            _documentationCommentsParser(node.documentationComment?.tokens),
+      );
     }
 
     node.visitChildren(this);
     return null;
   }
 
+  /// Converts Token's to Strings and removes documentation comment symbol.
+  List<String> _documentationCommentsParser(List<Token>? comments) {
+    const String docCommentPrefix = '///';
+    return comments
+            ?.map((Token line) => line.length > docCommentPrefix.length
+                ? line.toString().substring(docCommentPrefix.length)
+                : '')
+            .toList() ??
+        <String>[];
+  }
+
   NamedType formalParameterToField(dart_ast.FormalParameter parameter) {
     final dart_ast.NamedType? namedType =
         getFirstChildOfType<dart_ast.NamedType>(parameter);
@@ -952,12 +973,14 @@
     final TaskQueueType taskQueueType =
         _stringToEnum(TaskQueueType.values, taskQueueTypeName) ??
             TaskQueueType.serial;
+
     if (_currentApi != null) {
       // Methods without named return types aren't supported.
       final dart_ast.TypeAnnotation returnType = node.returnType!;
       final dart_ast.SimpleIdentifier returnTypeIdentifier =
           getFirstChildOfType<dart_ast.SimpleIdentifier>(returnType)!;
-      _currentApi!.methods.add(Method(
+      _currentApi!.methods.add(
+        Method(
           name: node.name2.lexeme,
           returnType: TypeDeclaration(
               baseName: returnTypeIdentifier.name,
@@ -968,7 +991,11 @@
           isAsynchronous: isAsynchronous,
           objcSelector: objcSelector,
           offset: node.offset,
-          taskQueueType: taskQueueType));
+          taskQueueType: taskQueueType,
+          documentationComments:
+              _documentationCommentsParser(node.documentationComment?.tokens),
+        ),
+      );
     } else if (_currentClass != null) {
       _errors.add(Error(
           message:
@@ -982,10 +1009,13 @@
   @override
   Object? visitEnumDeclaration(dart_ast.EnumDeclaration node) {
     _enums.add(Enum(
-        name: node.name2.lexeme,
-        members: node.constants
-            .map((dart_ast.EnumConstantDeclaration e) => e.name2.lexeme)
-            .toList()));
+      name: node.name2.lexeme,
+      members: node.constants
+          .map((dart_ast.EnumConstantDeclaration e) => e.name2.lexeme)
+          .toList(),
+      documentationComments:
+          _documentationCommentsParser(node.documentationComment?.tokens),
+    ));
     node.visitChildren(this);
     return null;
   }
@@ -1026,12 +1056,16 @@
         } else {
           final dart_ast.TypeArgumentList? typeArguments = type.typeArguments;
           _currentClass!.fields.add(NamedType(
-              type: TypeDeclaration(
-                  baseName: type.name.name,
-                  isNullable: type.question != null,
-                  typeArguments: typeAnnotationsToTypeArguments(typeArguments)),
-              name: node.fields.variables[0].name2.lexeme,
-              offset: node.offset));
+            type: TypeDeclaration(
+              baseName: type.name.name,
+              isNullable: type.question != null,
+              typeArguments: typeAnnotationsToTypeArguments(typeArguments),
+            ),
+            name: node.fields.variables[0].name2.lexeme,
+            offset: node.offset,
+            documentationComments:
+                _documentationCommentsParser(node.documentationComment?.tokens),
+          ));
         }
       } else {
         _errors.add(Error(
diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart
index aaff9ee..7719b05 100644
--- a/packages/pigeon/lib/swift_generator.dart
+++ b/packages/pigeon/lib/swift_generator.dart
@@ -6,6 +6,13 @@
 import 'functional.dart';
 import 'generator_tools.dart';
 
+/// Documentation comment open symbol.
+const String _docCommentPrefix = '///';
+
+/// Documentation comment spec.
+const DocumentCommentSpecification _docCommentSpec =
+    DocumentCommentSpecification(_docCommentPrefix);
+
 /// Options that control how Swift code will be generated.
 class SwiftOptions {
   /// Creates a [SwiftOptions] object
@@ -136,8 +143,12 @@
 
   final String apiName = api.name;
 
-  indent.writeln(
-      '/// Generated protocol from Pigeon that represents a handler of messages from Flutter.');
+  const List<String> generatedComments = <String>[
+    'Generated protocol from Pigeon that represents a handler of messages from Flutter.'
+  ];
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+      generatorComments: generatedComments);
+
   indent.write('protocol $apiName ');
   indent.scoped('{', '}', () {
     for (final Method method in api.methods) {
@@ -156,6 +167,9 @@
       final String returnType = method.returnType.isVoid
           ? ''
           : _nullsafeSwiftTypeForDartType(method.returnType);
+      addDocumentationComments(
+          indent, method.documentationComments, _docCommentSpec);
+
       if (method.isAsynchronous) {
         argSignature.add('completion: @escaping ($returnType) -> Void');
         indent.writeln('func ${method.name}(${argSignature.join(', ')})');
@@ -170,21 +184,23 @@
 
   indent.addln('');
   indent.writeln(
-      '/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
+      '$_docCommentPrefix Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
   indent.write('class ${apiName}Setup ');
   indent.scoped('{', '}', () {
     final String codecName = _getCodecName(api);
-    indent.writeln('/// The codec used by $apiName.');
+    indent.writeln('$_docCommentPrefix The codec used by $apiName.');
     indent.writeln(
         'static var codec: FlutterStandardMessageCodec { $codecName.shared }');
     indent.writeln(
-        '/// Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
+        '$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
     indent.write(
         'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) ');
     indent.scoped('{', '}', () {
       for (final Method method in api.methods) {
         final String channelName = makeChannelName(api, method);
         final String varChannelName = '${method.name}Channel';
+        addDocumentationComments(
+            indent, method.documentationComments, _docCommentSpec);
 
         indent.writeln(
             'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger, codec: codec)');
@@ -260,8 +276,12 @@
 /// }
 void _writeFlutterApi(Indent indent, Api api, Root root) {
   assert(api.location == ApiLocation.flutter);
-  indent.writeln(
-      '/// Generated class from Pigeon that represents Flutter messages that can be called from Swift.');
+  const List<String> generatedComments = <String>[
+    'Generated class from Pigeon that represents Flutter messages that can be called from Swift.'
+  ];
+  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+      generatorComments: generatedComments);
+
   indent.write('class ${api.name} ');
   indent.scoped('{', '}', () {
     indent.writeln('private let binaryMessenger: FlutterBinaryMessenger');
@@ -280,6 +300,9 @@
           ? ''
           : _nullsafeSwiftTypeForDartType(func.returnType);
       String sendArgument;
+      addDocumentationComments(
+          indent, func.documentationComments, _docCommentSpec);
+
       if (func.arguments.isEmpty) {
         indent.write(
             'func ${func.name}(completion: @escaping ($returnType) -> Void) ');
@@ -433,6 +456,9 @@
   }
 
   void writeEnum(Enum anEnum) {
+    addDocumentationComments(
+        indent, anEnum.documentationComments, _docCommentSpec);
+
     indent.write('enum ${anEnum.name}: Int ');
     indent.scoped('{', '}', () {
       // We use explicit indexing here as use of the ordinal() method is
@@ -449,6 +475,9 @@
 
   void writeDataClass(Class klass) {
     void writeField(NamedType field) {
+      addDocumentationComments(
+          indent, field.documentationComments, _docCommentSpec);
+
       indent.write(
           'var ${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}');
       final String defaultNil = field.type.isNullable ? ' = nil' : '';
@@ -542,8 +571,13 @@
       });
     }
 
-    indent.writeln(
-        '/// Generated class from Pigeon that represents data sent in messages.');
+    const List<String> generatedComments = <String>[
+      'Generated class from Pigeon that represents data sent in messages.'
+    ];
+    addDocumentationComments(
+        indent, klass.documentationComments, _docCommentSpec,
+        generatorComments: generatedComments);
+
     indent.write('struct ${klass.name} ');
     indent.scoped('{', '}', () {
       klass.fields.forEach(writeField);
@@ -589,7 +623,7 @@
   indent.addln('');
   writeImports();
   indent.addln('');
-  indent.writeln('/// Generated class from Pigeon.');
+  indent.writeln('$_docCommentPrefix Generated class from Pigeon.');
   for (final Enum anEnum in root.enums) {
     indent.writeln('');
     writeEnum(anEnum);
diff --git a/packages/pigeon/pigeons/enum.dart b/packages/pigeon/pigeons/enum.dart
index 8b35176..7552ac1 100644
--- a/packages/pigeon/pigeons/enum.dart
+++ b/packages/pigeon/pigeons/enum.dart
@@ -4,22 +4,31 @@
 
 import 'package:pigeon/pigeon.dart';
 
+/// This comment is to test enum documentation comments.
 enum EnumState {
   Pending,
   Success,
   Error,
 }
 
+/// This comment is to test class documentation comments.
 class DataWithEnum {
+  /// This comment is to test field documentation comments.
   EnumState? state;
 }
 
 @HostApi()
+
+/// This comment is to test api documentation comments.
 abstract class EnumApi2Host {
+  /// This comment is to test method documentation comments.
   DataWithEnum echo(DataWithEnum data);
 }
 
 @FlutterApi()
+
+/// This comment is to test api documentation comments.
 abstract class EnumApi2Flutter {
+  /// This comment is to test method documentation comments.
   DataWithEnum echo(DataWithEnum data);
 }
diff --git a/packages/pigeon/pigeons/message.dart b/packages/pigeon/pigeons/message.dart
index 7ffe172..644031c 100644
--- a/packages/pigeon/pigeons/message.dart
+++ b/packages/pigeon/pigeons/message.dart
@@ -16,40 +16,79 @@
     prefix: 'AC',
   ),
 ))
+
+/// This comment is to test enum documentation comments.
+///
+/// This comment also tests multiple line comments.
 enum MessageRequestState {
   pending,
   success,
   failure,
 }
 
+/// This comment is to test class documentation comments.
+///
+/// This comment also tests multiple line comments.
 class MessageSearchRequest {
+  /// This comment is to test field documentation comments.
   String? query;
+
+  /// This comment is to test field documentation comments.
   int? anInt;
+
+  /// This comment is to test field documentation comments.
   bool? aBool;
 }
 
+/// This comment is to test class documentation comments.
 class MessageSearchReply {
+  /// This comment is to test field documentation comments.
+  ///
+  /// This comment also tests multiple line comments.
   String? result;
+
+  /// This comment is to test field documentation comments.
   String? error;
+
+  /// This comment is to test field documentation comments.
   MessageRequestState? state;
 }
 
 @HostApi(dartHostTestHandler: 'TestHostApi')
+
+/// This comment is to test api documentation comments.
+///
+/// This comment also tests multiple line comments.
 abstract class MessageApi {
+  /// This comment is to test documentation comments.
+  ///
+  /// This comment also tests multiple line comments.
   void initialize();
+
+  /// This comment is to test method documentation comments.
   MessageSearchReply search(MessageSearchRequest request);
 }
 
+/// This comment is to test class documentation comments.
 class MessageNested {
+  /// This comment is to test field documentation comments.
   MessageSearchRequest? request;
 }
 
 @HostApi(dartHostTestHandler: 'TestNestedApi')
+
+/// This comment is to test api documentation comments.
 abstract class MessageNestedApi {
+  /// This comment is to test method documentation comments.
+  ///
+  /// This comment also tests multiple line comments.
   MessageSearchReply search(MessageNested nested);
 }
 
 @FlutterApi()
+
+/// This comment is to test api documentation comments.
 abstract class MessageFlutterSearchApi {
+  /// This comment is to test method documentation comments.
   MessageSearchReply search(MessageSearchRequest request);
 }
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index d677d9e..72f260d 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
 repository: https://github.com/flutter/packages/tree/main/packages/pigeon
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 4.0.3 # This must match the version in lib/generator_tools.dart
+version: 4.1.0 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/pigeon/test/cpp_generator_test.dart b/packages/pigeon/test/cpp_generator_test.dart
index 14b09e9..8b36e46 100644
--- a/packages/pigeon/test/cpp_generator_test.dart
+++ b/packages/pigeon/test/cpp_generator_test.dart
@@ -1051,4 +1051,75 @@
     final List<Error> errors = validateCpp(const CppOptions(), root);
     expect(errors.length, 1);
   });
+
+  test('transfers documentation comments', () {
+    final List<String> comments = <String>[
+      ' api comment',
+      ' api method comment',
+      ' class comment',
+      ' class field comment',
+      ' enum comment',
+    ];
+    int count = 0;
+
+    final Root root = Root(
+      apis: <Api>[
+        Api(
+          name: 'Api',
+          location: ApiLocation.flutter,
+          documentationComments: <String>[comments[count++]],
+          methods: <Method>[
+            Method(
+              name: 'method',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              documentationComments: <String>[comments[count++]],
+              arguments: <NamedType>[
+                NamedType(
+                  name: 'field',
+                  type: const TypeDeclaration(
+                    baseName: 'int',
+                    isNullable: true,
+                  ),
+                ),
+              ],
+            )
+          ],
+        )
+      ],
+      classes: <Class>[
+        Class(
+          name: 'class',
+          documentationComments: <String>[comments[count++]],
+          fields: <NamedType>[
+            NamedType(
+                documentationComments: <String>[comments[count++]],
+                type: const TypeDeclaration(
+                    baseName: 'Map',
+                    isNullable: true,
+                    typeArguments: <TypeDeclaration>[
+                      TypeDeclaration(baseName: 'String', isNullable: true),
+                      TypeDeclaration(baseName: 'int', isNullable: true),
+                    ]),
+                name: 'field1'),
+          ],
+        ),
+      ],
+      enums: <Enum>[
+        Enum(
+          name: 'enum',
+          documentationComments: <String>[comments[count++]],
+          members: <String>[
+            'one',
+            'two',
+          ],
+        ),
+      ],
+    );
+    final StringBuffer sink = StringBuffer();
+    generateCppHeader('foo', const CppOptions(), root, sink);
+    final String code = sink.toString();
+    for (final String comment in comments) {
+      expect(code, contains('//$comment'));
+    }
+  });
 }
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index ba30219..cf467d2 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -1164,4 +1164,74 @@
       tempDir.deleteSync(recursive: true);
     }
   });
+
+  test('transfers documentation comments', () {
+    final List<String> comments = <String>[
+      ' api comment',
+      ' api method comment',
+      ' class comment',
+      ' class field comment',
+      ' enum comment',
+    ];
+    int count = 0;
+    final Root root = Root(
+      apis: <Api>[
+        Api(
+          name: 'Api',
+          location: ApiLocation.flutter,
+          documentationComments: <String>[comments[count++]],
+          methods: <Method>[
+            Method(
+              name: 'method',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              documentationComments: <String>[comments[count++]],
+              arguments: <NamedType>[
+                NamedType(
+                  name: 'field',
+                  type: const TypeDeclaration(
+                    baseName: 'int',
+                    isNullable: true,
+                  ),
+                ),
+              ],
+            )
+          ],
+        )
+      ],
+      classes: <Class>[
+        Class(
+          name: 'class',
+          documentationComments: <String>[comments[count++]],
+          fields: <NamedType>[
+            NamedType(
+                documentationComments: <String>[comments[count++]],
+                type: const TypeDeclaration(
+                    baseName: 'Map',
+                    isNullable: true,
+                    typeArguments: <TypeDeclaration>[
+                      TypeDeclaration(baseName: 'String', isNullable: true),
+                      TypeDeclaration(baseName: 'int', isNullable: true),
+                    ]),
+                name: 'field1'),
+          ],
+        ),
+      ],
+      enums: <Enum>[
+        Enum(
+          name: 'enum',
+          documentationComments: <String>[comments[count++]],
+          members: <String>[
+            'one',
+            'two',
+          ],
+        ),
+      ],
+    );
+    final StringBuffer sink = StringBuffer();
+    generateDart(const DartOptions(), root, sink);
+    final String code = sink.toString();
+    for (final String comment in comments) {
+      expect(code, contains('///$comment'));
+    }
+  });
 }
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index b2eca32..da68603 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -1069,4 +1069,81 @@
     expect(code,
         isNot(contains('@javax.annotation.Generated("dev.flutter.pigeon")')));
   });
+
+  test('transfers documentation comments', () {
+    final List<String> comments = <String>[
+      ' api comment',
+      ' api method comment',
+      ' class comment',
+      ' class field comment',
+      ' enum comment',
+    ];
+    int count = 0;
+
+    final Root root = Root(
+      apis: <Api>[
+        Api(
+          name: 'api',
+          location: ApiLocation.flutter,
+          documentationComments: <String>[comments[count++]],
+          methods: <Method>[
+            Method(
+              name: 'method',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              documentationComments: <String>[comments[count++]],
+              arguments: <NamedType>[
+                NamedType(
+                  name: 'field',
+                  type: const TypeDeclaration(
+                    baseName: 'int',
+                    isNullable: true,
+                  ),
+                ),
+              ],
+            )
+          ],
+        )
+      ],
+      classes: <Class>[
+        Class(
+          name: 'class',
+          documentationComments: <String>[comments[count++]],
+          fields: <NamedType>[
+            NamedType(
+              documentationComments: <String>[comments[count++]],
+              type: const TypeDeclaration(
+                  baseName: 'Map',
+                  isNullable: true,
+                  typeArguments: <TypeDeclaration>[
+                    TypeDeclaration(baseName: 'String', isNullable: true),
+                    TypeDeclaration(baseName: 'int', isNullable: true),
+                  ]),
+              name: 'field1',
+            ),
+          ],
+        ),
+      ],
+      enums: <Enum>[
+        Enum(
+          name: 'enum',
+          documentationComments: <String>[comments[count++]],
+          members: <String>[
+            'one',
+            'two',
+          ],
+        ),
+      ],
+    );
+    final StringBuffer sink = StringBuffer();
+    const JavaOptions javaOptions = JavaOptions(className: 'Messages');
+    generateJava(javaOptions, root, sink);
+    final String code = sink.toString();
+    for (final String comment in comments) {
+      // This regex finds the comment only between the open and close comment block
+      expect(
+          RegExp(r'(?<=\/\*\*.*?)' + comment + r'(?=.*?\*\/)', dotAll: true)
+              .hasMatch(code),
+          true);
+    }
+  });
 }
diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart
index 5754601..ff698b3 100644
--- a/packages/pigeon/test/objc_generator_test.dart
+++ b/packages/pigeon/test/objc_generator_test.dart
@@ -1740,4 +1740,76 @@
             'NSObject<FlutterTaskQueue> *taskQueue = [binaryMessenger makeBackgroundTaskQueue];'));
     expect(code, contains('taskQueue:taskQueue'));
   });
+
+  test('transfers documentation comments', () {
+    final List<String> comments = <String>[
+      ' api comment',
+      ' api method comment',
+      ' class comment',
+      ' class field comment',
+      ' enum comment',
+    ];
+    int count = 0;
+
+    final Root root = Root(
+      apis: <Api>[
+        Api(
+          name: 'api',
+          location: ApiLocation.flutter,
+          documentationComments: <String>[comments[count++]],
+          methods: <Method>[
+            Method(
+              name: 'method',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              documentationComments: <String>[comments[count++]],
+              arguments: <NamedType>[
+                NamedType(
+                  name: 'field',
+                  type: const TypeDeclaration(
+                    baseName: 'int',
+                    isNullable: true,
+                  ),
+                ),
+              ],
+            )
+          ],
+        )
+      ],
+      classes: <Class>[
+        Class(
+          name: 'class',
+          documentationComments: <String>[comments[count++]],
+          fields: <NamedType>[
+            NamedType(
+              documentationComments: <String>[comments[count++]],
+              type: const TypeDeclaration(
+                  baseName: 'Map',
+                  isNullable: true,
+                  typeArguments: <TypeDeclaration>[
+                    TypeDeclaration(baseName: 'String', isNullable: true),
+                    TypeDeclaration(baseName: 'int', isNullable: true),
+                  ]),
+              name: 'field1',
+            ),
+          ],
+        ),
+      ],
+      enums: <Enum>[
+        Enum(
+          name: 'enum',
+          documentationComments: <String>[comments[count++]],
+          members: <String>[
+            'one',
+            'two',
+          ],
+        ),
+      ],
+    );
+    final StringBuffer sink = StringBuffer();
+    generateObjcHeader(const ObjcOptions(), root, sink);
+    final String code = sink.toString();
+    for (final String comment in comments) {
+      expect(code, contains('///$comment'));
+    }
+  });
 }
diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart
index 319054f..c40d85a 100644
--- a/packages/pigeon/test/swift_generator_test.dart
+++ b/packages/pigeon/test/swift_generator_test.dart
@@ -947,4 +947,77 @@
     final String code = sink.toString();
     expect(code, contains('var input: String\n'));
   });
+
+  test('transfers documentation comments', () {
+    final List<String> comments = <String>[
+      ' api comment',
+      ' api method comment',
+      ' class comment',
+      ' class field comment',
+      ' enum comment',
+    ];
+    int count = 0;
+
+    final Root root = Root(
+      apis: <Api>[
+        Api(
+          name: 'api',
+          location: ApiLocation.flutter,
+          documentationComments: <String>[comments[count++]],
+          methods: <Method>[
+            Method(
+              name: 'method',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              documentationComments: <String>[comments[count++]],
+              arguments: <NamedType>[
+                NamedType(
+                  name: 'field',
+                  type: const TypeDeclaration(
+                    baseName: 'int',
+                    isNullable: true,
+                  ),
+                ),
+              ],
+            )
+          ],
+        )
+      ],
+      classes: <Class>[
+        Class(
+          name: 'class',
+          documentationComments: <String>[comments[count++]],
+          fields: <NamedType>[
+            NamedType(
+              documentationComments: <String>[comments[count++]],
+              type: const TypeDeclaration(
+                  baseName: 'Map',
+                  isNullable: true,
+                  typeArguments: <TypeDeclaration>[
+                    TypeDeclaration(baseName: 'String', isNullable: true),
+                    TypeDeclaration(baseName: 'int', isNullable: true),
+                  ]),
+              name: 'field1',
+            ),
+          ],
+        ),
+      ],
+      enums: <Enum>[
+        Enum(
+          name: 'enum',
+          documentationComments: <String>[comments[count++]],
+          members: <String>[
+            'one',
+            'two',
+          ],
+        ),
+      ],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    for (final String comment in comments) {
+      expect(code, contains('///$comment'));
+    }
+  });
 }