diff --git a/lib/yard/autoload.rb b/lib/yard/autoload.rb
index d00371c64..9c3caf164 100644
--- a/lib/yard/autoload.rb
+++ b/lib/yard/autoload.rb
@@ -78,6 +78,7 @@ module C
       autoload :AttributeHandler,         __p('handlers/c/attribute_handler')
       autoload :ClassHandler,             __p('handlers/c/class_handler')
       autoload :ConstantHandler,          __p('handlers/c/constant_handler')
+      autoload :DirectiveParserHandler,   __p('handlers/c/directive_parser_handler')
       autoload :HandlerMethods,           __p('handlers/c/handler_methods')
       autoload :InitHandler,              __p('handlers/c/init_handler')
       autoload :MethodHandler,            __p('handlers/c/method_handler')
diff --git a/lib/yard/handlers/c/directive_parser_handler.rb b/lib/yard/handlers/c/directive_parser_handler.rb
new file mode 100644
index 000000000..535c4b82e
--- /dev/null
+++ b/lib/yard/handlers/c/directive_parser_handler.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+class YARD::Handlers::C::DirectiveParserHandler < YARD::Handlers::C::Base
+  handles(/./)
+  statement_class Comment
+
+  process do
+    Docstring.parser.parse(statement.source, namespace, self)
+  end
+end
diff --git a/lib/yard/parser/c/c_parser.rb b/lib/yard/parser/c/c_parser.rb
index 8518d811e..c8f09799c 100644
--- a/lib/yard/parser/c/c_parser.rb
+++ b/lib/yard/parser/c/c_parser.rb
@@ -97,7 +97,14 @@ def consume_body_statements
           stmts = []
           brace_level = 1
           loop do
-            strip_non_statement_data
+            # Need to include the @! directive tags for them to be processed
+            # along with the body statement content.
+            # (At least the @!group directive.)
+            directive_tags = capture_directive_tags_hack do
+              strip_non_statement_data
+            end
+            stmts.concat(directive_tags)
+
             start = @index
             line = @line
             consume_until(/[{};]/)
@@ -115,6 +122,15 @@ def consume_body_statements
           stmts
         end
 
+        def capture_directive_tags_hack
+          temp = @statements.dup
+          yield if block_given?
+          stmts = (@statements - temp)
+          stmts.select do |stmt|
+            stmt.is_a?(Comment) && stmt.source.start_with?('@!')
+          end
+        end
+
         def strip_non_statement_data
           start = @index
           loop do
diff --git a/spec/parser/c_parser_spec.rb b/spec/parser/c_parser_spec.rb
index bf11e7382..7c423c3d9 100755
--- a/spec/parser/c_parser_spec.rb
+++ b/spec/parser/c_parser_spec.rb
@@ -125,6 +125,44 @@ def parse(contents)
       end
     end
 
+    describe "Group directives" do
+      it "groups constants" do
+        parse <<-eof
+          void Init_Mask(void)
+          {
+              rb_cExample = rb_define_class("Example", rb_cObject);
+
+              // @!group Foos
+
+              /* 1: Foobar description. */
+              rb_define_const(rb_cExample, "FOOBAR", INT2NUM(1));
+
+              /* 2: Foobiz description. */
+              rb_define_const(rb_cExample, "FOOBIZ", INT2NUM(2));
+
+              // @!endgroup
+
+              /* 3: Hello description. */
+              rb_define_const(rb_cExample, "HELLO", INT2NUM(3));
+          }
+        eof
+        constant = Registry.at('Example::FOOBAR')
+        expect(constant.value).to eq '1'
+        expect(constant.docstring).to eq "Foobar description."
+        expect(constant.group).to eq "Foos"
+
+        constant = Registry.at('Example::FOOBIZ')
+        expect(constant.value).to eq '2'
+        expect(constant.docstring).to eq "Foobiz description."
+        expect(constant.group).to eq "Foos"
+
+        constant = Registry.at('Example::HELLO')
+        expect(constant.value).to eq '3'
+        expect(constant.docstring).to eq "Hello description."
+        expect(constant.group).to eq nil
+      end
+    end
+
     describe "Macros" do
       it "handles param## inside of macros" do
         thr = Thread.new do
diff --git a/spec/parser/ruby/ruby_parser_spec.rb b/spec/parser/ruby/ruby_parser_spec.rb
index 2ba9006f8..646597726 100644
--- a/spec/parser/ruby/ruby_parser_spec.rb
+++ b/spec/parser/ruby/ruby_parser_spec.rb
@@ -504,5 +504,38 @@ def bar; end if true
     it "handles compile errors" do
       expect { stmt(":~$ Do not clobber") }.to raise_error(Parser::ParserSyntaxError)
     end
+
+    it "groups constants" do
+      YARD.parse_string <<-eof
+        class Example
+          # @!group Foos
+
+          # Foobar description.
+          FOOBAR = 1
+
+          # Foobiz description.
+          FOOBIZ = 2
+
+          # @!endgroup
+
+          # Hello description.
+          HELLO = 3
+        end
+      eof
+      constant = Registry.at('Example::FOOBAR')
+      expect(constant.value).to eq '1'
+      expect(constant.docstring).to eq "Foobar description."
+      expect(constant.group).to eq "Foos"
+
+      constant = Registry.at('Example::FOOBIZ')
+      expect(constant.value).to eq '2'
+      expect(constant.docstring).to eq "Foobiz description."
+      expect(constant.group).to eq "Foos"
+
+      constant = Registry.at('Example::HELLO')
+      expect(constant.value).to eq '3'
+      expect(constant.docstring).to eq "Hello description."
+      expect(constant.group).to eq nil
+    end
   end
 end if HAVE_RIPPER