feat(inlayhints): хинты типов и inlayHint/resolve#4100
Conversation
Новый VariableTypeInlayHintSupplier показывает выведенный тип переменной сразу после её имени в присваивании (Контрагент: Массив = ...). Хинт даётся только для простой переменной-цели с единственным нетривиальным типом (не Произвольный/any) и не дублирует тип, очевидный из литерала справа. Co-Authored-By: Claude Fable 5 <[email protected]>
Заявлен resolveProvider=true; tooltip хинтов типа строится лениво в inlayHint/resolve, а не жадно при расчёте. Хинт несёт в data ссылку на тип (VariableTypeInlayHintData), по которой InlayHintProvider.resolveInlayHint находит сапплаер-владельца и дорассчитывает полное описание типа. Лёгкие поля (label/position/kind) остаются жадными; BSLTextDocumentService переопределяет resolveInlayHint и делегирует в провайдер. Co-Authored-By: Claude Fable 5 <[email protected]>
📝 WalkthroughWalkthroughThis PR implements lazy inlay hint resolution for the BSL Language Server. The server now advertises resolution support, adds a new variable-type hint supplier that generates type hints for assignments and resolves their tooltips on demand, and wires the resolution pipeline through the service and provider layers with comprehensive test coverage. ChangesInlay Hint Resolution & Variable Type Hints
Sequence DiagramsequenceDiagram
participant Client
participant BSLTextDocumentService
participant InlayHintProvider
participant VariableTypeInlayHintSupplier
participant TypeService
Client->>BSLTextDocumentService: inlayHint/resolve (unresolved hint with data)
BSLTextDocumentService->>BSLTextDocumentService: extract URI from hint data
BSLTextDocumentService->>BSLTextDocumentService: lookup DocumentContext
BSLTextDocumentService->>InlayHintProvider: resolveInlayHint(documentContext, hint)
InlayHintProvider->>InlayHintProvider: extract supplierId from hint data
InlayHintProvider->>InlayHintProvider: find matching VariableTypeInlayHintSupplier
InlayHintProvider->>VariableTypeInlayHintSupplier: resolve(documentContext, hint, data)
VariableTypeInlayHintSupplier->>TypeService: resolve type reference by name
TypeService-->>VariableTypeInlayHintSupplier: TypeRef with display name & description
VariableTypeInlayHintSupplier->>VariableTypeInlayHintSupplier: set markdown tooltip
VariableTypeInlayHintSupplier-->>InlayHintProvider: resolved hint with tooltip
InlayHintProvider->>InlayHintProvider: clear hint data field
InlayHintProvider-->>BSLTextDocumentService: resolved hint (tooltip populated, data cleared)
BSLTextDocumentService-->>Client: inlayHint/resolve response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
… Sonar VariableTypeInlayHintSupplier: simpleTargetIdentifier/inferType/extractData/ toInlayHint теперь возвращают Optional вместо null (методы @NullMarked-пакета). getInlayHints: flatMap(Optional::stream) + toList() (S6204). resolve и getInlayHints переведены на Optional.isEmpty(); убраны ставшие мёртвыми null-проверки (S2583/S2589): expression==null в isTrivialLiteral (параметр @nonnull), identifier/inferredType/data. S864: скобки вокруг конкатенации в тернаре tooltip. InlayHintProvider: dataField/extractSupplierId возвращают Optional<String>; extractUri через map(Absolute::uri); resolveInlayHint на Optional.isEmpty(). Тесты: непокрытые ветки type-хинтов и resolve — цель-член (хинта нет), тип не выводится (хинта нет), запрос вне диапазона, resolve без data, resolve с неизвестным типом (fallback по имени), provider.resolveInlayHint без data и с неизвестным сапплаером, extractUri пусто/из data. Co-Authored-By: Claude Fable 5 <[email protected]>
There was a problem hiding this comment.
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/BSLTextDocumentService.java`:
- Around line 647-650: The call to withFreshDocumentContext(documentContext, ()
-> inlayHintProvider.resolveInlayHint(documentContext, unresolved)) can return
null if the context disappears; update the resolve path in
BSLTextDocumentService so you capture the result of withFreshDocumentContext
into a local (e.g., resolved = withFreshDocumentContext(...)) and if resolved is
null return the original unresolved inlay hint instead of propagating null;
reference the methods withFreshDocumentContext and
inlayHintProvider.resolveInlayHint and ensure the public resolveInlayHint path
never returns null by falling back to the original hint.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java`:
- Around line 133-143: InlayHintProvider.java (133-143): in dataField(InlayHint,
String) guard jsonMapper.convertValue(rawData, Map.class) in a try/catch and
treat any exception or a null result as "no data" by returning Optional.empty();
also check dataMap == null before calling get(...).
VariableTypeInlayHintSupplier.java (130-139): in the code path that reconstructs
VariableTypeInlayHintData (the block that currently builds a TypeRef from the
deserialized data), treat a missing or blank typeName as unresolved and return
the original inlayHint (no change) instead of creating/using an invalid TypeRef.
VariableTypeInlayHintSupplier.java (154-164): wrap
jsonMapper.convertValue(rawData, VariableTypeInlayHintData.class) in a try/catch
and return Optional.empty() when conversion throws or yields null so downstream
resolve(...) sees empty maybeData and no-op.
🪄 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: 601edaea-1fbf-4bfb-a1b3-4046157753a6
⛔ Files ignored due to path filters (1)
src/test/resources/inlayhints/VariableTypeInlayHintSupplier.bslis excluded by!src/test/resources/**
📒 Files selected for processing (9)
src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLLanguageServer.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/InlayHintSupplier.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintData.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintResolveTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplierTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProviderTest.java
| return withFreshDocumentContext( | ||
| documentContext, | ||
| () -> inlayHintProvider.resolveInlayHint(documentContext, unresolved) | ||
| ); |
There was a problem hiding this comment.
Ensure resolveInlayHint never resolves to null on context races.
If server context disappears between pre-check and async execution, withFreshDocumentContext(...) can produce null, which leaks through this new public resolve path instead of returning the original hint.
Suggested fix
return withFreshDocumentContext(
documentContext,
() -> inlayHintProvider.resolveInlayHint(documentContext, unresolved)
- );
+ ).thenApply(resolved -> resolved == null ? unresolved : resolved);📝 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.
| return withFreshDocumentContext( | |
| documentContext, | |
| () -> inlayHintProvider.resolveInlayHint(documentContext, unresolved) | |
| ); | |
| return withFreshDocumentContext( | |
| documentContext, | |
| () -> inlayHintProvider.resolveInlayHint(documentContext, unresolved) | |
| ).thenApply(resolved -> resolved == null ? unresolved : resolved); |
🤖 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/BSLTextDocumentService.java`
around lines 647 - 650, The call to withFreshDocumentContext(documentContext, ()
-> inlayHintProvider.resolveInlayHint(documentContext, unresolved)) can return
null if the context disappears; update the resolve path in
BSLTextDocumentService so you capture the result of withFreshDocumentContext
into a local (e.g., resolved = withFreshDocumentContext(...)) and if resolved is
null return the original unresolved inlay hint instead of propagating null;
reference the methods withFreshDocumentContext and
inlayHintProvider.resolveInlayHint and ensure the public resolveInlayHint path
never returns null by falling back to the original hint.
| private Optional<String> dataField(InlayHint inlayHint, String field) { | ||
| var rawData = inlayHint.getData(); | ||
| if (rawData == null) { | ||
| return Optional.empty(); | ||
| } | ||
| // Клиент присылает data назад как JSON-объект (round-trip); in-process | ||
| // (тесты, single-jvm клиент) — как исходный объект сапплаера. Конвертируем | ||
| // через JsonMapper в карту единообразно для обоих случаев. | ||
| Map<String, Object> dataMap = jsonMapper.convertValue(rawData, Map.class); | ||
| var value = dataMap.get(field); | ||
| return value == null ? Optional.empty() : Optional.of(value.toString()); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the exact files (in case paths differ)
fd -a "InlayHintProvider.java" src/main/java | head -n 20
fd -a "VariableTypeInlayHintSupplier.java" src/main/java | head -n 20
# Show the referenced line ranges with context
sed -n '110,170p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java
echo "----"
sed -n '110,200p' src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.javaRepository: 1c-syntax/bsl-language-server
Length of output: 6364
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --hidden --no-mmap "convertValue\(\s*rawData\s*,\s*Map\.class\s*\)" src/main/java || true
rg -n --hidden --no-mmap "convertValue\(\s*rawData\s*,\s*VariableTypeInlayHintData\.class\s*\)" src/main/java || true
rg -n --hidden --no-mmap "getData\(\)" src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints src/main/java/com/github/_1c_syntax/bsl/languageserver/providers || trueRepository: 1c-syntax/bsl-language-server
Length of output: 2732
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find jsonMapper declarations in the two files
rg -n "jsonMapper" src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java
rg -n "jsonMapper" src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java
# Show surrounding sections where jsonMapper is injected/constructed
sed -n '1,220p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java | sed -n '1,220p'Repository: 1c-syntax/bsl-language-server
Length of output: 7279
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show VariableTypeInlayHintSupplier around jsonMapper usage and resolve
sed -n '1,240p' src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java
# Try to locate JsonMapper wrapper in repo (tools/...) if present
rg -n "package tools\.jackson\.databind\.json;|class JsonMapper" -S . || true
rg -n "tools\.jackson\.databind\.json\.JsonMapper" -S src/main/java || trueRepository: 1c-syntax/bsl-language-server
Length of output: 11305
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show JsonMapperConfiguration
sed -n '1,240p' src/main/java/com/github/_1c_syntax/bsl/languageserver/databind/JsonMapperConfiguration.java
# Find how JsonMapper bean is created/ configured
rg -n "new JsonMapper|JsonMapper\.builder|Builder" src/main/java/com/github/_1c_syntax/bsl/languageserver/databind/JsonMapperConfiguration.java src/main/java/com/github/_1c_syntax/bsl/languageserver -S || trueRepository: 1c-syntax/bsl-language-server
Length of output: 42433
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "resolveInlayHint\\(" src/main/java/com/github/_1c_syntax/bsl/languageserver || true
rg -n "inlayHint/resolve|inlayHint resolve|resolve\\s*\\(" src/main/java/com/github/_1c_syntax/bsl/languageserver -S | head -n 50Repository: 1c-syntax/bsl-language-server
Length of output: 9026
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '600,720p' src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.javaRepository: 1c-syntax/bsl-language-server
Length of output: 4394
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "withFreshDocumentContext\\(" -S src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java
# Show definition area
rg -n "withFreshDocumentContext" src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java
sed -n '1,140p' src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.javaRepository: 1c-syntax/bsl-language-server
Length of output: 10681
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1000,1120p' src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.javaRepository: 1c-syntax/bsl-language-server
Length of output: 3416
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "convertValue\\(" src/main/java/com/github/_1c_syntax/bsl/languageserver | head -n 200
rg -n "try \\{[^}]*convertValue\\(" -S src/main/java/com/github/_1c_syntax/bsl/languageserver || true
rg -n "catch \\(.*\\).*convertValue\\(" -S src/main/java/com/github/_1c_syntax/bsl/languageserver || trueRepository: 1c-syntax/bsl-language-server
Length of output: 440
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -a "VariableTypeInlayHintData.java" src/main/java
sed -n '1,200p' src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintData.javaRepository: 1c-syntax/bsl-language-server
Length of output: 1815
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "Optional<\\s*TypeRef\\s*>\\s*resolve\\(" -S src/main/java/com/github/_1c_syntax/bsl/languageserver/types src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeService.java
sed -n '1,220p' src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeService.javaRepository: 1c-syntax/bsl-language-server
Length of output: 10550
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '240,340p' src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeService.javaRepository: 1c-syntax/bsl-language-server
Length of output: 4331
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '200,360p' src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.javaRepository: 1c-syntax/bsl-language-server
Length of output: 6279
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' src/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeRef.java
rg -n "record TypeRef|class TypeRef" src/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeRef.javaRepository: 1c-syntax/bsl-language-server
Length of output: 7229
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "displayName\\(" src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java
rg -n "getDescription\\(" -S src/main/java/com/github/_1c_syntax/bsl/languageserver/types src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java
# Show relevant parts around displayName and description methods
sed -n '1,260p' src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java | head -n 260Repository: 1c-syntax/bsl-language-server
Length of output: 16964
Handle malformed/deserialized InlayHint.data safely to prevent inlayHint/resolve from failing
InlayHint.data coming from the client is deserialized with jsonMapper.convertValue(...) without any failure handling; if conversion throws (or returns null), the exception propagates (the LSP service doesn’t catch it) and breaks resolveInlayHint (also via extractUri/extractSupplierId).
private Optional<String> dataField(InlayHint inlayHint, String field) {
var rawData = inlayHint.getData();
if (rawData == null) {
return Optional.empty();
}
// Клиент присылает data назад как JSON-объект (round-trip); in-process
// (тесты, single-jvm клиент) — как исходный объект сапплаера. Конвертируем
// через JsonMapper в карту единообразно для обоих случаев.
Map<String, Object> dataMap = jsonMapper.convertValue(rawData, Map.class);
var value = dataMap.get(field);
return value == null ? Optional.empty() : Optional.of(value.toString());
}src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java#L133-L143: wrapjsonMapper.convertValue(rawData, Map.class)and subsequentdataMap.get(...)in a safe block; on any conversion failure (ordataMap == null) returnOptional.empty().src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java#L154-L164: wrapjsonMapper.convertValue(rawData, VariableTypeInlayHintData.class); on conversion failure (ornullresult) returnOptional.empty()soresolve(...)can no-op viamaybeData.isEmpty().src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java#L130-L139: treat reconstructedVariableTypeInlayHintDatawith missing/blanktypeNameas unresolved (returninlayHint), instead of constructing/using aTypeReffrom invalid data.
📍 Affects 2 files
src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java#L133-L143(this comment)src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java#L130-L139src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java#L154-L164
🤖 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/providers/InlayHintProvider.java`
around lines 133 - 143, InlayHintProvider.java (133-143): in
dataField(InlayHint, String) guard jsonMapper.convertValue(rawData, Map.class)
in a try/catch and treat any exception or a null result as "no data" by
returning Optional.empty(); also check dataMap == null before calling get(...).
VariableTypeInlayHintSupplier.java (130-139): in the code path that reconstructs
VariableTypeInlayHintData (the block that currently builds a TypeRef from the
deserialized data), treat a missing or blank typeName as unresolved and return
the original inlayHint (no change) instead of creating/using an invalid TypeRef.
VariableTypeInlayHintSupplier.java (154-164): wrap
jsonMapper.convertValue(rawData, VariableTypeInlayHintData.class) in a try/catch
and return Optional.empty() when conversion throws or yields null so downstream
resolve(...) sees empty maybeData and no-op.
…ay hints Зеркалит инфраструктуру данных линз для inlay hints (замечания 1, 5, 6): - InlayHintData (интерфейс, @JsonTypeInfo по id) + DefaultInlayHintData - InlayHintSupplier стал generic <T extends InlayHintData> с getInlayHintDataClass() и типизированным resolve(ctx, unresolved, data) - VariableTypeInlayHintData переведён в @value @NonFinal; поле supplierId переименовано в id (замечание 1) - извлечение данных вынесено в InlayHintProvider.extractData (замечание 5), карта сапплаеров собирается в поле inlayHintSuppliersById (замечание 6) - регистрация Jackson-подтипов inlay hints в JsonMapperConfiguration - BSLTextDocumentService.resolveInlayHint использует data.getUri() Замечание 4: Ranges.create(identifier) вместо create(identifier, identifier). Замечание 2: пропуск хинта для конструктора «Новый Тип(...)» — тип очевиден. Co-Authored-By: Claude Fable 5 <[email protected]>
There was a problem hiding this comment.
♻️ Duplicate comments (2)
src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java (1)
145-147:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard
typeNamebefore resolution to keep malformed data a no-op.
resolve(...)trustsdata.getTypeName()unconditionally. If deserialization yields null/blank, this path can fail or produce invalid tooltip state; unresolved hint should be returned unchanged in that case.Suggested fix
public InlayHint resolve( DocumentContext documentContext, InlayHint unresolved, VariableTypeInlayHintData data ) { - var typeRef = typeService.resolve(data.getTypeName(), documentContext.getFileType()) - .orElse(new TypeRef(TypeRef.UNKNOWN.kind(), data.getTypeName())); + var typeName = data.getTypeName(); + if (typeName == null || typeName.isBlank()) { + return unresolved; + } + var typeRef = typeService.resolve(typeName, documentContext.getFileType()) + .orElse(new TypeRef(TypeRef.UNKNOWN.kind(), typeName));🤖 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/inlayhints/VariableTypeInlayHintSupplier.java` around lines 145 - 147, Before calling typeService.resolve() in the VariableTypeInlayHintSupplier class, add a guard check to validate that data.getTypeName() is not null and not blank. If the type name is null or blank, return an unresolved TypeRef (such as new TypeRef(TypeRef.UNKNOWN.kind(), data.getTypeName())) directly as a no-op without calling resolve(), since malformed or missing type names should not be processed further and should prevent invalid tooltip state from occurring.src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java (1)
116-129:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
extractDatacan crashinlayHint/resolveon non-JSONdatapayloads.
jsonMapper.readValue(rawData.toString(), InlayHintData.class)assumestoString()is valid JSON. For map/object round-trips this is often false, and with@SneakyThrowsthe exception bubbles up through resolve path instead of returning “unresolved as-is”.Suggested fix
- `@SneakyThrows` public `@Nullable` InlayHintData extractData(InlayHint inlayHint) { var rawData = inlayHint.getData(); if (rawData == null) { return null; } if (rawData instanceof InlayHintData data) { return data; } - return jsonMapper.readValue(rawData.toString(), InlayHintData.class); + try { + return jsonMapper.convertValue(rawData, InlayHintData.class); + } catch (RuntimeException ex) { + return null; + } }🤖 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/providers/InlayHintProvider.java` around lines 116 - 129, The extractData method assumes that rawData.toString() always produces valid JSON when passed to jsonMapper.readValue(), but non-JSON payloads will cause parsing to fail. With `@SneakyThrows`, this exception propagates up and crashes the inlayHint/resolve operation. Wrap the jsonMapper.readValue(rawData.toString(), InlayHintData.class) call in a try-catch block to handle JsonProcessingException or similar parsing failures, returning null when the conversion fails instead of allowing the exception to bubble up. This allows the resolve operation to gracefully handle unresolvable data payloads.
🧹 Nitpick comments (1)
src/main/java/com/github/_1c_syntax/bsl/languageserver/databind/JsonMapperConfiguration.java (1)
56-65: ⚡ Quick winReplace side-effect
collect(...)calls with direct accumulation.These
collect(Collectors.toCollection(() -> namedTypes))calls are used only for side effects and trigger PMDUselessPureMethodCall. UsingforEach(namedTypes::add)is clearer and avoids the warning.Suggested refactor
- codeLensResolvers.stream() - .map(JsonMapperConfiguration::toNamedType) - .collect(Collectors.toCollection(() -> namedTypes)); - commandSuppliers.stream() - .map(JsonMapperConfiguration::toNamedType) - .collect(Collectors.toCollection(() -> namedTypes)); - inlayHintSuppliers.stream() - .filter(inlayHintSupplier -> inlayHintSupplier.getInlayHintDataClass() != null) - .map(JsonMapperConfiguration::toNamedType) - .collect(Collectors.toCollection(() -> namedTypes)); + codeLensResolvers.stream() + .map(JsonMapperConfiguration::toNamedType) + .forEach(namedTypes::add); + commandSuppliers.stream() + .map(JsonMapperConfiguration::toNamedType) + .forEach(namedTypes::add); + inlayHintSuppliers.stream() + .filter(inlayHintSupplier -> inlayHintSupplier.getInlayHintDataClass() != null) + .map(JsonMapperConfiguration::toNamedType) + .forEach(namedTypes::add);🤖 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/databind/JsonMapperConfiguration.java` around lines 56 - 65, The code uses collect(Collectors.toCollection(() -> namedTypes)) calls purely for side effects to add items to the namedTypes collection, which triggers a PMD UselessPureMethodCall warning. Replace all three of these collect calls (one for codeLensResolvers stream, one for commandSuppliers stream, and one for inlayHintSuppliers stream) with forEach(namedTypes::add) instead, which is clearer in intent and explicitly designed for side effects. This change applies to all three stream chains in the diff.Source: Linters/SAST tools
🤖 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.
Duplicate comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.java`:
- Around line 145-147: Before calling typeService.resolve() in the
VariableTypeInlayHintSupplier class, add a guard check to validate that
data.getTypeName() is not null and not blank. If the type name is null or blank,
return an unresolved TypeRef (such as new TypeRef(TypeRef.UNKNOWN.kind(),
data.getTypeName())) directly as a no-op without calling resolve(), since
malformed or missing type names should not be processed further and should
prevent invalid tooltip state from occurring.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.java`:
- Around line 116-129: The extractData method assumes that rawData.toString()
always produces valid JSON when passed to jsonMapper.readValue(), but non-JSON
payloads will cause parsing to fail. With `@SneakyThrows`, this exception
propagates up and crashes the inlayHint/resolve operation. Wrap the
jsonMapper.readValue(rawData.toString(), InlayHintData.class) call in a
try-catch block to handle JsonProcessingException or similar parsing failures,
returning null when the conversion fails instead of allowing the exception to
bubble up. This allows the resolve operation to gracefully handle unresolvable
data payloads.
---
Nitpick comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/databind/JsonMapperConfiguration.java`:
- Around line 56-65: The code uses collect(Collectors.toCollection(() ->
namedTypes)) calls purely for side effects to add items to the namedTypes
collection, which triggers a PMD UselessPureMethodCall warning. Replace all
three of these collect calls (one for codeLensResolvers stream, one for
commandSuppliers stream, and one for inlayHintSuppliers stream) with
forEach(namedTypes::add) instead, which is clearer in intent and explicitly
designed for side effects. This change applies to all three stream chains in the
diff.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0c7cfe86-a6ee-402b-8ad9-baa2d463bf96
⛔ Files ignored due to path filters (1)
src/test/resources/inlayhints/VariableTypeInlayHintSupplier.bslis excluded by!src/test/resources/**
📒 Files selected for processing (14)
src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/databind/JsonMapperConfiguration.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/AbstractComplexityInlayHintSupplier.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/AbstractMethodCallInlayHintSupplier.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/DefaultInlayHintData.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/InlayHintData.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/InlayHintSupplier.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintData.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplier.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/infrastructure/InlayHintsConfiguration.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProvider.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintResolveTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplierTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/providers/InlayHintProviderTest.java
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/com/github/_1c_syntax/bsl/languageserver/BSLTextDocumentService.java
- src/test/java/com/github/_1c_syntax/bsl/languageserver/inlayhints/VariableTypeInlayHintSupplierTest.java
|
…енивого резолва Объявление параметра/типа уже разрешено на этапе построения хинта, поэтому ссылка части метки (InlayHintLabelPart.location) проставляется жадно, без отложенного inlayHint/resolve. - SourceDefinedMethodCallInlayHintSupplier: убран ленивый путь location и гейтинг по inlayHint.resolveSupport(label.location); хинт вызова метода не несёт тяжёлых отложенных полей, поэтому переведён на DefaultInlayHintData и больше не кладёт data/не переопределяет resolve. Удалён неиспользуемый DTO MethodCallInlayHintData. - VariableTypeInlayHintData: оставлены только поля для ленивого tooltip (uri, id, typeName); убраны targetUri, координаты и hasLocation/NO_LOCATION. - VariableTypeInlayHintSupplier: location проставляется жадно, tooltip по-прежнему дорассчитывается лениво через inlayHint/resolve (как в #4100). - Тесты: убраны проверки ленивого round-trip'а location, оставлены жадные проверки location и ленивая проверка tooltip. Co-Authored-By: Claude Fable 5 <[email protected]>



Проблема
До изменений inlay-хинты ограничивались
kind=Parameter(имена аргументов вызовов и метки сложности) — выведенного типа переменной не показывалось. Кроме того,resolveProvider=false: tooltip строился жадно, хотя дорогие поля можно отдавать лениво.Решение
Часть 1 — хинты выведенного типа (
InlayHintKind.Type).Новый
VariableTypeInlayHintSupplier(Spring@Component, idvariableType) для присваиванияПерем = Выражениепоказывает выведенный тип сразу после имени переменной:Контрагент: Массив = Новый Массив(). Тип берётся черезTypeService.expressionTypesAtпо правой части. Хинт даётся только когда:lValue.IDENTIFIER()безacceptor, т.е. неПерем.Поле = ...);Произвольный/any, неUNKNOWN);= 1,= "Текст"— пропускаются).Включение управляется штатным механизмом
InlayHintsConfiguration.supplierIsEnabledпо id сапплаера (как у complexity-тогглов).Часть 2 — отложенный tooltip через
inlayHint/resolve.Заявлен
resolveProvider=true. Tooltip (полное описание типа) строится лениво: при жадном расчёте вInlayHint.dataкладётсяVariableTypeInlayHintData(id сапплаера, uri, имя типа);BSLTextDocumentService.resolveInlayHintнаходит документ по uri и делегирует вInlayHintProvider.resolveInlayHint, который по id диспетчеризует в сапплаер-владельца (InlayHintSupplier.resolve— новый default-метод). Round-trip data единообразно конвертируется черезJsonMapper.convertValue(и для in-process объекта, и для JSON от клиента). Лёгкие поля (label/position/kind) остаются жадными; после резолваdataочищается.Тесты
VariableTypeInlayHintSupplierTest— присваивание с выводимым типом → хинтkind=Typeс именем типа и позицией после имени; литеральное присваивание → нет хинта.VariableTypeInlayHintResolveTest— жадный хинт несётdataбез tooltip; послеresolveInlayHinttooltip заполнен описанием типа,dataочищена.InlayHintProviderTest,SourceDefinedMethodCallInlayHintSupplierTest— зелёные.🤖 Generated with Claude Code
Summary by CodeRabbit