Skip to content

Rector rules and tooling based to create rectors base on dbuytaert/drupal-digests #2

Open
bbrala wants to merge 201 commits into
mainfrom
feature/digest-rectors
Open

Rector rules and tooling based to create rectors base on dbuytaert/drupal-digests #2
bbrala wants to merge 201 commits into
mainfrom
feature/digest-rectors

Conversation

@bbrala
Copy link
Copy Markdown
Owner

@bbrala bbrala commented Apr 30, 2026

Overview

This PR implements Drupal deprecation rules for drupal-rector, covering Drupal 10 and Drupal 11 (11.1–11.4). It adds 52 new Drupal 11 rector classes, 4 new Drupal 10 rector classes, and 2 new generic data-driven rectors, all wired into the per-version deprecation config files. Each rule targets a specific Drupal change record and includes type-guarded transformations and version-gating tests.

The work originated as an experiment converting drupal-digests AI-generated rules into fully drupal-rector-compliant implementations — validating the conversion workflow and tooling in the process.

Also worked on a better way to control if/how BC code paths are implemented.

This PR also removed rector 1 support. Which is a little before Drupal 10 EOL in august, but made life a lot easier.


Infrastructure improvements

Generic data-driven rectors

  • FunctionCallRemovalRector (src/Rector/Deprecation/) — removes calls to deprecated functions with no replacement; handles 11 deprecated functions across Drupal 11.2–11.4
  • FunctionToFirstArgMethodRector — refactored to src/Rector/Deprecation/ as a shared base for Drupal 8/9/10/11 use

BC wrapping extended

Previously, backwardsCompatibleCall() wrapping in AbstractDrupalCoreRector only applied when both old and new nodes were of matching types. Extended to cover all Expr node types so mixed-type rewrites (e.g. function call → method call) also get proper BC wrapping.

Version-gating tests

Added DrupalRectorSettings::setDrupalVersion() (via static::getContainer()->make(DrupalRectorSettings::class)) for clean version-specific test scenarios. Replaces the previous namespaced-class \Drupal hack that was causing test brittleness.

DrupalRectorSettings is a container-managed service that centralises the runtime settings rectors need to make version-aware decisions. It holds three values: whether backwards-compatibility wrapping is enabled, the minimum Drupal core version the project still supports, and an optional Drupal version override.

Config-driven refactoring

Three custom constant-replacement rectors were replaced with ConstantToClassConstantRector configuration entries, reducing custom code:

  • ReplaceRequirementSeverityConstantsRector → config
  • ReplaceJsonApiFilterConstantsRector → config
  • ReplaceNodeSetPreviewModeRector for the enum path → config

Tooling (.claude/skills/)

Adds three Claude Code skills used to drive the conversion workflow:

  • rector-discover lists unimplemented drupal-digests rules by implementation phase; regenerates docs/rector-index.yml when stale
  • rector-implement AI prompt that converts a drupal-digests rule into a fully compliant rector class, test, fixture, and config entry
  • rector-qa QA checklist and fixture expansion workflow for completed rectors

New Drupal 11 rules

11.1

  • AliasManager::pathAliasWhitelistRebuild()pathAliasPrefixListRebuild() (via RenameMethodRector config)
  • ReplaceCommentManagerGetCountNewCommentsRector — replaces CommentManagerInterface::getCountNewComments() with a service call (issue #3543035)

11.2

  • template_preprocess_{container,html,page,links,time,datetime_form,datetime_wrapper}()ThemePreprocess* attribute hooks (DeprecatedFilterFunctionsRector and related)
  • CacheBackendInterface::invalidateAll()deleteAll()
  • StatementPrefetchIteratorFetchColumnRectorfetchColumn()fetchField(), skipping PDO's native $column arg form
  • RemoveModuleHandlerAddModuleCallsRector — removes no-op addModule()/addProfile() calls
  • RemoveHandlerBaseDefineExtraOptionsRector — removes deprecated defineExtraOptions() with no replacement
  • ReplaceAlphadecimalToIntNullRectorNumber::alphadecimalToInt(null/'')0
  • LoadAllIncludesRectorModuleHandler::loadAllIncludes() → explicit foreach getModuleList() + loadInclude() loop
  • RemoveCacheExpireOverrideRector — removes deprecated CACHE_* constant override in settings
  • RemoveConfigSaveTrustedDataArgRector — strips the deprecated $trusted_data arg from Config::save()
  • ReplaceDateTimeRangeConstantsRectorDateTimeRangeItem::DATETIME_TYPE_* constants → DateTimeRangeItem::*
  • Various FunctionCallRemovalRector entries (removed deprecated theme preprocess and update manager functions)

11.3

  • NodeMassUpdateRector (via ReplaceNodeModuleProceduralFunctionsRector) — node_mass_update()NodeBulkUpdate::process()
  • JSONAPI_FILTER_AMONG_* constants → JsonApiFilter::* (via ConstantToClassConstantRector config, issue #3560657)
  • StripMigrationDependenciesExpandArgRector — removes deprecated $expand_dependencies argument

11.4

  • FunctionToServiceRector config for 29 deprecated procedural functions across 9 modules (issue numbers in config)
  • RemoveLinkWidgetValidateTitleElementRector — validation moved to element-level, call removed
  • RemoveAutomatedCronSubmitHandlerRector — removes deprecated $form['#submit'][] = 'automated_cron_settings_submit'
  • DRUPAL_DISABLED/OPTIONAL/REQUIRED in NodeTypeInterface::setPreviewMode()PreviewMode::* enum (via ConstantToClassConstantRector config)
  • ReplaceRecipeRunnerInstallModuleRectorRecipeRunner::installModules()RecipeRunner::processRecipe()
  • ReplaceEntityReferenceRecursiveLimitRectorEntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT → literal 20
  • RemoveUpdaterPostInstallMethodsRector — removes deprecated postInstall()/postUpdate() Updater calls
  • ReplaceSystemPerformanceGzipKeyRectorsystem.performance css.gzip/js.gzip keys → gzip
  • ReplaceViewsProceduralFunctionsRectorviews_embed_view(), views_get_view_result() etc. → service calls

11.x (multi-version)

  • NodeStorageDeprecatedMethodsRectorrevisionIds()/userRevisionIds()/countDefaultLanguageRevisions() → entity query chains (11.1)
  • PluginBaseIsConfigurableRectorisConfigurable()instanceof ConfigurableInterface (11.2)
  • ReplaceSessionManagerDeleteRectorSessionManager::delete($uid)UserSessionRepository::deleteByUid() (11.3)
  • ReplaceEntityOriginalPropertyRector$entity->originalgetOriginal()/setOriginal(), with NullsafePropertyFetch support (11.1)
  • ReplaceEditorLoadRectoreditor_load()entityTypeManager()->getStorage('editor')->load() (11.4)
  • ReplaceThemeGetSettingRectortheme_get_setting()ThemeSettingsProvider service (11.3)
  • ReplaceSessionWritesWithRequestSessionRector — direct $_SESSION writes → RequestSession service
  • ReplaceUserSessionNamePropertyRector$this->name in UserSession subclasses → $this->getAccountName() (with $this guard to prevent recursion)
  • MigrateSqlGetMigrationPluginManagerRector$this->getMigrationPluginManager()$this->migrationPluginManager property, scoped to Drupal\migrate\Plugin\migrate\id_map\Sql
  • UseEntityTypeHasIntegerIdRector$entityType->getKey('id') === 'nid' pattern → hasIntegerId()
  • ReplaceNodeAddBodyFieldRectornode_add_body_field() → direct field config API
  • ReplaceNodeAccessViewAllNodesRectornode_access_view_all_nodes()AccessResult::allowed()
  • ReplaceFieldgroupToFieldsetRector — fieldgroup display component migration
  • ReplaceLocaleConfigBatchFunctionsRectorlocale_config_batch_refresh_name() etc. → service calls
  • ReplacePdoFetchConstantsRector\PDO::FETCH_* constants in Drupal DB context → Fetch::* enum values
  • ReplaceJsonApiFilterConstantsRector → (refactored to ConstantToClassConstantRector config)
  • RemoveStateCacheSettingRector — removes $settings['state_cache'] (permanently enabled since 11.0)
  • RemoveModuleHandlerDeprecatedMethodsRector — other deprecated ModuleHandler methods
  • RemoveSetUriCallbackRector — removes setUriCallback() standalone and mid-chain
  • RemoveRootFromConvertDbUrlRector — strips deprecated $root arg from Database::convertDbUrlToConnectionInfo()
  • RemoveTrustDataCallRector, RemoveTwigNodeTransTagArgumentRector, RemoveFilterTipsLongParamRector
  • RemoveViewsRowCacheKeysRector, RenameStopProceduralHookScanRector
  • ReplaceRequirementSeverityConstantsRectorREQUIREMENT_* globals → RequirementSeverity::* enum
  • Various class renames (migrate_drupal ContentEntity/Deriver, locale translation files) via RenameClassRector config
  • ReplaceFileGetContentHeadersRectorfile_get_content_headers($file)$file->getDownloadHeaders()
  • FilterFormatFunctionsToServiceRector, DeprecatedFilterFunctionsRector, FileManagedFileSubmitRector, MediaFilterFormatEditFormValidateRector, NodeAccessRebuildFunctionsRector, ErrorCurrentErrorHandlerRector, FileSystemBasenameToNativeRector, ReplaceTwigExtensionRector

New Drupal 10 rules

  • ReplaceModuleHandlerGetNameRectorModuleHandlerInterface::getName($module)\Drupal::service('extension.list.module')->getName($module) (issue #3571063)
  • ReplaceRebuildThemeDataRectorThemeHandlerInterface::rebuildThemeData()ThemeExtensionList::reset()->getList()
  • ReplaceRequestTimeConstantRectorREQUEST_TIME constant → \Drupal::time()->getRequestTime()
  • VersionedFunctionToServiceRector + VersionedFunctionToServiceConfiguration — version-gated variant of FunctionToServiceRector for cases where the service name depends on the Drupal target version

Testing

  • Each new rector has a fixture test with happy-path and at least one no_change case
  • QA pass expanded fixture coverage for 30+ rectors: added edge cases for FQCN-prefixed calls, self::/static:: forms, method-call-as-argument, class-property usage, and unrelated-class no-change guards
  • Several bugs were caught and fixed during QA (see individual fix commits)

bbrala added 30 commits April 30, 2026 12:53
Creates src/Drupal11/Rector/Deprecation/ and tests/src/Drupal11/Rector/Deprecation/
directory structure for incoming Drupal 11 deprecation rules. Adds stub
Drupal11SetList.php following the established Drupal10SetList pattern.
Captures the structural differences between drupal-digests AI-generated rules
and drupal-rector conventions: namespace, base class selection, BC wrapping
decision tree, fixture format, test config patterns, and multi-node-type handling.
Reusable 14-step AI prompt that converts any drupal-digests rector rule into a
fully drupal-rector-compliant implementation: namespaced rule class, test class,
fixture file, and test config. Includes BC decision tree, class templates for both
AbstractRector and AbstractDrupalCoreRector patterns, and a verification checklist.
…ecationsRector

Validates the conversion workflow on two real drupal-digests rules:
- FormLocationRector (issue #3550054): ClassConstFetch replacement, no BC wrapping,
  replaces deprecated CommentItemInterface constants with FormLocation enum cases.
- LanguageModuleFunctionDeprecationsRector (issue #3574727): FuncCall → StaticCall/MethodCall,
  BC-wrapped via AbstractDrupalCoreRector, replaces deprecated language.module functions
  with OOP equivalents.

Updates Drupal stub VERSION from 10.99.x-dev to 11.99.x-dev so Drupal11 rules fire in
tests. All 95 tests pass (93 existing + 2 new).
Implements Drupal 11.4.0 deprecation fixes using existing data-driven rectors
rather than custom classes, following the pattern established by Drupal 8/9/10:

- CommentItemInterface::FORM_BELOW/FORM_SEPARATE_PAGE → Drupal\comment\FormLocation
  (ClassConstantToClassConstantRector, no BC wrap — ClassConstFetch is not CallLike)
- language_configuration_element_submit → LanguageConfiguration::submit
  (FunctionToStaticRector, BC-wrapped for 11.4.0)
- language_process_language_select → \Drupal::service('…')->processLanguageSelect()
  (FunctionToServiceRector, BC-wrapped for 11.4.0)

Also scaffolds src/Drupal11/ and tests/src/Drupal11/ directories and adds
Drupal11SetList stub, plus digest-to-rector conversion prompt and mapping docs.
Bumps Drupal VERSION stub to 11.99.x-dev so Drupal11 rules fire in tests.
… for version-specific test scenarios

Replaces the broken namespaced-class Drupal hack in BackwardsCompatibilityActionAnnotationToAttributeRectorTest
with a proper setUp/tearDown pattern. Updates docs to describe the new mechanism.
…Rector

Previously backwardsCompatibleCall wrapping only applied when both the
old and new nodes were CallLike (FuncCall, MethodCall, StaticCall, etc.).
The restriction was in AbstractDrupalCoreRector, not in DeprecationHelper
itself, which accepts any callable.

Broaden the guard to Node\Expr so that non-CallLike transformations such
as ClassConstFetch → ClassConstFetch and ClassConstFetch → PropertyFetch
also get BC-wrapped when the introduced version supports it.

Add a test stub rector (ClassConstFetchBCRector) and fixture to cover
the new Expr → Expr path.
…eplacements

FileSystemInterface::EXISTS_RENAME, EXISTS_REPLACE, and EXISTS_ERROR were
deprecated in drupal:10.3.0 and removed in drupal:12.0.0 (issue #3575575).
Replaced by the \Drupal\Core\File\FileExists backed enum.
- template_preprocess_{container,html,page,links,time,datetime_form,
  datetime_wrapper}() → ThemePreprocess/DatePreprocess service methods
  (issue #3501136)
- SystemManager::REQUIREMENT_{OK,WARNING,ERROR} → RequirementSeverity
  enum cases (issue #3575841)
node_mass_update() was deprecated in drupal:11.3.0 and removed in
drupal:13.0.0 (issue #3571623). Replaced by NodeBulkUpdate::process()
via the service container.
…rable rectors

FunctionToServiceRector (29 functions across 9 modules, issue numbers below):
- ckeditor5_filter_format_edit_form_submit, _update_ckeditor5_html_filter
  → Ckeditor5Hooks service (#3566792)
- _dblog_get_message_types, dblog_filters → DbLogFilters service (#3560398)
- contact_user_profile_form_submit, contact_form_user_admin_settings_submit
  → ContactFormHooks service (#3566888)
- 6× content_translation_* → content_translation.manager /
  ContentTranslationEnableTranslationPerBundle / ContentTranslationHooks (#3548571)
- locale_translation_batch_update_build, _batch_fetch_build → LocaleFetch (#3572339)
- 7× locale_translation_* → locale.project / LocaleSource (#3569328)
- 6× menu_ui_* → MenuUiUtility / MenuUiHooks (#3571400)
- text_summary → TextSummary::generate (#3568387)
- user_form_process_password_confirm → UserThemeHooks (#3582106)

FunctionToStaticRector (4 functions):
- views_ui_form_button_was_clicked → ViewsFormHelperTrait::formButtonWasClicked
- views_ui_add_limited_validation, _add_ajax_wrapper, _nojs_submit
  → ViewsFormAjaxHelperTrait (#3035340)

ClassConstantToClassConstantRector (6 constants):
- CommentItemInterface::HIDDEN/CLOSED/OPEN → CommentingStatus enum (#3574661)
- CommentInterface::ANONYMOUS_* → AnonymousContact enum (#3574661)
Replaces deprecated ModuleHandlerInterface::getName($module) calls with
\Drupal::service('extension.list.module')->getName($module), BC-wrapped
for sites running Drupal 10.3+.
…ist rename

Adds AliasManager::pathAliasWhitelistRebuild() -> pathAliasPrefixListRebuild()
rename (drupal:11.1.0, issue #3151086). Also registers DRUPAL_111/112/113
constants in Drupal11SetList.
…n rule

Replaces invalidateAll() with deleteAll() on CacheBackendInterface objects
(drupal:11.2.0, issue #3498947).
…r issue #3543035

Replaces CommentManagerInterface::getCountNewComments() with
\Drupal::service(\Drupal\history\HistoryManager::class)->getCountNewComments(),
BC-wrapped for drupal:11.3.0.
…6062

Replaces NodeStorage::revisionIds() and userRevisionIds() with equivalent
entity query chains (drupal:11.3.0, removed in drupal:13.0.0).
Replaces deprecated PluginBase::isConfigurable() calls with
instanceof \Drupal\Component\Plugin\ConfigurableInterface checks
(drupal:11.1.0, removed in drupal:12.0.0).
Replaces SessionManager::delete($uid) with
\Drupal::service(UserSessionRepositoryInterface::class)->deleteAll($uid),
BC-wrapped for drupal:11.4.0.
…sue #3490200

Renames fetchColumn() to fetchField() on database statements, skipping
PDO's native $this->clientStatement->fetchColumn() (drupal:11.2.0).
Replaces ModuleHandler::loadAllIncludes() with an explicit foreach loop
over getModuleList() + loadInclude() (drupal:11.3.0, removed in 13.0.0).
…Rector

- ReplaceRebuildThemeDataRector: replaces ThemeHandlerInterface::rebuildThemeData()
  with extension.list.theme service chain (drupal:10.3.0, issue #3571068)
- ViewsPluginHandlerManagerRector: replaces Views::pluginManager() and
  Views::handlerManager() with Drupal::service() equivalents (drupal:11.4.0, issue #3566424)
…all removal

Handles 11 deprecated Drupal functions across versions 11.2–11.4 that have been
removed with no direct replacement, using a configurable accumulation pattern
so multiple version config files can each add their function list.
…ting removal

$settings['state_cache'] deprecated in drupal:11.0.0 — state caching is permanently
enabled. Adds drupal-11.0-deprecations.php config file and DRUPAL_110 set list constant.
ModuleHandlerInterface::addModule() and addProfile() are no-ops since drupal:11.2.0
and removed in drupal:12.0.0. Uses isObjectType() type check to target only calls
on ModuleHandlerInterface implementors.
LinkWidget::validateTitleElement() deprecated in drupal:11.4.0, removed in drupal:12.0.0.
Validation is now handled by LinkTitleRequiredConstraint on the LinkItem field type.
\$form['#submit'][] = 'automated_cron_settings_submit' deprecated in drupal:11.4.0,
removed in drupal:13.0.0. Config saving is now handled automatically via
#config_target on the interval element.
- drupal-11.2: FunctionCallRemovalRector (template_preprocess, 5 update manager funcs),
  RemoveModuleHandlerAddModuleCallsRector
- drupal-11.3: FunctionCallRemovalRector (block_content_add_body_field)
- drupal-11.4: FunctionCallRemovalRector (views_ui contextual suppress funcs,
  automated_cron_settings_submit), RemoveLinkWidgetValidateTitleElementRector,
  RemoveAutomatedCronSubmitHandlerRector
REQUEST_TIME constant was deprecated in drupal:8.3.0 and removed in drupal:11.0.0.
Replaces all uses with \Drupal::time()->getRequestTime().
JSONAPI_FILTER_AMONG_* global constants deprecated in drupal:11.3.0, removed in
drupal:13.0.0. Replaced by \Drupal\jsonapi\JsonApiFilter::AMONG_* class constants.
bbrala added 11 commits May 10, 2026 08:16
… #3196937

Replaces the deprecated string $values argument in
BlockContentTestBase::createBlockContentType() with an array
(['id' => 'basic']) as required since drupal:11.1.0.
…ractRector

Array signature predates 11.1.0 so the replacement is version-agnostic;
no BC wrapping needed. Remove fixture-below-version and simplify test class.
Converts deprecated $this->movePointerTo('#css-id') calls to the
equivalent $this->getSession()->getDriver()->mouseOver('.//*[@id="..."]')
chain. Handles simple CSS ID selectors only; complex selectors are left
untouched. Includes a stub for LayoutBuilderDisableInteractionsTest.
…#3511123

Removes deprecated CacheTagChecksumCount and CacheTagIsValidCount keys from
assertMetrics() calls (deprecated drupal:11.2.0, removed drupal:12.0.0).
Includes PerformanceTestTrait stub for isObjectType() guard.
@bbrala bbrala changed the title Feature/digest rectors Rector rules and tooling based to create rectors base on dbuytaert/drupal-digests May 11, 2026
bbrala added 18 commits May 11, 2026 13:25
…ro-match modules

- Replace --only/--no-cache with a dedicated rector-live-test.php config file
  that always includes fileExtensions() — Rector silently skips .module and
  .install files without it, making misses impossible to distinguish from
  true no-ops
- Make zero-match diagnosis mandatory: grep the call site, show ±8 lines of
  surrounding code, identify the cause, report inline
- Update causes table with "How to spot it" and "Verdict" columns, add rows
  for chained unresolvable returns, broken use imports, and already-migrated modules
…ssue #3557461

Replaces deprecated EntityTypeInterface::getOriginalClass() with
getDecoratedClasses()[0]. The new method was introduced alongside the
deprecation in Drupal 11.4, so the rector uses AbstractDrupalCoreRector
for BC wrapping.

https://www.drupal.org/node/3557461
https://www.drupal.org/node/3587853
…x script missed a few. Script was expanded to look a little more loosely.
…extual_links #2571679

views_add_contextual_links() deprecated in drupal:11.4.0, removed in
drupal:13.0.0. Replaced by \Drupal\views\ContextualLinksHelper::addLinks().
Replace deprecated system_region_list() and system_default_region() with
Theme object methods via \Drupal::service('theme_handler')->getTheme().
…iceRector config #3533083

The FunctionToServiceConfiguration entry was missing the 5th `true` argument,
causing the output to use a string service ID instead of the FQCN ::class form
as specified in the deprecation and the digest rule.
…manage_field_form_submit #3567163

No contrib modules found using this function — it was field_ui's own submit handler,
not called directly by third-party code.
…dle functions #3495966

entity_test_create_bundle() and entity_test_delete_bundle() deprecated in
drupal:11.2.0 and removed in drupal:12.0.0. Replaced by static methods on
\Drupal\entity_test\EntityTestHelper.

The entity_test module is widely used as a test dependency across contrib,
so this rule covers a broad set of test code migrations.

Tested: scheduler_field (1 file changed), search_api (already migrated)
…oServiceRector config #3533083

Update config comment and test config comment from #3571623 to the
correct issue #3533083 (node_mass_update → NodeBulkUpdate service).
…ceduralFunctionsRector

node_mass_update is already covered by FunctionToServiceRector config (#3533083).
Having both handle the same function was redundant — whichever ran first won and
the other was a no-op. Remove the case from the custom multi-function rector and
its two associated test fixtures.
…pe_get_description #3531944

node_type_get_description($node_type) deprecated in drupal:11.3.0, removed in
drupal:12.0.0. Replaced by $node_type->getDescription().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant