Skip to content

fix: устранить S2583 reliability-issue и почистить nullability/scope-аннотации#4003

Merged
nixel2007 merged 3 commits into
developfrom
fix/missed-required-parameter-nullable-docall
Jun 4, 2026
Merged

fix: устранить S2583 reliability-issue и почистить nullability/scope-аннотации#4003
nixel2007 merged 3 commits into
developfrom
fix/missed-required-parameter-nullable-docall

Conversation

@nixel2007

@nixel2007 nixel2007 commented Jun 4, 2026

Copy link
Copy Markdown
Member

Зачем

Quality gate на develop падал из-за new_reliability_rating = C — единственная reliability-issue в новом коде: S2583 в MissedRequiredParameterDiagnostic («condition always evaluates to false»). По ходу разбора заодно убраны связанные шероховатости с nullability- и scope-аннотациями.

Что сделано

1. Фикс S2583 (reliability) — fix(diagnostics)

@Nullable на параметрах arguments(...)/appendMethodCall(...). doCall() у ANTLR реально может быть null — и легитимно (опциональный doCall? в newExpression, например Новый Массив без скобок), и при восстановлении парсера после ошибок даже для обязательных правил. Null-guard нужен; аннотация документирует это и снимает ложное срабатывание, не удаляя защитную проверку.

2. Миграция @Nullable на JSpecify — refactor

jakarta.annotation.Nullable / javax.annotation.Nullableorg.jspecify.annotations.Nullable (стандарт проекта, 110+ использований). Аннотации переведены в TYPE_USE-позицию.

3. Мета-аннотация @WorkspaceScoperefactor(infrastructure)

Композитная замена повторяющегося @Scope(value = WorkspaceScope.SCOPE_NAME, proxyMode = ScopedProxyMode.TARGET_CLASS) (~30 бинов). proxyMode через @AliasFor (по умолчанию TARGET_CLASS); interface-proxied @Bean-executors используют @WorkspaceScope(proxyMode = ScopedProxyMode.INTERFACES). Класс-реализация Spring Scope переименован WorkspaceScopeWorkspaceBeanScope, чтобы освободить имя под аннотацию.

Проверки

  • compileJava / compileTestJava — зелёные
  • spotlessJavaCheck — зелёный
  • TypeServiceTest (TARGET_CLASS-бины) и AnalyzeProjectOnStartTest (per-workspace INTERFACES-executors) — проходят, подтверждая, что Spring корректно понимает мета-аннотацию в рантайме

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Improved workspace bean dependency injection scoping mechanism for better performance and maintainability.
    • Standardized nullability annotations across the codebase to enhance code quality and IDE support.
    • Streamlined component configuration for workspace-scoped services.

nixel2007 and others added 3 commits June 4, 2026 08:17
…Diagnostic

ANTLR doCall() can be null — both legitimately for the optional `doCall?`
branch of newExpression (e.g. `Новый Массив` without parentheses) and during
parser error recovery for mandatory rule references. Annotating the parameters
@nullable documents this and resolves the S2583 reliability false positive
(condition "always false") without dropping the required defensive null guard.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Replace jakarta.annotation.Nullable and javax.annotation.Nullable with the
project's standard org.jspecify.annotations.Nullable across TypeService,
DereferenceMemberMatcher, WorkspaceScope, PlatformMemberSymbol and
WorkspaceContextHolder. JSpecify @nullable is TYPE_USE, so annotations are
moved into type-use position (e.g. `private @nullable URI`).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Replace the repeated
`@Scope(value = WorkspaceScope.SCOPE_NAME, proxyMode = ScopedProxyMode.TARGET_CLASS)`
boilerplate (used on ~30 beans) with a composed `@WorkspaceScope`
meta-annotation. The proxy mode is exposed via @aliasfor (default
TARGET_CLASS), so the few interface-proxied @bean executors use
`@WorkspaceScope(proxyMode = ScopedProxyMode.INTERFACES)`.

The Spring Scope SPI implementation is renamed WorkspaceScope ->
WorkspaceBeanScope to free the `WorkspaceScope` name for the annotation;
SCOPE_NAME stays on the implementation and is referenced by the meta-annotation.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors Spring workspace-scoped bean management by extracting the scope implementation into a dedicated WorkspaceBeanScope class and converting the original WorkspaceScope into a custom annotation. The change is applied across 30+ workspace-scoped classes throughout the codebase, with additional standardization of nullability imports to org.jspecify.annotations.Nullable.

Changes

Workspace Scope Refactoring

Layer / File(s) Summary
WorkspaceBeanScope implementation and scope registration
src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceBeanScope.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceScopeConfiguration.java
New WorkspaceBeanScope class implementing Spring's Scope interface provides per-workspace bean instance management with deadlock-safe creation, destruction callback handling, and workspace URI tracking via concurrent maps. WorkspaceScopeConfiguration wires the scope into Spring's custom scope registry.
@WorkspaceScope annotation and context holder nullability updates
src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceScope.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceContextHolder.java
WorkspaceScope.java converted from Spring Scope implementation to custom @WorkspaceScope annotation with proxyMode() attribute and @AliasFor wiring. WorkspaceContextHolder updated to use org.jspecify.annotations.Nullable for consistent nullability semantics.
ServerContextProvider workspace scope injection
src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContextProvider.java
Constructor and field updated to inject WorkspaceBeanScope instead of old WorkspaceScope type, ensuring correct removeWorkspace(uri) method is called during workspace cleanup.
Workspace-scoped repositories and indices migration
src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/AnnotationRepository.java, LocationRepository.java, SymbolOccurrenceRepository.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/CallStatementByReceiverIndex.java, InferredVariableTypeIndex.java, SymbolTypeIndex.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptLibraryIndex.java
All repositories and indices replace explicit @Scope(value = WorkspaceScope.SCOPE_NAME, proxyMode = TARGET_CLASS) with @WorkspaceScope annotation; unused Spring scope imports removed.
Type services and registry migration
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/DereferenceMemberMatcher.java, TypeService.java, ExpressionTypeInferencer.java, TypeRegistry.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/BslContextHolder.java, BslContextPlatformTypesProvider.java, BuiltinPlatformTypesProvider.java, BuiltinOScriptPlatformTypesProvider.java, ConfigurationModuleMembersProvider.java, ConfigurationTypesProvider.java, GlobalScopeProvider.java, MemberMetadataIndex.java, PlatformContextProviderFactory.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/types/scope/GlobalSymbolScope.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/types/symbol/PlatformMemberSymbol.java
Type-related services and registries consistently migrate from explicit @Scope configuration to @WorkspaceScope annotation; nullability imports updated to org.jspecify.annotations.Nullable in DereferenceMemberMatcher, TypeService, and PlatformMemberSymbol.
Autumn inference components migration
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/autumn/AutumnBeanIndex.java, AutumnCollectionIndex.java, AutumnComponentInferencer.java, AutumnMetaAnnotationResolver.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptModuleMembersProvider.java, OScriptModuleTypeResolver.java
Autumn bean inference components and OScript type providers replace @Scope(..., proxyMode = TARGET_CLASS) with @WorkspaceScope; Spring scope imports removed.
ExecutorConfiguration and diagnostics scoping
src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/ExecutorConfiguration.java, src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/infrastructure/DiagnosticInfos.java
ExecutorConfiguration applies @WorkspaceScope(proxyMode = ...) to per-workspace ForkJoinPool-backed executor beans; DiagnosticInfos switches from explicit @Scope to @WorkspaceScope for per-workspace diagnostic management.
Nullability annotation standardization
src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissedRequiredParameterDiagnostic.java
Added @Nullable import from org.jspecify.annotations; updated appendMethodCall and arguments method parameters to use nullability annotations for doCallContext argument.
Test infrastructure updates
src/test/java/com/github/_1c_syntax/bsl/languageserver/util/AbstractDirtyContextTestExecutionListener.java
Test execution listener updated to retrieve and use WorkspaceBeanScope instead of old WorkspaceScope when enumerating registered URIs and destroying per-workspace bean state during test cleanup cycles.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

hacktoberfest-accepted

Suggested reviewers

  • theshadowco
  • sfaqer

🐰 A rabbit hops through Spring-scoped beans, now annotated with grace,
WorkspaceBeanScope holds each workspace's place,
From scope-class to @WorkspaceScope we trace,
Cleaner, simpler, a more elegant embrace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly identifies the three main changes: fixing S2583 reliability issue, cleaning up nullability annotations, and refactoring scope annotations. The title directly reflects the core objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/missed-required-parameter-nullable-docall

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceBeanScope.java`:
- Around line 77-80: The remove(String name) method removes the bean from store
but leaves its associated entry in destructionCallbacks, causing stale
callbacks; update remove(...) (use resolveKey(), store, destructionCallbacks) to
also remove the callback for the same key/name pair (e.g., fetch
destructionCallbacks.get(key) and remove(name) when present) so that no stale
callback remains and removeWorkspace(...) won't run a left-over callback later.
- Around line 103-108: The removeWorkspace method currently runs all destruction
callbacks via callbacks.values().forEach(Runnable::run) which lets any thrown
exception abort the loop and skip store.remove; change removeWorkspace in
WorkspaceBeanScope so each Runnable from destructionCallbacks is invoked inside
its own try/catch (catch Throwable) to log the error and continue, and ensure
store.remove(workspaceUri) always executes (e.g., run callbacks in a try block
and call store.remove in a finally or simply perform callbacks with per-callback
error handling before removing from store); reference destructionCallbacks,
Runnable callbacks, removeWorkspace, and store to locate the code to modify.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a5022159-9ce7-4688-8d8e-6e0f48b0d56e

📥 Commits

Reviewing files that changed from the base of the PR and between 25e6dc0 and 09b6780.

📒 Files selected for processing (37)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContextProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissedRequiredParameterDiagnostic.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/infrastructure/DiagnosticInfos.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/ExecutorConfiguration.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceBeanScope.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceContextHolder.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceScope.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceScopeConfiguration.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/AnnotationRepository.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/LocationRepository.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/references/model/SymbolOccurrenceRepository.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/DereferenceMemberMatcher.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeService.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/CallStatementByReceiverIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/InferredVariableTypeIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/SymbolTypeIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ExpressionTypeInferencer.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/autumn/AutumnBeanIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/autumn/AutumnCollectionIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/autumn/AutumnComponentInferencer.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/autumn/AutumnMetaAnnotationResolver.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptLibraryIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptModuleMembersProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptModuleTypeResolver.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/BslContextHolder.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/BslContextPlatformTypesProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/BuiltinOScriptPlatformTypesProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/BuiltinPlatformTypesProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/ConfigurationModuleMembersProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/ConfigurationTypesProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/GlobalScopeProvider.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/MemberMetadataIndex.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/PlatformContextProviderFactory.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/scope/GlobalSymbolScope.java
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/symbol/PlatformMemberSymbol.java
  • src/test/java/com/github/_1c_syntax/bsl/languageserver/util/AbstractDirtyContextTestExecutionListener.java

Comment on lines +77 to +80
public @Nullable Object remove(String name) {
var key = resolveKey();
var beans = store.get(key);
return beans != null ? beans.remove(name) : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

remove(...) leaves destruction callbacks behind.

On Line 77, the bean is removed from store, but its callback remains in destructionCallbacks. That stale callback can still run during removeWorkspace(...), causing double-destroy side effects and callback-map growth over time.

Suggested fix
`@Override`
public `@Nullable` Object remove(String name) {
  var key = resolveKey();
  var beans = store.get(key);
- return beans != null ? beans.remove(name) : null;
+ var removed = beans != null ? beans.remove(name) : null;
+ var callbacks = destructionCallbacks.get(key);
+ if (callbacks != null) {
+   callbacks.remove(name);
+ }
+ return removed;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceBeanScope.java`
around lines 77 - 80, The remove(String name) method removes the bean from store
but leaves its associated entry in destructionCallbacks, causing stale
callbacks; update remove(...) (use resolveKey(), store, destructionCallbacks) to
also remove the callback for the same key/name pair (e.g., fetch
destructionCallbacks.get(key) and remove(name) when present) so that no stale
callback remains and removeWorkspace(...) won't run a left-over callback later.

Comment on lines +103 to +108
public void removeWorkspace(URI workspaceUri) {
var callbacks = destructionCallbacks.remove(workspaceUri);
if (callbacks != null) {
callbacks.values().forEach(Runnable::run);
}
store.remove(workspaceUri);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

removeWorkspace(...) should be exception-safe for callback execution.

On Line 106, any callback exception aborts the loop and prevents full cleanup on Line 108. This can leave workspace-scoped beans alive and pollute subsequent runs/tests.

Suggested fix
public void removeWorkspace(URI workspaceUri) {
  var callbacks = destructionCallbacks.remove(workspaceUri);
- if (callbacks != null) {
-   callbacks.values().forEach(Runnable::run);
- }
- store.remove(workspaceUri);
+ RuntimeException failure = null;
+ if (callbacks != null) {
+   for (var callback : callbacks.values()) {
+     try {
+       callback.run();
+     } catch (RuntimeException ex) {
+       if (failure == null) {
+         failure = ex;
+       } else {
+         failure.addSuppressed(ex);
+       }
+     }
+   }
+ }
+ store.remove(workspaceUri);
+ if (failure != null) {
+   throw failure;
+ }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void removeWorkspace(URI workspaceUri) {
var callbacks = destructionCallbacks.remove(workspaceUri);
if (callbacks != null) {
callbacks.values().forEach(Runnable::run);
}
store.remove(workspaceUri);
public void removeWorkspace(URI workspaceUri) {
var callbacks = destructionCallbacks.remove(workspaceUri);
RuntimeException failure = null;
if (callbacks != null) {
for (var callback : callbacks.values()) {
try {
callback.run();
} catch (RuntimeException ex) {
if (failure == null) {
failure = ex;
} else {
failure.addSuppressed(ex);
}
}
}
}
store.remove(workspaceUri);
if (failure != null) {
throw failure;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/infrastructure/WorkspaceBeanScope.java`
around lines 103 - 108, The removeWorkspace method currently runs all
destruction callbacks via callbacks.values().forEach(Runnable::run) which lets
any thrown exception abort the loop and skip store.remove; change
removeWorkspace in WorkspaceBeanScope so each Runnable from destructionCallbacks
is invoked inside its own try/catch (catch Throwable) to log the error and
continue, and ensure store.remove(workspaceUri) always executes (e.g., run
callbacks in a try block and call store.remove in a finally or simply perform
callbacks with per-callback error handling before removing from store);
reference destructionCallbacks, Runnable callbacks, removeWorkspace, and store
to locate the code to modify.

@nixel2007
nixel2007 enabled auto-merge June 4, 2026 06:47
@sonarqubecloud

sonarqubecloud Bot commented Jun 4, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
69.5% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@nixel2007
nixel2007 disabled auto-merge June 4, 2026 07:00
@nixel2007
nixel2007 merged commit b50d75a into develop Jun 4, 2026
37 of 38 checks passed
@nixel2007
nixel2007 deleted the fix/missed-required-parameter-nullable-docall branch June 4, 2026 07:00
@nixel2007

Copy link
Copy Markdown
Member Author

Учёл оба замечания CodeRabbit по WorkspaceBeanScope (коммит ac23962):

  1. remove(name) оставлял destruction callback — теперь снимаем callback вместе с бином (по контракту Scope.remove), чтобы он не выполнился повторно при removeWorkspace().
  2. removeWorkspace() не был exception-safe — каждый callback изолирован в своём try/catch (лог + продолжение), store.remove() выполняется всегда; падение одного callback больше не срывает очистку и не оставляет workspace-scoped бины висеть между запусками/тестами.

Добавлен WorkspaceBeanScopeTest (red до фикса, green после).

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