Skip to content

[Bug]: Code formatting sometimes inserts extra "end" statements #1297

@tipeter

Description

@tipeter

Environment

  • OS and Version: Linux Mint 22.3
  • IDE Version: VS Code 1.109.5
  • Ada & SPARK Extension Version: 2026.1.202601121

Bug Summary and Reproducer

Bug Summary: When formatting inserted code snippets, in some cases extra "end if;", "end loop;" etc. are added at the end of blocks.

Steps to reproduce: Insert the code snippet and select the "Format Document" menu item (Ctrl+Shift+I).

Expected behavior: Format the code so that its content remains unchanged.

Configuration and Logs

tasks.json:
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "ada",
            "command": "gprbuild",
            "args": [
                "${command:ada.gprProjectArgs}",
                "-cargs:ada",
                "-gnatef"
            ],
            "problemMatcher": [
                "$ada-error",
                "$ada-warning",
                "$ada-info"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "label": "ada: Build current project"
        }
    ]
}

launch.json:
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "cppdbg",
            "name": "Ada: Debug main - src/format_test.adb",
            "request": "launch",
            "cwd": "${workspaceFolder}",
            "program": "${workspaceFolder}/bin/format_test",
            "stopAtEntry": false,
            "externalConsole": false,
            "args": [],
            "MIMode": "gdb",
            "preLaunchTask": "ada: Build current project",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "Disable questions on multiple matches",
                    "text": "set multiple-symbols cancel",
                    "ignoreFailures": true
                }
            ],
            "miDebuggerPath": "/home/ptihanyi/.local/share/alire/toolchains/gnat_native_15.2.1_4640d4b3/bin/gdb",
            "sourceFileMap": {
                "/home/runner/work/GNAT-FSF-builds/GNAT-FSF-builds/sbx/x86_64-linux/gcc/build/gcc/ada/rts/": "/home/ptihanyi/.local/share/alire/toolchains/gnat_native_15.2.1_4640d4b3/lib/gcc/x86_64-pc-linux-gnu/15.2.0/adainclude"
            }
        }
    ]
}

Other VS Code Extensions

No response

Additional context

The code obviously won't build even if it's formatted correctly, as this is just a minimal test environment to demonstrate the behavior.

Before formatting:

procedure Format_Test is
   procedure Basic_FIR_Optimized
  (Coeffs : in DSP_API.Signal_Array;
   X      : in DSP_API.Signal_Array;
   Y      : out DSP_API.Signal_Array)
is
   M   : constant Positive := Coeffs'Length;
   Acc : Float;
begin
   -- STARTUP: first M-1 samples need bounds check
   for I in X'First .. Integer'Min(X'Last, X'First + M - 2) loop
      Acc := 0.0;
      for J in Coeffs'Range loop
         if I - J + Coeffs'First >= X'First then
            Acc := Acc + X(I - J + Coeffs'First) * Coeffs(J);
         end if;
      end loop;
      Y(I) := Acc;
   end loop;

   -- STEADY-STATE: no branch, compiler can vectorize this
   for I in X'First + M - 1 .. X'Last loop
      Acc := 0.0;
      for J in Coeffs'Range loop
         Acc := Acc + X(I - J + Coeffs'First) * Coeffs(J);
      end loop;
      Y(I) := Acc;
   end loop;
end Basic_FIR_Optimized;

begin
   null;
end Format_Test;

After formatting:
Notice the double "end loop;"

procedure Format_Test is
   procedure Basic_FIR_Optimized
     (Coeffs : in DSP_API.Signal_Array;
      X      : in DSP_API.Signal_Array;
      Y      : out DSP_API.Signal_Array)
   is
      M   : constant Positive := Coeffs'Length;
      Acc : Float;
   begin
      -- STARTUP: first M-1 samples need bounds check
      for I in X'First .. Integer'Min (X'Last, X'First + M - 2) loop
         Acc := 0.0;
         for J in Coeffs'Range loop
            if I - J + Coeffs'First >= X'First then
               Acc := Acc + X (I - J + Coeffs'First) * Coeffs (J);
            end if;
         end loop;
         Y (I) := Acc;
      end loop;
      end loop;

      -- STEADY-STATE: no branch, compiler can vectorize this
      for I in X'First + M - 1 .. X'Last loop
         Acc := 0.0;
         for J in Coeffs'Range loop
            Acc := Acc + X (I - J + Coeffs'First) * Coeffs (J);
         end loop;
         Y (I) := Acc;
      end loop;
   end Basic_FIR_Optimized;

begin
   null;
end Format_Test;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions