Repository: symfony/symfony
File: src/Symfony/Component/DependencyInjection/ContainerBuilder.php (PHP)

Code before:
            if ($definition->hasTag($name) && !$definition->hasTag('container.excluded')) {
                if ($throwOnAbstract && $definition->isAbstract()) {
                    throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
                }
                $tags[$id] = $definition->getTag($name);
            }
        }

        return $tags;
    }

    /**
     * Find definitions of value-object classes by tag, and add the "container.excluded" tag to them.
     *
     * @param bool $autoExclude Adds the "container.excluded" tag to the returned definitions
     *
     * @return array<string, array> An array of tags with the tagged classes as key, holding a list of attribute arrays
     */
    public function findTaggedValueClasses(string $name, bool $autoExclude = true): array
    {
        $this->usedTags[] = $name;
        $tags = [];
        foreach ($this->getDefinitions() as $id => $definition) {
            if ($definition->hasTag($name)) {
                if ($autoExclude) {
                    $definition->addTag('container.excluded', ['source' => \sprintf('by tag "%s"', $name)]);
                }

                $tags[$id] = $definition->getTag($name);
            }
        }

        return $tags;
    }

    /**
     * Returns all tags the defined services use.
     *
     * @return string[]
     */
    public function findTags(): array
    {
        $tags = [];
        foreach ($this->getDefinitions() as $id => $definition) {
            $tags[] = array_keys($definition->getTags());
        }

        return array_unique(array_merge([], ...$tags));
    }

    /**

Review comment (refactor):
let's also make the definition abstract as we do in FileLoader?
```suggestion
                    $definition->setAbstract(true)
                        ->addTag('container.excluded', ['source' => \sprintf('by tag "%s"', $name)]);
```

Code after:
            if ($definition->hasTag($name) && !$definition->hasTag('container.excluded')) {
                if ($throwOnAbstract && $definition->isAbstract()) {
                    throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
                }
                $tags[$id] = $definition->getTag($name);
            }
        }

        return $tags;
    }

    /**
     * Returns service ids for a given tag, asserting they have the "container.excluded" tag.
     *
     *  Example:
     *
     *      $container->register('foo')->addExcludeTag('my.tag', ['hello' => 'world'])
     *
     *      $serviceIds = $container->findExcludedServiceIds('my.tag');
     *      foreach ($serviceIds as $serviceId => $tags) {
     *          foreach ($tags as $tag) {
     *              echo $tag['hello'];
     *          }
     *      }
     *
     * @return array<string, array> An array of tags with the tagged service as key, holding a list of attribute arrays
     */
    public function findExcludedServiceIds(string $tagName): array
    {
        $this->usedTags[] = $tagName;
        $tags = [];
        foreach ($this->getDefinitions() as $id => $definition) {
            if ($definition->hasTag($tagName)) {
                if (!$definition->hasTag('container.excluded')) {
                    throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" is missing the "container.excluded" tag.', $id, $tagName));
                }
                $tags[$id] = $definition->getTag($tagName);
            }
        }

        return $tags;
    }

    /**
     * Returns all tags the defined services use.
     *
     * @return string[]
     */
    public function findTags(): array
    {
        $tags = [];