Skip to content

Commit

Permalink
Merge branch 'main' into python-models-docstring
Browse files Browse the repository at this point in the history
  • Loading branch information
msyyc authored Feb 26, 2025
2 parents ad09dcc + c79e263 commit dcbe244
Show file tree
Hide file tree
Showing 124 changed files with 1,913 additions and 1,193 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/compiler"
---

Add autocomplete of model properties for union type
2 changes: 2 additions & 0 deletions eng/common/pipelines/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pr:
extends:
template: /eng/common/pipelines/templates/1es-redirect.yml
parameters:
BinSkimSettings:
analyzeTargetGlob: +:file|**/*.dll;+:file|**/*.exe;-:f|**/tsp.exe # Flag issue with node binary which we can't fix https://github.com/nodejs/node/issues/42100
stages:
- stage: InitStage
displayName: Initialize
Expand Down
5 changes: 5 additions & 0 deletions eng/tsp-core/pipelines/jobs/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,15 @@ jobs:
}
displayName: "Check if @typespec/http-specs was published"
- script: pnpm -r --filter '@typespec/http-specs...' build
condition: eq(variables['PublishHttpSpecs'], 'true')
displayName: Build spector

- task: AzureCLI@2
displayName: Upload scenario manifest
condition: eq(variables['PublishHttpSpecs'], 'true')
inputs:
workingDirectory: packages/http-specs
azureSubscription: "TypeSpec Storage"
scriptType: "bash"
scriptLocation: "inlineScript"
Expand Down
2 changes: 2 additions & 0 deletions eng/tsp-core/pipelines/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pr: none
extends:
template: /eng/common/pipelines/templates/1es-redirect.yml
parameters:
BinSkimSettings:
analyzeTargetGlob: +:file|**/*.dll;+:file|**/*.exe;-:f|**/tsp.exe # Flag issue with node binary which we can't fix https://github.com/nodejs/node/issues/42100
variables:
- template: /eng/tsp-core/pipelines/templates/variables/globals.yml@self

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
"sync-labels": "tsx ./eng/common/scripts/labels/sync-labels.ts --config ./eng/common/config/labels.ts",
"validate-scenarios": "pnpm -r --filter=@typespec/http-specs run validate-scenarios",
"validate-mock-apis": "pnpm -r --filter=@typespec/http-specs run validate-mock-apis",
"generate-scenarios-summary": "pnpm -r --filter=@typespec/http-specs run generate-scenarios-summary",
"upload-manifest": "pnpm -r --filter=@typespec/http-specs run upload-manifest"
"generate-scenarios-summary": "pnpm -r --filter=@typespec/http-specs run generate-scenarios-summary"
},
"devDependencies": {
"@alloy-js/prettier-plugin-alloy": "^0.1.0",
Expand Down
159 changes: 100 additions & 59 deletions packages/compiler/src/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export interface Checker {
projection: ProjectionNode,
args?: (Type | string | number | boolean)[],
): Type;
resolveIdentifier(node: IdentifierNode): Sym | undefined;
resolveRelatedSymbols(node: IdentifierNode): Sym[] | undefined;
resolveCompletions(node: IdentifierNode): Map<string, TypeSpecCompletionItem>;
createType<T extends Type extends any ? CreateTypeProps : never>(
typeDef: T,
Expand Down Expand Up @@ -412,7 +412,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
getGlobalNamespaceNode,
getMergedSymbol,
cloneType,
resolveIdentifier,
resolveRelatedSymbols,
resolveCompletions,
evalProjection,
project,
Expand Down Expand Up @@ -2545,24 +2545,21 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
return resolver.getSymbolLinks(s);
}

function resolveIdentifier(id: IdentifierNode, mapper?: TypeMapper): Sym | undefined {
function resolveRelatedSymbols(id: IdentifierNode, mapper?: TypeMapper): Sym[] | undefined {
let sym: Sym | undefined;
const { node, kind } = getIdentifierContext(id);

switch (kind) {
case IdentifierKind.ModelExpressionProperty:
case IdentifierKind.ObjectLiteralProperty:
const model = getReferencedModel(node as ModelPropertyNode | ObjectLiteralPropertyNode);
if (model) {
sym = getMemberSymbol(model.node!.symbol, id.sv);
} else {
return undefined;
}
break;
return model
.map((m) => getMemberSymbol(m.node!.symbol, id.sv))
.filter((m): m is Sym => m !== undefined);
case IdentifierKind.ModelStatementProperty:
case IdentifierKind.Declaration:
const links = resolver.getNodeLinks(id);
return links.resolvedSymbol;
return links.resolvedSymbol === undefined ? undefined : [links.resolvedSymbol];
case IdentifierKind.Other:
return undefined;

Expand Down Expand Up @@ -2601,7 +2598,14 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
compilerAssert(false, "Unreachable");
}

return sym?.symbolSource ?? sym;
if (sym) {
if (sym.symbolSource) {
return [sym.symbolSource];
} else {
return [sym];
}
}
return undefined; //sym?.symbolSource ?? sym;
}

function getTemplateDeclarationsForArgument(
Expand All @@ -2621,7 +2625,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker

function getReferencedModel(
propertyNode: ObjectLiteralPropertyNode | ModelPropertyNode,
): Model | undefined {
): Model[] {
type ModelOrArrayValueNode = ArrayLiteralNode | ObjectLiteralNode;
type ModelOrArrayTypeNode = ModelExpressionNode | TupleExpressionNode;
type ModelOrArrayNode = ModelOrArrayValueNode | ModelOrArrayTypeNode;
Expand Down Expand Up @@ -2664,9 +2668,8 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
refType = getReferencedTypeFromConstAssignment(foundNode as ModelOrArrayValueNode);
break;
}
return refType?.kind === "Model" || refType?.kind === "Tuple"
? getNestedModel(refType, path)
: undefined;

return getNestedModel(refType, path);

function pushToModelPath(node: Node, preNode: Node | undefined, path: PathSeg[]) {
if (node.kind === SyntaxKind.ArrayLiteral || node.kind === SyntaxKind.TupleExpression) {
Expand All @@ -2685,38 +2688,75 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
}
}

function getNestedModel(
modelOrTuple: Model | Tuple | undefined,
path: PathSeg[],
): Model | undefined {
let cur: Type | undefined = modelOrTuple;
for (const seg of path) {
function getNestedModel(modelOrTupleOrUnion: Type | undefined, path: PathSeg[]): Model[] {
let cur = modelOrTupleOrUnion;

if (cur && cur.kind !== "Model" && cur.kind !== "Tuple" && cur.kind !== "Union") {
return [];
}

if (path.length === 0) {
// Handle union and model type nesting when path is empty
switch (cur?.kind) {
case "Tuple":
if (
seg.tupleIndex !== undefined &&
seg.tupleIndex >= 0 &&
seg.tupleIndex < cur.values.length
) {
cur = cur.values[seg.tupleIndex];
} else {
return undefined;
}
break;
case "Model":
if (cur.name === "Array" && seg.tupleIndex !== undefined) {
cur = cur.templateMapper?.args[0] as Model;
} else if (cur.name !== "Array" && seg.propertyName) {
cur = cur.properties.get(seg.propertyName)?.type;
} else {
return undefined;
return [cur];
case "Union":
const models: Model[] = [];
for (const variant of cur.variants.values()) {
if (
variant.type.kind === "Model" ||
variant.type.kind === "Tuple" ||
variant.type.kind === "Union"
) {
models.push(...(getNestedModel(variant.type, path) ?? []));
}
}
break;
return models;
default:
return undefined;
return [];
}
}
return cur?.kind === "Model" ? cur : undefined;

const seg = path[0];
switch (cur?.kind) {
case "Tuple":
if (
seg.tupleIndex !== undefined &&
seg.tupleIndex >= 0 &&
seg.tupleIndex < cur.values.length
) {
return getNestedModel(cur.values[seg.tupleIndex], path.slice(1));
} else {
return [];
}

case "Model":
if (cur.name === "Array" && seg.tupleIndex !== undefined) {
cur = cur.templateMapper?.args[0] as Model;
} else if (cur.name !== "Array" && seg.propertyName) {
cur = cur.properties.get(seg.propertyName)?.type;
} else {
return [];
}
return getNestedModel(cur, path.slice(1));

case "Union":
// When seg.property name exists, it means that it is in the union model or tuple,
// and the corresponding model or tuple needs to be found recursively.
const models: Model[] = [];
for (const variant of cur.variants.values()) {
if (
variant.type.kind === "Model" ||
variant.type.kind === "Tuple" ||
variant.type.kind === "Union"
) {
models.push(...(getNestedModel(variant.type, path) ?? []));
}
}
return models;
default:
return [];
}
}

function getReferencedTypeFromTemplateDeclaration(node: ModelOrArrayNode): Type | undefined {
Expand Down Expand Up @@ -2813,14 +2853,14 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
return undefined;
}

const decSym = program.checker.resolveIdentifier(
const decSym = program.checker.resolveRelatedSymbols(
decNode.target.kind === SyntaxKind.MemberExpression ? decNode.target.id : decNode.target,
);
if (!decSym) {
if (!decSym || decSym.length <= 0) {
return undefined;
}

const decDecl: DecoratorDeclarationStatementNode | undefined = decSym.declarations.find(
const decDecl: DecoratorDeclarationStatementNode | undefined = decSym[0].declarations.find(
(x): x is DecoratorDeclarationStatementNode =>
x.kind === SyntaxKind.DecoratorDeclarationStatement,
);
Expand Down Expand Up @@ -2909,24 +2949,25 @@ export function createChecker(program: Program, resolver: NameResolver): Checker
kind === IdentifierKind.ObjectLiteralProperty
) {
const model = getReferencedModel(ancestor as ModelPropertyNode | ObjectLiteralPropertyNode);
if (!model) {
if (model.length <= 0) {
return completions;
}
const curModelNode = ancestor.parent as ModelExpressionNode | ObjectLiteralNode;

for (const prop of walkPropertiesInherited(model)) {
if (
identifier.sv === prop.name ||
!curModelNode.properties.find(
(p) =>
(p.kind === SyntaxKind.ModelProperty ||
p.kind === SyntaxKind.ObjectLiteralProperty) &&
p.id.sv === prop.name,
)
) {
const sym = getMemberSymbol(model.node!.symbol, prop.name);
if (sym) {
addCompletion(prop.name, sym);
for (const curModel of model) {
for (const prop of walkPropertiesInherited(curModel)) {
if (
identifier.sv === prop.name ||
!curModelNode.properties.find(
(p) =>
(p.kind === SyntaxKind.ModelProperty ||
p.kind === SyntaxKind.ObjectLiteralProperty) &&
p.id.sv === prop.name,
)
) {
const sym = getMemberSymbol(curModel.node!.symbol, prop.name);
if (sym) {
addCompletion(prop.name, sym);
}
}
}
}
Expand Down
Loading

0 comments on commit dcbe244

Please sign in to comment.