feat(types): тип возвращаемого значения рассчитывается по телу метода - #4402
Conversation
|
Warning Review limit reached
Next review available in: 7 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe change adds body-based method return inference, dependency-aware indexing and propagation, inferred return types in hover and signature help, document-state access, generic-prefix lookup caching, and Python command permission rules. ChangesMethod return-type inference
Generic prefix lookup cache
Command permission rules
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant ServerContext
participant MethodReturnTypeIndexer
participant ExpressionTypeInferencer
participant SymbolTypeIndex
ServerContext->>MethodReturnTypeIndexer: document content change
MethodReturnTypeIndexer->>ExpressionTypeInferencer: recompute method return types
ExpressionTypeInferencer->>SymbolTypeIndex: store inferred types
MethodReturnTypeIndexer->>MethodReturnTypeIndexer: invalidate and recompute dependent documents
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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 |
Test Results 4 038 files + 18 4 038 suites +18 58m 57s ⏱️ + 13m 53s Results for commit ec6b60a. ± Comparison against base commit efa5187. This pull request removes 1 and adds 25 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
|
Смотрю со стороны параллельной ветки — в #4406 задет тот же кусок Кэш типов выражений: два разных «неокончательных ответа»Здесь: if (cacheKey != null && !ctx.cycleCut) {
inferredExpressionTypeIndex.put(uri, cacheKey, result, ctx.dependencies);В #4406 правится соседняя строка — вычисление var cacheKey = ctx.visited.isEmpty() && ctx.inProgress.isEmpty()
&& !ctx.flowSession.computing() ? node.getRepresentingAst() : null;Причина у обоих одна: в кэш попадал ответ, который ещё не окончателен. Но условия разные и друг друга не закрывают:
Так что после обоих мержей условие должно стать составным, а не «чьим-то одним». Второму по очереди достанется ещё и адаптация под новую сигнатуру Про замерыМы этот же вывод возврата по телу пробовали лениво — считать изнутри инференса, с кэшем по методу, — и уткнулись: полный Если полного прогона ещё не было, цифра выше годится как база для сравнения. Гочки, которые нам дорого дались: 🤖 Generated with Claude Code |
|
Спасибо, оба пункта по делу. Про условие кэшаСогласен, условия разные и друг друга не закрывают. На этой ветке
// было
void put(URI uri, ParseTree node, TypeSet types);
// стало
void put(URI uri, ParseTree node, TypeSet types, Set<URI> dependencies);Четвёртый параметр — URI документов, чьё содержимое участвовало в расчёте; по ним индекс Про замерыПолного прогона по ERP у меня не было — мерил на другой конфигурации (23 575 модулей,
То есть ветка вышла быстрее базы. Ключевым оказался не сам вывод возврата, а то, что он Ваш вывод про ленивый расчёт подтверждаю с другой стороны: проход доразрешения после По диагностикам: ветка добавляет 100 363 срабатывания За гочки по запуску спасибо — учту, если буду гонять ERP. |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (8)
.claude/settings.json (1)
11-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the redundant pipe-based Bash rules.
Claude Code evaluates compound Bash commands as segments, so
Bash(python:*)andBash(python3:*)already cover the interpreter segment in commands such ascommand | python. These addedBash(*|python:*)/Bash(*|python3:*)rules do not add coverage and depend on unverified parser behavior.Proposed cleanup
"Bash(python:*)", - "Bash(python3:*)", - "Bash(*|python:*)", - "Bash(*|python3:*)" + "Bash(python3:*)"🤖 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 @.claude/settings.json around lines 11 - 12, Remove the redundant Bash(*|python:*) and Bash(*|python3:*) entries from the permissions configuration, leaving the existing Bash(python:*) and Bash(python3:*) rules unchanged.src/test/java/com/github/_1c_syntax/bsl/languageserver/hover/MethodSymbolMarkupContentBuilderTest.java (1)
242-244: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueJoin assertions on the same value.
SonarCloud reports duplicate assertion subjects at Lines 242 and 263. Chain the
containscalls to keep each scenario as one assertion chain.Proposed cleanup
- assertThat(content).contains("**Возвращаемое значение:**"); - assertThat(content).contains("Массив"); - assertThat(content).contains("Число"); + assertThat(content) + .contains("**Возвращаемое значение:**") + .contains("Массив") + .contains("Число");Also applies to: 263-265
🤖 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/test/java/com/github/_1c_syntax/bsl/languageserver/hover/MethodSymbolMarkupContentBuilderTest.java` around lines 242 - 244, Combine the consecutive contains assertions for the same content value in MethodSymbolMarkupContentBuilderTest into a single fluent assertion chain for each scenario, including both the assertions around lines 242 and 263, while preserving all existing expected substrings.Source: Linters/SAST tools
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/DocumentDependencies.java (1)
24-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the unused
MethodSymbolimport.The file does not reference
MethodSymbol. Static analysis reports it as unused.♻️ Proposed fix
-import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; - import java.net.URI;🤖 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/types/index/DocumentDependencies.java` at line 24, Remove the unused MethodSymbol import from DocumentDependencies.java, leaving the remaining imports and implementation unchanged.Source: Linters/SAST tools
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexer.java (1)
465-500: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReduce the cognitive complexity of
resolveComponent.SonarCloud reports a failure: cognitive complexity 18 against the allowed 15. The method mixes three strategies (single document, oversized component, in-memory component) and repeats the same fixed-point loop twice. Extract the two loop bodies into named private methods, for example
resolveLargeComponentandresolveLoadedComponent. The behaviour stays the same and each strategy becomes readable on its own.🤖 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/types/index/MethodReturnTypeIndexer.java` around lines 465 - 500, Reduce cognitive complexity in resolveComponent by extracting the oversized-component pass loop into a private resolveLargeComponent method and the withDocumentsLoaded fixed-point loop into a private resolveLoadedComponent method. Keep the existing single-document handling, MAX_PASSES termination, changed checks, and return behavior unchanged while delegating each strategy from resolveComponent.Source: Linters/SAST tools
src/test/java/com/github/_1c_syntax/bsl/languageserver/types/MapElementFieldsInferenceTest.java (1)
50-52: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSelect the map reference by name instead of by position.
getReturnTypescan now return several references: the declared type plus the types inferred from the body. Line 51 takes the first reference. The assertion passes only becauseTypeSet.unionkeeps the declared references first. The same test file family already moved to explicit selection for this reason — seeInlineTypeCommentInferenceTestlines 86-89. SelectingСоответствиеby qualified name removes the ordering dependency. The variable namedeclaredis also stale now that the value includes inferred types.♻️ Proposed change
- var declared = typeService.getReturnTypes(method("Тело")); - var mapRef = declared.refs().iterator().next(); - var element = declared.getElementTypes(mapRef); + var returned = typeService.getReturnTypes(method("Тело")); + var mapRef = returned.refs().stream() + .filter(ref -> "Соответствие".equals(ref.qualifiedName())) + .findFirst() + .orElseThrow(); + var element = returned.getElementTypes(mapRef);🤖 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/test/java/com/github/_1c_syntax/bsl/languageserver/types/MapElementFieldsInferenceTest.java` around lines 50 - 52, Update the test around getReturnTypes in MapElementFieldsInferenceTest to select the Соответствие map reference by its qualified name instead of taking declared.refs().iterator().next(). Rename the stale declared variable to reflect that it contains inferred and declared types, while preserving the existing element-type assertion.src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ExpressionTypeInferencer.java (2)
802-808: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winLog the failure before returning an empty result.
returnTypesOfBodycatchesStackOverflowErrorandRuntimeExceptionand returns an emptyComputedReturnTypeswithincomplete=false. The indexer stores that value as a final answer, so a crash in body inference becomes a silent "this function returns nothing" for the whole workspace. The surrounding code logs comparable failures, for exampleflowTypeAtat lines 850-855. SonarCloud also flags line 805.♻️ Proposed change
} catch (StackOverflowError | RuntimeException e) { + LOGGER.error("Расчёт типа возврата по телу сорвался на методе {}: {}", + method.getName(), method.getOwner().getUri(), e); return new MethodReturnTypeIndexer.ComputedReturnTypes(TypeSet.EMPTY, Set.of(), false); }🤖 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/types/inferencer/ExpressionTypeInferencer.java` around lines 802 - 808, Update returnTypesOfBody to log the caught StackOverflowError or RuntimeException before returning the empty ComputedReturnTypes result. Follow the existing failure-logging pattern used by flowTypeAt, preserving the current fallback return value while including the exception details.Source: Linters/SAST tools
643-653: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the source-method branch to clear the SonarCloud gate.
SonarCloud reports a failure on line 649: more than three nested
if/forstatements. The newsymbolReturnblock adds the fourth level inside thefor/for/ifchain. Move the member handling into a small private method that returns the resolvedTypeSetfor one member.🤖 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/types/inferencer/ExpressionTypeInferencer.java` around lines 643 - 653, Extract the source-method handling from the member iteration in ExpressionTypeInferencer into a small private helper that resolves and returns the TypeSet for a single member, including the MethodSymbol return-type lookup and fallback behavior. Update the existing loop to call this helper and union its result, reducing nesting without changing inference semantics.Source: Linters/SAST tools
src/test/java/com/github/_1c_syntax/bsl/languageserver/types/ReturnTypeFromBodyInferenceTest.java (1)
235-243: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueShare one marker-to-position helper across the type tests.
This helper duplicates
InlineTypeCommentInferenceTest.inferAtMarkerandMapElementFieldsInferenceTest.at, and it uses a different line-start rule:lastIndexOf('\n', targetOffset - 1)here versuslastIndexOf('\n', targetOffset)in the other two. The two rules disagree when the offset lands on a newline. Move one implementation intoTestUtilsand call it from all three tests.🤖 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/test/java/com/github/_1c_syntax/bsl/languageserver/types/ReturnTypeFromBodyInferenceTest.java` around lines 235 - 243, Move the marker-to-Position logic from ReturnTypeFromBodyInferenceTest.at, InlineTypeCommentInferenceTest.inferAtMarker, and MapElementFieldsInferenceTest.at into a shared TestUtils helper, preserving marker validation and a single consistent newline boundary rule. Replace all three local helpers with calls to TestUtils and update callers as needed.
🤖 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/hover/DescriptionFormatter.java`:
- Around line 171-181: Split each documented returned-value name on commas
before applying DescriptionFormatter.headName, then normalize and compare the
resulting individual type names against returnTypes.refs() so documented union
members are not classified as undescribed. Apply the same correction to the
analogous logic around the second reported range, and add a hover test covering
documented “Число, Строка” with matching branch returns that asserts
inferredReturnedValue is absent.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java`:
- Around line 494-495: Update SignatureInfoProvider.methodToDescriptor(...) to
preserve all alternatives from typeService.getReturnTypes(method) instead of
selecting one with findFirst(). Build the return label from the complete
returnTypes.refs() set, or suppress the specific type when multiple alternatives
cannot be represented, while retaining TypeRef.UNKNOWN for an empty result.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexer.java`:
- Around line 510-535: Update withDocumentsLoaded to acquire document write
locks in a consistent global order by sorting the component URIs before
iterating and locking them. Preserve the existing document lookup, rebuild, work
execution, cleanup, and reverse-order unlock behavior.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/SymbolTypeIndex.java`:
- Around line 171-179: Update SymbolTypeIndex.putReturnTypes and the
inferredByUri map to use a set per URI instead of a CopyOnWriteArrayList,
ensuring repeated recomputation does not retain duplicate MethodSymbol
references. Remove the method from the URI set when types is empty, and review
clear(uri) ordering or synchronization so concurrent putReturnTypes calls cannot
leave inferredReturnTypes entries unreachable from a later clear.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ExpressionTypeInferencer.java`:
- Around line 795-808: Update returnTypesOfBody to isolate ctx.consulted and
ctx.sawMissing for each method-body computation by using fresh per-call tracking
state, then merge that state into the parent context after inference completes,
including exceptional paths as appropriate. Ensure ComputedReturnTypes contains
only dependencies and incompleteness produced by the current method. When
ctx.depth reaches MAX_DEPTH, return the empty result with incomplete=true so
truncated inference is reprocessed consistently with cycleCut.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ReturnTypeFromBodyInference.java`:
- Around line 105-117: Update typesOfExit in ReturnTypeFromBodyInference to
store the result of
ExpressionTreeBuildingVisitor.buildExpressionTree(expression), check it for
null, and call expressionTypes.of only for a non-null tree; return the existing
empty TypeSet fallback otherwise. Preserve the current handling for absent
return expressions, raise statements, and undefined results.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.java`:
- Around line 370-375: Synchronize generic-prefix cache lookups and alias
mutations so stale results cannot be republished after invalidation. Update the
lookup containing genericByPrefix.computeIfAbsent and the alias mutation/removal
paths, including the operation near the alias removal site, to use one shared
lock or equivalent epoch-checked publication; route all direct alias writes
through the same protocol. Add concurrency coverage for lookups racing alias
addition and removal.
---
Nitpick comments:
In @.claude/settings.json:
- Around line 11-12: Remove the redundant Bash(*|python:*) and Bash(*|python3:*)
entries from the permissions configuration, leaving the existing Bash(python:*)
and Bash(python3:*) rules unchanged.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/DocumentDependencies.java`:
- Line 24: Remove the unused MethodSymbol import from DocumentDependencies.java,
leaving the remaining imports and implementation unchanged.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexer.java`:
- Around line 465-500: Reduce cognitive complexity in resolveComponent by
extracting the oversized-component pass loop into a private
resolveLargeComponent method and the withDocumentsLoaded fixed-point loop into a
private resolveLoadedComponent method. Keep the existing single-document
handling, MAX_PASSES termination, changed checks, and return behavior unchanged
while delegating each strategy from resolveComponent.
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ExpressionTypeInferencer.java`:
- Around line 802-808: Update returnTypesOfBody to log the caught
StackOverflowError or RuntimeException before returning the empty
ComputedReturnTypes result. Follow the existing failure-logging pattern used by
flowTypeAt, preserving the current fallback return value while including the
exception details.
- Around line 643-653: Extract the source-method handling from the member
iteration in ExpressionTypeInferencer into a small private helper that resolves
and returns the TypeSet for a single member, including the MethodSymbol
return-type lookup and fallback behavior. Update the existing loop to call this
helper and union its result, reducing nesting without changing inference
semantics.
In
`@src/test/java/com/github/_1c_syntax/bsl/languageserver/hover/MethodSymbolMarkupContentBuilderTest.java`:
- Around line 242-244: Combine the consecutive contains assertions for the same
content value in MethodSymbolMarkupContentBuilderTest into a single fluent
assertion chain for each scenario, including both the assertions around lines
242 and 263, while preserving all existing expected substrings.
In
`@src/test/java/com/github/_1c_syntax/bsl/languageserver/types/MapElementFieldsInferenceTest.java`:
- Around line 50-52: Update the test around getReturnTypes in
MapElementFieldsInferenceTest to select the Соответствие map reference by its
qualified name instead of taking declared.refs().iterator().next(). Rename the
stale declared variable to reflect that it contains inferred and declared types,
while preserving the existing element-type assertion.
In
`@src/test/java/com/github/_1c_syntax/bsl/languageserver/types/ReturnTypeFromBodyInferenceTest.java`:
- Around line 235-243: Move the marker-to-Position logic from
ReturnTypeFromBodyInferenceTest.at,
InlineTypeCommentInferenceTest.inferAtMarker, and
MapElementFieldsInferenceTest.at into a shared TestUtils helper, preserving
marker validation and a single consistent newline boundary rule. Replace all
three local helpers with calls to TestUtils and update callers as needed.
🪄 Autofix
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 Plus
Run ID: cc4b88c5-538c-4b5a-aa0f-6161d20d0334
📒 Files selected for processing (27)
.claude/settings.jsonsrc/main/java/com/github/_1c_syntax/bsl/languageserver/context/DocumentState.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/hover/DescriptionFormatter.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/hover/MethodSymbolMarkupContentBuilder.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeService.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/DocumentDependencies.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/InferredExpressionTypeIndex.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexer.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/index/SymbolTypeIndex.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ExpressionTypeInferencer.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/inferencer/ReturnTypeFromBodyInference.javasrc/main/java/com/github/_1c_syntax/bsl/languageserver/types/registry/TypeRegistry.javasrc/main/resources/com/github/_1c_syntax/bsl/languageserver/hover/DescriptionFormatter_en.propertiessrc/main/resources/com/github/_1c_syntax/bsl/languageserver/hover/DescriptionFormatter_ru.propertiessrc/main/resources/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexer_en.propertiessrc/main/resources/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexer_ru.propertiessrc/test/java/com/github/_1c_syntax/bsl/languageserver/architecture/ArchitectureTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/hover/MethodSymbolMarkupContentBuilderTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/InlineTypeCommentInferenceTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/MapElementFieldsInferenceTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/NestedSeeRefInferenceTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/ReturnTypeFromBodyInferenceTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/TypeServiceDelegationTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/index/InferredExpressionTypeIndexTest.javasrc/test/java/com/github/_1c_syntax/bsl/languageserver/types/index/MethodReturnTypeIndexerTest.java
| var returnTypes = typeService.getReturnTypes(method); | ||
| var returnRef = returnTypes.refs().stream().findFirst().orElse(TypeRef.UNKNOWN); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the return-type representation used by signature help.
ast-grep outline src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java \
--match methodToDescriptor --view expanded
rg -n -C 5 'record SignatureDescriptor|class SignatureDescriptor|returnType' src/main/java
rg -n -C 8 'getSignatureHelp|РазныеТипы|Возврат Новый Массив|Возврат 10' src/test/javaRepository: 1c-syntax/bsl-language-server
Length of output: 50386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- location candidates ---\n'
git ls-files | rg 'SignatureHelpProvider\.java|SignatureDescriptor|TypeRef|TypeSet|MethodReturnType|getReturnTypes'
printf '\n--- SignatureHelpProvider relevant area ---\n'
wc -l src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
sed -n '460,520p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
printf '\n--- SignatureDescriptor files ---\n'
for f in $(git ls-files | rg 'SignatureDescriptor\.java'); do
echo "### $f"
wc -l "$f"
sed -n '1,220p' "$f"
done
printf '\n--- getReturnTypes definitions/usages non-test ---\n'
rg -n -C 5 'MethodReturnType|getReturnTypes|TypeSet\(null|TypeSet\.EMPTY|unknown\s*=\s*null|refs\(\)' src/main/java/com/github/_1c_syntax/bsl/languageserver | sed -n '1,260p'Repository: 1c-syntax/bsl-language-server
Length of output: 258
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- location candidates ---'
git ls-files | rg 'SignatureHelpProvider\.java|SignatureDescriptor|TypeRef|TypeSet|MethodReturnType|getReturnTypes' || true
printf '%s\n' '--- SignatureHelpProvider relevant area ---'
wc -l src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
sed -n '460,520p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
printf '%s\n' '--- SignatureDescriptor files ---'
while IFS= read -r f; do
echo "### $f"
wc -l "$f"
sed -n '1,220p' "$f"
done < <(git ls-files | rg 'SignatureDescriptor\.java')
printf '%s\n' '--- getReturnTypes definitions/usages non-test ---'
rg -n -C 5 'MethodReturnType|getReturnTypes|TypeSet\(null|TypeSet\.EMPTY|unknown\s*=\s*null|refs\(\)' src/main/java/com/github/_1c_syntax/bsl/languageserver | sed -n '1,260p' || trueRepository: 1c-syntax/bsl-language-server
Length of output: 4485
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- SignatureDescriptor files ---'
git ls-files | rg 'SignatureDescriptor\.java' | while IFS= read -r f; do
echo "### $f"
wc -l "$f"
sed -n '1,220p' "$f"
done
printf '%s\n' '--- getReturnTypes definitions/usages non-test ---'
rg -n -C 5 'MethodReturnType|getReturnTypes|TypeSet\(null|TypeSet\.EMPTY|unknown\s*=\s*null|refs\(\)' src/main/java/com/github/_1c_syntax/bsl/languageserver | sed -n '1,320p' || true
printf '%s\n' '--- SignatureHelpProvider tests ---'
git ls-files | rg 'SignatureHelpProvider.*Test\.java' | while IFS= read -r f; do
echo "### $f"
rg -n -C 4 'return|Массив|Число|Возврат|Разные' "$f" || true
done
printf '%s\n' '--- TypeSet relevant APIs ---'
for f in $(git ls-files 'src/main/java/**/TypeSet.java' 'src/main/java/**/MethodReturnTypeIndexer.java'); do
echo "### $f"
wc -l "$f"
sed -n '1,220p' "$f"
doneRepository: 1c-syntax/bsl-language-server
Length of output: 50385
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- SignatureHelpProvider formatting method ---'
sed -n '600,645p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
printf '%s\n' '--- TypeSet relevant APIs ---'
for f in $(git ls-files 'src/main/java/**/TypeSet.java' 'src/main/java/**/MethodReturnTypeIndexer.java'); do
echo "### $f"
wc -l "$f"
sed -n '1,220p' "$f"
done
printf '%s\n' '--- focused signature-help union-related tests ---'
rg -n -C 5 'Массив|Число|Разн|Возврат|Скелет|Union|union|returnType|returnTypes' src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProviderTest.java || trueRepository: 1c-syntax/bsl-language-server
Length of output: 26195
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- SignatureHelpProvider relevant full section ---'
sed -n '1,90p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
sed -n '460,550p' src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProvider.java
printf '%s\n' '--- focused tests around function return signature ---'
sed -n '520,570p' src/test/java/com/github/_1c_syntax/bsl/languageserver/providers/SignatureHelpProviderTest.javaRepository: 1c-syntax/bsl-language-server
Length of output: 10465
Show all return alternatives or suppress the return type.
SignatureDescriptor already stores return types as TypeSet, but SignatureInfoProvider.methodToDescriptor(...) keeps only findFirst() and labels the signature with that single TypeRef. A function with multiple reachable return types, such as Массив in one branch and Число in another, shows only one return type. Build the return type label from all returnTypes.refs() or omit the specific return type for multi-alternative unions.
🤖 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/SignatureHelpProvider.java`
around lines 494 - 495, Update SignatureInfoProvider.methodToDescriptor(...) to
preserve all alternatives from typeService.getReturnTypes(method) instead of
selecting one with findFirst(). Build the return label from the complete
returnTypes.refs() set, or suppress the specific type when multiple alternatives
cannot be represented, while retaining TypeRef.UNKNOWN for an empty result.
| private MethodReturnTypeIndexer.ComputedReturnTypes returnTypesOfBody( | ||
| MethodSymbol method, | ||
| InferenceContext ctx | ||
| ) { | ||
| if (ctx.depth >= MAX_DEPTH) { | ||
| return new MethodReturnTypeIndexer.ComputedReturnTypes(TypeSet.EMPTY, Set.of(), false); | ||
| } | ||
| try { | ||
| var types = returnTypeFromBodyInference.of(method, expression -> inferInternal(expression, ctx)); | ||
| return new MethodReturnTypeIndexer.ComputedReturnTypes(types, Set.copyOf(ctx.consulted), ctx.sawMissing); | ||
| } catch (StackOverflowError | RuntimeException e) { | ||
| return new MethodReturnTypeIndexer.ComputedReturnTypes(TypeSet.EMPTY, Set.of(), false); | ||
| } | ||
| } |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
ctx.consulted and ctx.sawMissing are shared by the whole inference, so a nested computation records dependencies that are not its own.
returnTypesOfBody snapshots Set.copyOf(ctx.consulted) and reads ctx.sawMissing. Both live on the shared InferenceContext. Line 754 calls returnTypesOfBody(method, ctx) inside an inference that already consulted other methods, so the snapshot for that nested method contains every method consulted earlier in the same context, plus the sawMissing flag raised by an unrelated branch.
MethodReturnTypeIndexer.store uses consulted() to build the dependency graph and incomplete() to schedule a re-pass. The extra edges make unrelated methods recompute whenever any of those documents change, and the sticky incomplete flag adds methods to pending that are already complete. Results stay correct, but the propagation set grows.
Record the consulted set and the missing flag per computation instead of per context. For example, swap in a fresh set for the duration of returnTypesOfBody and merge it back into the parent afterwards.
Line 799 has a related fidelity gap: when the depth limit stops the computation, the returned ComputedReturnTypes reports incomplete=false. The indexer then treats a truncated result as final. Consider reporting true there, as cycleCut already does for the expression cache.
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis
[warning] 805-805: Either log or rethrow this exception.
🤖 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/types/inferencer/ExpressionTypeInferencer.java`
around lines 795 - 808, Update returnTypesOfBody to isolate ctx.consulted and
ctx.sawMissing for each method-body computation by using fresh per-call tracking
state, then merge that state into the parent context after inference completes,
including exceptional paths as appropriate. Ensure ComputedReturnTypes contains
only dependencies and incompleteness produced by the current method. When
ctx.depth reaches MAX_DEPTH, return the empty result with incomplete=true so
truncated inference is reprocessed consistently with cycleCut.
|
@coderabbitai разобрал все семь. Пять исправил, два оставил — с обоснованием. Исправлено (b1005c7)Объединение типов в описании ( Дерево выражения возврата ( Порядок взятия замков ( Накопление повторов ( Гонка сброса и записи (там же). Окно настоящее: ОставленоПодсказка сигнатуры показывает только первый тип из объединения. Замечание верное, но Общий контекст вывода ( |
|
Tip For best results, initiate chat on the files or code changes.
Принял исправления из Ограничение Замечание о совместных 🐇 ✏️ Learnings added
You are interacting with an AI system. |
На URI в рабочей области приходится ровно один DocumentContext, а сравнение самих URI нормализует процентные последовательности и заметно дороже: в профиле analyze по ssl_3_1 percentNormalizedComparison виден отдельной строкой. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Раньше в очередь попадал каждый зависимый метод, чей документ выгружен, — а при наполнении рабочей области выгружены почти все, и проход перечитывал с диска половину конфигурации. Теперь метод откладывается, только если расчёт правда видел непосчитанное значение чужой функции. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Проход после наполнения рабочей области догружал документы и оставлял их в памяти: на большой конфигурации куча кончалась. Теперь документ возвращается в прежнее состояние сразу после пересчёта, а догрузка и освобождение идут под блокировкой документа на запись — иначе у соседнего потока вторичные данные пропадали бы посреди его расчёта. Одновременно загруженных документов не больше, чем потоков в пуле. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Перечень состояний вынесен из ServerContext в DocumentState, состояние отдаётся геттером. Раньше «разобран ли документ» приходилось выяснять перехватом исключения из getAst(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Проход после наполнения рабочей области на большой конфигурации идёт минутами, поэтому сообщает прогресс так же, как само наполнение. Слою типов разрешён доступ к client — ради индикатора. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Проход идёт волнами, и каждая вскрывает новых потребителей: общее число документов заранее неизвестно, поэтому наращивается по мере того, как работа находится. Иначе счётчик убегал за границу — «310/104 модулей». Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Выведенные по телу типы складываются в SymbolTypeIndex рядом с объявленными, а наружу торчит один getReturnTypes — и у индекса, и у фасада. Отдельного хранилища и понятия «расчётный тип» в публичном API больше нет. Расчёт остался отдельным компонентом MethodReturnTypeIndexer: он слушает событие разбора, считает и пишет результат. Зависимость на инференсер теперь прямая — ObjectProvider не нужен, потому что хранилище про инференсер не знает. Обработчикам события задан явный порядок: без него SymbolTypeIndex шёл последним и стирал только что записанное индексатором. Ховер метода показывает тип возвращаемого значения, выведенный по телу, когда автор его не описал. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Если метод возвращает что-то сверх описанного, под секцией «Возвращаемое значение» появляется приписка с этими типами: описание могло устареть или быть неполным, а работает код по своему возврату. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Без общего контекста у расчёта своя нулевая глубина, ограничитель не срабатывает и цепочка вызовов внутри модуля уходит в рекурсию: на ssl_3_1 это давало 229 переполнений стека. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
В ширину волна пересчитывала всех потребителей разом, и документ попадал под разбор снова и снова: на ssl_3_1 — 181 разбор на 19 документов, на cpm — 301 на 31. В глубину сначала доводятся значения вызванных методов, потом пересчитывается вызывающий, поэтому повторов нет, а одновременно нужна лишь текущая цепочка. Заодно закрыт рассинхрон неэкспортных методов: пересчёт загруженного документа снимает пометки так же, как это делает разбор, иначе экспортные пересчитывались по неэкспортным со старыми значениями. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Задачи прохода ждут блокировки документов, а такое ожидание ForkJoinPool компенсировать не умеет: воркеры вставали, и проход зависал целиком. Работы здесь на десятки документов, поэтому обход идёт последовательно, в одном потоке. Индикатор считает то же, по чему тикает, — методы, а не документы: раньше числитель убегал за знаменатель. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Обход в глубину по методам оказался хуже: грузить всё равно приходится документ целиком, а обход шёл по методам, и один документ загружался заново под каждый свой метод — на ssl_3_1 4252 разбора против 181 у прохода по документам. Теперь единица работы — документ: загрузили, пересчитали все его отложенные методы, отпустили. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Отложенные методы группируются по документам, документы — по компонентам сильной связности, компоненты идут в обратном топологическом порядке. Внутри цикла его документы держатся разобранными разом, но не больше восьми. Пометка «посчитан» перед пересчётом больше не снимается: соседний метод, читающий этот прямо сейчас, считал его непосчитанным и снова уходил в отложенные. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Догрузка документа стирала его записи и пересчитывала с нуля, поэтому изменившимся выглядел каждый его метод, и весь документ снова уезжал в очередь. Проход крутил одно и то же до предохранителя: на ssl_3_1 десять волн по 313 методов и 215 разборов на 26 документов. Теперь при общем проходе разбор в очередь ничего не складывает — что пересчитывать, решает сам проход по методам, чей расчёт видел непосчитанное. Стало: одна волна, 66 методов, 18 разборов на 18 документов. Добавлены отладочные счётчики по волнам и по причинам откладывания. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Каждый вызов перебирал весь индекс имён со startsWith. В профиле analyze по cpm это была вторая строка сверху: 59 410 сэмплов, а вместе со сравнением строк — больше, чем у любого другого места. Ответ зависит только от содержимого индекса, поэтому запоминается и сбрасывается при его изменении. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
…одам По дампу cpm два графа зависимостей занимали 185 МБ из 278 у всего индекса: 42 253 набора потребителей и 60 127 наборов зависимостей, около 1,26 млн рёбер. Хранение по методам того не стоит: правка пересчитывает документ целиком, поэтому связь метода с соседом по тому же файлу не спрашивается никогда, а межфайловые связи укладываются в число документов. Заодно закрыт дефект разноса: clear() стирал записи о потребителях метода за шаг до того, как propagate их спрашивал, — правка модуля не доходила до тех, чьи значения на нём построены. Теперь снимаются только связи с зависимостями, а записи о потребителях остаются: содержимое изменилось, но построены они по-прежнему на этом документе. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Quality Gate валился по покрытию нового кода: 61% против требуемых 80%. Непокрытым оставался порядок обхода документов и вся машинерия прохода — resolveAll, resolveComponent, withDocumentsLoaded, recomputeLoading. DocumentDependenciesTest: независимые документы, цепочка, взаимная зависимость, ссылка на себя, зависимость вне пересчёта, повтор зависимости. MethodReturnTypeIndexerTest: отложенный метод пересчитывается после наполнения рабочей области; освобождённый документ догружается ради пересчёта и сразу отпускается; документы цикла держатся загруженными до неподвижной точки. Покрытие: DocumentDependencies 0/45 непокрытых, MethodReturnTypeIndexer 25/196 вместо 92/196. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Разбор объявленных типов: имя типа в описании могло перечислять несколько типов через запятую, а сравнение брало строку целиком — «Число, Строка» превращалось в «число,» и не совпадало ни с чем. В ховере из-за этого появлялась приписка «выведено по коду» там, где автор всё описал. Выведенные значения по документам: набор вместо CopyOnWriteArrayList. Значение метода пересчитывается многократно за одно поколение — при разборе, в проходе доразрешения, при разносе по потребителям, — и список копил повторы, копируя при каждом добавлении весь массив. Запись и сброс выведенных значений идут через compute по ключу документа, то есть под одним замком. Прежде расчёт по запросу, идущий вне событий жизненного цикла, мог вклиниться между снятием набора и обходом, и значение оставалось в карте типов без ссылки на него — недостижимым для следующего сброса. Замки документов в цикле берутся в порядке имён: их держится сразу несколько, и без общего порядка два потока на пересекающихся компонентах встали бы друг против друга. Дерево выражения возврата проверяется на null: на нераспознанном тексте оно не строится, а контракт вызываемого предполагает непустой узел. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
b1005c7 to
ec6b60a
Compare
|
Замер после правок по ревьюВетка перебазирована на свежий
Замедления нет. Опасение было обоснованным — запись выведенных значений переведена на Разброс между прогонами одной и той же сборки — 8 замечаний. То есть разница с прежней Что именно изменилось. Целиком одна диагностика:
Остальные правила совпадают до единицы. Причина — правка атомарности в После правки такой потери нет: у большего числа методов есть посчитанный тип, и проверка |
Недетерминированность анализа: замеры и разборПроверял, стабилен ли результат анализа от прогона к прогону. Короткий ответ: нет, и это не свойство ветки — ЗамерыОдин и тот же jar гонялся несколько раз подряд на неизменном исходном коде. Конфигурация cpm, 16 ГБ, sarif. Оговорюсь: по первым двум прогонам ветки размах выходил 8, и это выглядело лучше Дальше всё на ssl_3_1 (2162 модуля, 8 ГБ, json) — там расхождение воспроизводится за 80 секунд вместо двадцати минут:
По времени ветка быстрее Что именно гуляетВезде расходятся ровно две диагностики —
То есть разбор цепочки доходит до разной глубины: состав ПричинаФакты о типах продолжают появляться во время самого анализа, а не только при наполнении рабочей области. Расчёт для неэкспортных методов идёт по требованию прямо из разбора документа и пишет в общий индекс, оттуда изменение разносится по потребителям — но те, чья очередь уже прошла, диагностики построили на прежних значениях. Кто успел раньше, решает планировщик, отсюда и разброс. Что проверено и отпадает:
Что подтверждено: в Опытная правка, помечающая такой документ отложенным, на ssl_3_1 срезает размах с 86 до 16. Но на cpm выигрыша нет: четыре прогона против четырёх у ветки — 2 979 842 / 2 980 088 / 2 980 133 / 2 979 743, размах 390 против 359. Разницы не видно, обе величины в пределах собственного шума. Значит на 23 575 модулей решает не пропуск выгруженных документов, а расчёт по требованию во время самого анализа. Правку в ветку не вношу — лечить надо гонку целиком (#4429), а не её частный случай. ВопросНаправление лечения выглядит так: доводить типы до неподвижной точки до запуска диагностик, а не полагаться на кэш, который лишь маскирует гонку. Общая часть выделена в #4429. @nixel2007, вопрос по мержу этого PR: ветка расходится сильнее |



Пункт 2.26 методической рекомендации «Типизация кода»: тип возвращаемого значения функции рассчитывается по её телу, а объявленный в документирующем комментарии тип его дополняет, а не заменяет (пункт 2.11: «Типизирующие комментарии не могут переопределять типы, которые рассчитала EDT, а могут только их дополнять»).
Как считается
Точки выхода берутся из графа потока управления — в вершину выхода ведут и операторы
Возврат, и достижимый конец тела:Возврат <выражение>ВызватьИсключениеНеопределено, потому что функция без явного возврата возвращает именно егоГраф берётся из готового
ControlFlowGraphIndex, отдельный обход дерева не нужен. У функции, где значение возвращается на всех путях, лишнегоНеопределеноне появляется.По общим типам верим описанию: у
Массив из Числоиз документирующего комментария состав элементов не размывается платформенным умолчанием отНовый Массивв теле.Где считается
MethodReturnTypeIndex— workspace-scoped индекс, считающий значения в момент построения контекста документа, пока его дерево разбора под рукой. Читать дерево чужого документа нельзя: его вторичные данные могут быть освобождены ради памяти, и результат зависел бы от того, загружен ли документ прямо сейчас. Потребители берут из индекса готовое.Рекурсии по стеку между методами нет: при расчёте тела значения вызванных методов читаются из индекса как есть, а до неподвижной точки их доводит пересчёт по зависимым. Индекс держит связи «метод → потребители»; изменилось значение — волна идёт по цепочке. Набор типов при пересчёте только растёт, поэтому взаимная рекурсия сходится; число проходов ограничено предохранителем.
Заранее считаются только экспортные функции — из чужих модулей видны лишь они. Неэкспортные считаются по запросу внутри своего документа и кэшируются там же.
Освобождение вторичных данных и закрытие документа записи не трогают: содержимое не менялось, дерево символов освобождение переживает, а значение и заводилось ради чтения без дерева разбора. Сбрасывают запись только правка и удаление файла.
Доразрешение после наполнения рабочей области
Документы разбираются параллельно и в произвольном порядке, поэтому вызов в модуль, ещё не зарегистрированный на тот момент, никуда не ведёт. На
ServerContextPopulatedEventоткладыванные методы пересчитываются — с догрузкой документа, если его данные освобождены. Проход идёт в пуле рабочей области: его воркеры несут её контекст, без которого workspace-бины из форков потока недоступны.Кэш выражений
InferredExpressionTypeIndexполучил обратный индекс зависимостей: запись знает, на каких документах построена, и сбрасывается вместе с ними по цепочке (с защитой от круговых зависимостей). Результат, полученный с обрывом цикла, в кэш не пишется — он зависит от точки входа в цикл.Замеры
analyzeпо ssl_3_1, без JFR, одна машина:develop)Прирост замечаний — только там, где он и ожидается от лучшей типизации:
DeprecatedMethodCall+29,CompareWithBoolean+22,AssignToReadOnlyProperty+2. Потерь нет.Замер с JFR и прогон на cpm — в работе, выложу в комментарии.
Тесты
ReturnTypeFromBodyInferenceTest— восемь случаев: объединение веток, дополнение объявленным типом,Неопределенона достижимом конце тела, отсутствие лишнегоНеопределенокогда все пути возвращают,ВызватьИсключение, самоссылка, процедура, чтение тела метода другого модуля. Плюс два теста на обратный индекс зависимостей кэша выражений.Ожидания трёх тестов
InlineTypeCommentInferenceTestприведены к правилу «комментарий дополняет»: заглушкаФункция ВызовФункции() Возврат "stub"теперь честно даётСтрокавдобавок к типу из комментария.🤖 Generated with Claude Code
https://claude.ai/code/session_015awbqkuFMyTddXSsVuoHhc
Summary by CodeRabbit
New Features
Bug Fixes