implements PLN-0110

This commit is contained in:
bQUARKz 2026-07-15 09:45:49 +01:00
parent fee89d7522
commit da00e9c50c
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
5 changed files with 163 additions and 5 deletions

View File

@ -9,7 +9,7 @@
{"type":"discussion","id":"DSC-0058","status":"open","ticket":"multi-frontend-serializable-ir","title":"Manter a IR comum serializavel por design","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","ir","backend","serialization","multi-frontend"],"agendas":[{"id":"AGD-0061","file":"AGD-0061-multi-frontend-serializable-ir.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0057","status":"open","ticket":"multi-frontend-frontend-backend-contract","title":"Estabilizar contrato entre frontend e backend comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","ir","backend","multi-frontend"],"agendas":[{"id":"AGD-0060","file":"AGD-0060-multi-frontend-frontend-backend-contract.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0056","status":"open","ticket":"multi-frontend-remove-pbs-branches","title":"Remover verificacoes explicitas de PBS do codigo comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","studio","frontend","coupling","multi-frontend"],"agendas":[{"id":"AGD-0059","file":"AGD-0059-multi-frontend-remove-pbs-branches.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0055","status":"in_progress","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[{"id":"AGD-0058","file":"AGD-0058-multi-frontend-compiler-vs-language-services.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0041","file":"DEC-0041-separate-frontend-compilation-from-editorial-language-services.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0058"}],"plans":[{"id":"PLN-0107","file":"PLN-0107-specify-frontend-compiler-and-editorial-capability-boundary.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]},{"id":"PLN-0108","file":"PLN-0108-move-pbs-editor-assistance-behind-optional-language-service.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]},{"id":"PLN-0109","file":"PLN-0109-make-lsp-editorial-calls-capability-aware.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]},{"id":"PLN-0110","file":"PLN-0110-add-compile-only-frontend-and-absent-capability-conformance-coverage.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0055","status":"in_progress","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[{"id":"AGD-0058","file":"AGD-0058-multi-frontend-compiler-vs-language-services.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0041","file":"DEC-0041-separate-frontend-compilation-from-editorial-language-services.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0058"}],"plans":[{"id":"PLN-0107","file":"PLN-0107-specify-frontend-compiler-and-editorial-capability-boundary.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]},{"id":"PLN-0108","file":"PLN-0108-move-pbs-editor-assistance-behind-optional-language-service.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]},{"id":"PLN-0109","file":"PLN-0109-make-lsp-editorial-calls-capability-aware.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]},{"id":"PLN-0110","file":"PLN-0110-add-compile-only-frontend-and-absent-capability-conformance-coverage.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0041"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0054","status":"done","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0055","file":"discussion/lessons/DSC-0054-multi-frontend-provider-contract/LSN-0055-static-frontend-providers-before-plugin-architecture.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15"}]}
{"type":"discussion","id":"DSC-0053","status":"open","ticket":"pbs-lsp-call-and-type-hierarchy","title":"PBS LSP Call and Type Hierarchy","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","call-hierarchy","type-hierarchy"],"agendas":[{"id":"AGD-0056","file":"AGD-0056-pbs-lsp-call-and-type-hierarchy.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0052","status":"open","ticket":"pbs-lsp-document-links","title":"PBS LSP Document Links","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","document-links","imports"],"agendas":[{"id":"AGD-0055","file":"AGD-0055-pbs-lsp-document-links.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}

View File

@ -2,7 +2,7 @@
id: PLN-0110
ticket: multi-frontend-compiler-vs-language-services
title: Add compile only frontend and absent capability conformance coverage
status: open
status: done
created: 2026-07-15
ref_decisions: [DEC-0041]
tags: [compiler, compiler-general, studio, lsp, editor, frontend, multi-frontend]

View File

@ -1,12 +1,23 @@
package p.studio.compiler.specs;
import org.junit.jupiter.api.Test;
import p.studio.compiler.messages.BuildingIssueSink;
import p.studio.compiler.messages.FrontendPhaseContext;
import p.studio.compiler.models.FrontendSpec;
import p.studio.compiler.models.IRBackend;
import p.studio.compiler.services.FrontendPhaseService;
import p.studio.compiler.services.FrontendProvider;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.utilities.logs.LogAggregator;
import p.studio.utilities.structures.ReadOnlySet;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
class FrontendProviderBoundaryTest {
@ -37,6 +48,45 @@ class FrontendProviderBoundaryTest {
}
}
@Test
void commonModulesMustNotInstantiatePbsEditorialSupportServiceDirectly() throws IOException {
final var repoRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize());
final var sourceRoots = List.of(
repoRoot.resolve("prometeu-compiler/prometeu-build-pipeline/src/main/java"),
repoRoot.resolve("prometeu-compiler/prometeu-deps/src/main/java"),
repoRoot.resolve("prometeu-lsp/prometeu-lsp-v1/src/main/java"),
repoRoot.resolve("prometeu-studio/src/main/java"),
repoRoot.resolve("prometeu-app/src/main/java"));
for (final var sourceRoot : sourceRoots) {
if (!Files.exists(sourceRoot)) {
continue;
}
try (final var files = Files.walk(sourceRoot)) {
final var violations = files
.filter(path -> path.toString().endsWith(".java"))
.filter(path -> contains(path, "new PbsEditorialSupportService("))
.toList();
if (!violations.isEmpty()) {
fail("common modules must resolve PBS editor behavior through FrontendProvider.languageService(): " + violations);
}
}
}
}
@Test
void compileOnlyFrontendProviderMayOmitLanguageService() {
final FrontendProvider provider = new CompileOnlyFrontendProvider();
assertTrue(provider.languageService().isEmpty());
assertNotNull(provider.compiler().compile(
null,
DiagnosticSink.empty(),
LogAggregator.empty(),
BuildingIssueSink.empty()));
}
private boolean contains(final Path path, final String needle) {
try {
return Files.readString(path).contains(needle);
@ -56,4 +106,34 @@ class FrontendProviderBoundaryTest {
fail("unable to locate repository root from " + start);
return start;
}
private static final class CompileOnlyFrontendProvider implements FrontendProvider {
private final FrontendSpec specification = FrontendSpec.builder()
.languageId("compile-only")
.allowedExtensions(ReadOnlySet.from("co"))
.sourceRoots(ReadOnlySet.from("src"))
.build();
private final FrontendPhaseService compiler = new CompileOnlyFrontendPhaseService();
@Override
public FrontendSpec specification() {
return specification;
}
@Override
public FrontendPhaseService compiler() {
return compiler;
}
}
private static final class CompileOnlyFrontendPhaseService implements FrontendPhaseService {
@Override
public IRBackend compile(
final FrontendPhaseContext request,
final DiagnosticSink diagnostics,
final LogAggregator logs,
final BuildingIssueSink issues) {
return IRBackend.builder().build();
}
}
}

View File

@ -6,6 +6,7 @@ import p.studio.compiler.PBSFrontendLanguageService;
import p.studio.compiler.messages.*;
import p.studio.compiler.models.*;
import p.studio.compiler.pbs.semantics.*;
import p.studio.compiler.services.FrontendProvider;
import p.studio.compiler.services.PBSFrontendPhaseService;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.utilities.SourceProviderFactory;
@ -19,9 +20,19 @@ import p.studio.utilities.structures.ReadOnlyList;
import java.net.URI;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Function;
public final class CompilerLanguageServiceBridge implements LanguageServiceBridge {
private final AssetSurfaceContextLoader assetSurfaceContextLoader = new AssetSurfaceContextLoader();
private final Function<String, FrontendProvider> frontendProviderResolver;
public CompilerLanguageServiceBridge() {
this(FrontendRegistryService::require);
}
CompilerLanguageServiceBridge(final Function<String, FrontendProvider> frontendProviderResolver) {
this.frontendProviderResolver = Objects.requireNonNull(frontendProviderResolver, "frontendProviderResolver");
}
@Override
public BaselineServerDescription describeServer(final LspProjectContext context) {
@ -349,9 +360,8 @@ public final class CompilerLanguageServiceBridge implements LanguageServiceBridg
return frontendProvider(context).specification();
}
private p.studio.compiler.services.FrontendProvider frontendProvider(final LspProjectContext context) {
return FrontendRegistryService.getProvider(context.languageId())
.orElseThrow(() -> new IllegalArgumentException("no frontend registered for languageId: " + context.languageId()));
private FrontendProvider frontendProvider(final LspProjectContext context) {
return frontendProviderResolver.apply(context.languageId());
}
private Optional<PBSFrontendLanguageService> pbsLanguageService(final LspProjectContext context) {

View File

@ -1,8 +1,17 @@
package p.studio.lsp.services.compiler;
import org.junit.jupiter.api.Test;
import p.studio.compiler.messages.BuildingIssueSink;
import p.studio.compiler.messages.FrontendPhaseContext;
import p.studio.compiler.models.FrontendSpec;
import p.studio.compiler.models.IRBackend;
import p.studio.compiler.services.FrontendPhaseService;
import p.studio.compiler.services.FrontendProvider;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.lsp.messages.BaselineIssueSeverity;
import p.studio.lsp.messages.LspProjectContext;
import p.studio.utilities.logs.LogAggregator;
import p.studio.utilities.structures.ReadOnlySet;
import java.nio.file.Files;
import java.nio.file.Path;
@ -12,6 +21,35 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class CompilerLanguageServiceBridgeTest {
@Test
void editorialEndpointsReturnNeutralResponsesWhenLanguageServiceIsAbsent() {
final var provider = new CompileOnlyFrontendProvider();
final CompilerLanguageServiceBridge bridge = new CompilerLanguageServiceBridge(languageId -> {
if (provider.specification().getLanguageId().equals(languageId)) {
return provider;
}
throw new IllegalArgumentException("no frontend registered for languageId: " + languageId);
});
final LspProjectContext context = new LspProjectContext("main", provider.specification().getLanguageId(), Path.of("."));
final String documentUri = Path.of("demo.co").toUri().toString();
final var completion = bridge.completion(context, documentUri, "fn main() {}", 0, 0);
assertFalse(completion.incomplete());
assertTrue(completion.items().isEmpty());
final var hover = bridge.hover(context, documentUri, "fn main() {}", 0, 0);
assertEquals("No symbol information.", hover.markdown());
final var signatureHelp = bridge.signatureHelp(context, documentUri, "fn main() {}", 0, 0);
assertTrue(signatureHelp.signatures().isEmpty());
assertEquals(0, signatureHelp.activeSignature());
assertEquals(0, signatureHelp.activeParameter());
final var semanticTokens = bridge.semanticTokens(context, documentUri, "fn main() {}");
assertTrue(semanticTokens.legend().isEmpty());
assertTrue(semanticTokens.tokens().isEmpty());
}
@Test
void analyzeDocumentReturnsCompilerDiagnosticsForOverlayContent() {
final Path projectRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize())
@ -305,4 +343,34 @@ class CompilerLanguageServiceBridgeTest {
}
throw new IllegalStateException("unable to locate repository root from " + start);
}
private static final class CompileOnlyFrontendProvider implements FrontendProvider {
private final FrontendSpec specification = FrontendSpec.builder()
.languageId("compile-only")
.allowedExtensions(ReadOnlySet.from("co"))
.sourceRoots(ReadOnlySet.from("src"))
.build();
private final FrontendPhaseService compiler = new CompileOnlyFrontendPhaseService();
@Override
public FrontendSpec specification() {
return specification;
}
@Override
public FrontendPhaseService compiler() {
return compiler;
}
}
private static final class CompileOnlyFrontendPhaseService implements FrontendPhaseService {
@Override
public IRBackend compile(
final FrontendPhaseContext request,
final DiagnosticSink diagnostics,
final LogAggregator logs,
final BuildingIssueSink issues) {
return IRBackend.builder().build();
}
}
}