Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

The `internal` modifier is no longer experimental. Using `internal` will no longer emit an `experimental-feature` warning, and `#suppress "experimental-feature"` directives are no longer needed.
1 change: 0 additions & 1 deletion packages/compiler/lib/prototypes.tsp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace TypeSpec.Prototypes;

#suppress "experimental-feature" "Compiler internal decorator."
internal extern dec getter(target: unknown);

namespace Types {
Expand Down
1 change: 0 additions & 1 deletion packages/compiler/src/core/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ const diagnostics = {
default: paramMessage`${"feature"} is an experimental feature. It may change in the future or be removed. Use with caution and consider providing feedback on this feature.`,
functionDeclarations:
"Function declarations are an experimental feature that may change in the future. Use with caution and consider providing feedback to the TypeSpec team.",
internal: `Internal symbols are experimental and may be changed in a future release. Use with caution. Suppress this message ('#suppress "experimental-feature"') to silence this warning.`,
},
},
"using-invalid-ref": {
Expand Down
14 changes: 0 additions & 14 deletions packages/compiler/src/core/modifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,6 @@ export function checkModifiers(program: Program, node: Declaration): boolean {

let isValid = true;

// Emit experimental warning for any use of the 'internal' modifier.
if (node.modifierFlags & ModifierFlags.Internal) {
const internalModifiers = filterModifiersByFlags(node.modifiers, ModifierFlags.Internal);
for (const modifier of internalModifiers) {
program.reportDiagnostic(
createDiagnostic({
code: "experimental-feature",
messageId: "internal",
target: modifier,
}),
);
}
}

const invalidModifiers = node.modifierFlags & ~compatibility.allowed;

if (invalidModifiers) {
Expand Down
89 changes: 22 additions & 67 deletions packages/compiler/test/checker/internal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@ describe("modifier validation", () => {
];

for (const { keyword, code } of declarationKinds) {
it(`allows 'internal' on ${keyword} declaration (with experimental warning)`, async () => {
it(`allows 'internal' on ${keyword} declaration`, async () => {
const diagnostics = await Tester.diagnose(code);
expectDiagnostics(diagnostics, {
code: "experimental-feature",
severity: "warning",
message: `Internal symbols are experimental and may be changed in a future release. Use with caution. Suppress this message ('#suppress "experimental-feature"') to silence this warning.`,
});
expectDiagnosticEmpty(diagnostics);
});
}

Expand All @@ -32,18 +28,12 @@ describe("modifier validation", () => {
.import("./test.js")
.diagnose(`internal extern dec myDec(target: unknown);`);

// Only the experimental warning, no error
expectDiagnostics(diagnostics, {
code: "experimental-feature",
});
expectDiagnosticEmpty(diagnostics);
});

it("does not allow 'internal' on namespace", async () => {
const diagnostics = await Tester.diagnose(`internal namespace Foo {}`);
expectDiagnostics(diagnostics, [
{
code: "experimental-feature",
},
{
code: "invalid-modifier",
message: "Modifier 'internal' cannot be used on declarations of type 'namespace'.",
Expand All @@ -54,9 +44,6 @@ describe("modifier validation", () => {
it("does not allow 'internal' on blockless namespace", async () => {
const diagnostics = await Tester.diagnose(`internal namespace Foo;`);
expectDiagnostics(diagnostics, [
{
code: "experimental-feature",
},
{
code: "invalid-modifier",
message: "Modifier 'internal' cannot be used on declarations of type 'namespace'.",
Expand Down Expand Up @@ -123,10 +110,7 @@ describe("access control", () => {
model Consumer { x: LibModel }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal scalar from another package", async () => {
Expand All @@ -137,10 +121,7 @@ describe("access control", () => {
model Consumer { x: LibScalar }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal interface from another package", async () => {
Expand All @@ -151,10 +132,7 @@ describe("access control", () => {
interface Consumer extends LibIface {}
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal union from another package", async () => {
Expand All @@ -165,10 +143,7 @@ describe("access control", () => {
model Consumer { x: LibUnion }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal op from another package", async () => {
Expand All @@ -179,10 +154,7 @@ describe("access control", () => {
op consumer is libOp;
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal enum from another package", async () => {
Expand All @@ -193,10 +165,7 @@ describe("access control", () => {
model Consumer { x: LibEnum }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal alias from another package", async () => {
Expand All @@ -210,10 +179,7 @@ describe("access control", () => {
model Consumer { x: LibAlias }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal model in a namespace from another package", async () => {
Expand All @@ -227,10 +193,7 @@ describe("access control", () => {
model Consumer { x: MyLib.Secret }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects access to internal model via 'using' from another package", async () => {
Expand All @@ -245,10 +208,7 @@ describe("access control", () => {
model Consumer { x: Secret }
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});

it("rejects extending an internal model from another package", async () => {
Expand All @@ -259,10 +219,7 @@ describe("access control", () => {
model Consumer extends Base {}
`);

expectDiagnostics(diagnostics, [
{ code: "invalid-ref", message: /internal/ },
{ code: "experimental-feature" },
]);
expectDiagnostics(diagnostics, [{ code: "invalid-ref", message: /internal/ }]);
});
});

Expand All @@ -273,8 +230,7 @@ describe("access control", () => {
model Consumer { x: Secret }
`);

// Only the experimental warning, no access error
expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});

it("allows access to internal enum within the same project", async () => {
Expand All @@ -283,7 +239,7 @@ describe("access control", () => {
model Consumer { x: Status }
`);

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});

it("allows access to internal model across files in the same project", async () => {
Expand All @@ -297,7 +253,7 @@ describe("access control", () => {
`,
});

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});

it("allows access to internal op within the same project", async () => {
Expand All @@ -306,7 +262,7 @@ describe("access control", () => {
op consumer is helper;
`);

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});

it("allows access to internal scalar within the same project", async () => {
Expand All @@ -315,7 +271,7 @@ describe("access control", () => {
model Consumer { x: MyScalar }
`);

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});

it("allows access to internal alias within the same project", async () => {
Expand All @@ -324,7 +280,7 @@ describe("access control", () => {
model Consumer { x: Shorthand }
`);

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});
});

Expand All @@ -343,8 +299,7 @@ describe("access control", () => {
model Consumer { x: Public }
`);

// experimental-feature for InternalHelper in the library, no access error
expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});
});

Expand All @@ -361,7 +316,7 @@ describe("access control", () => {
model Consumer { x: PublicModel }
`);

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});

it("allows access to non-internal model in a namespace from another package", async () => {
Expand All @@ -378,7 +333,7 @@ describe("access control", () => {
model Consumer { x: MyLib.PublicModel }
`);

expectDiagnostics(diagnostics, { code: "experimental-feature" });
expectDiagnosticEmpty(diagnostics);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,6 @@ The `internal` modifier can be combined with the `extern` modifier on decorator
internal extern dec myInternalDecorator(target: unknown);
```

### Suppressing the experimental warning

Since access modifiers are currently experimental, using `internal` will emit a warning. You can suppress this warning with a `#suppress` directive:

```typespec
#suppress "experimental-feature"
internal model MyInternalModel {}
```

## Why not `namespace`?

The `internal` modifier is not supported on namespaces because namespaces in TypeSpec are **open and merged** across files. A namespace declared in one file can be extended in another file — potentially across library boundaries. Applying `internal` to a namespace would create ambiguity about which parts of the namespace are internal and which are public. Instead, mark individual declarations within a namespace as `internal`.
Expand Down
Loading