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
Expand Up @@ -2,6 +2,7 @@ import { resolveProviderNamespace } from "@azure-tools/typespec-azure-resource-m
import {
createRule,
getDiscriminator,
getProperty,
isNeverType,
isNullType,
paramMessage,
Expand Down Expand Up @@ -204,16 +205,23 @@ function getPayloadProperties(program: Program, model: Model): Map<string, Paylo

for (let current: Model | undefined = model; current !== undefined; current = current.baseModel) {
for (const property of current.properties.values()) {
// A derived declaration shadows its base even when its payload type is never.
if (getProperty(model, property.name) !== property) {
continue;
}
const jsonName = resolveEncodedName(program, property, "application/json");
if (!properties.has(jsonName) && !isNeverType(property.type)) {
properties.set(jsonName, { target: property, type: property.type });
}
}
}

// Resolve the complete authored shape before filling in discriminator metadata.
for (let current: Model | undefined = model; current !== undefined; current = current.baseModel) {
const discriminator = getDiscriminator(program, current);
if (
discriminator !== undefined &&
!current.properties.has(discriminator.propertyName) &&
getProperty(model, discriminator.propertyName) === undefined &&
!properties.has(discriminator.propertyName)
) {
properties.set(discriminator.propertyName, { target: current });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
"schemaVersion": 2,
"rule": "ConsistentPatchProperties",
"specsCommit": "f6b53f105b95da05276530a0754a1c71b4f16397",
"generatedAt": "2026-09-09T09:24:28.545Z",
"completedAt": "2026-09-09T17:28:59.7719922+08:00",
"sourceBaseCommit": "29c4a87b0799092be0ede854ffdf23a0a9648795",
"generatedAt": "2026-09-11T05:20:19.336Z",
"completedAt": "2026-09-11T13:23:02.8497634+08:00",
"sourceBaseCommit": "ca14b58cdf4dfc8b1fe835c16bf3bbb30f6bdb93",
"sourceBranch": "feature/lintdiff-consistent-patch-properties-native",
"sourceState": "Uncommitted native-boundary repair, before final formatting",
"localLinterFingerprint": "sha256:33b2527404c4fb88cc32a37f2dd7cb1d58b17789308bdce020e23635019bd6f1",
"sourceState": "Uncommitted inherited authored-discriminator precedence repair, after source formatting",
"localLinterFingerprint": "sha256:00fc4e58bfeaff393f9d36ad3bca3cd43da8cd4b00d7e46c811fb19a43e84130",
"diagnosticMultisetUnchangedFrom": "2026-09-11T04:09:06.638Z",
"diagnosticMultisetIdentity": "project + source file + line + column + message + severity, including multiplicity",
"coverageKind": "partial",
"command": "pnpm --dir packages/typespec-lintdiff specs:typespec --specs-repo C:\\dev\\worktrees\\azure-rest-api-specs-lintdiff-consistent-patch-properties --concurrency 6",
"fullRun": true,
"durationMs": 4678050,
"durationMs": 1289034,
"sourceProjectCount": 468,
"successfulProjectCount": 462,
"failedProjectCount": 6,
Expand Down Expand Up @@ -58,7 +60,7 @@
"uncertainty": {
"informatica": "Selected Swagger schema mismatches are verified; the validator-side omission mechanism is not isolated.",
"versions": "The remaining 306 diagnostics are not a globally latest-version-projected population.",
"scope": "Four fixtures prove intentional native/emitter contract differences despite unchanged corpus counts."
"scope": "Four scope fixtures, two inherited-never fixtures, and two inherited-discriminator fixtures prove intentional native/emitter contract differences despite unchanged corpus findings."
},
"overlapProjects": [
"specification/apimanagement/resource-manager/Microsoft.ApiManagement/ApiManagement",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "violation": false }
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import "../../lib/imports.tsp";

using TypeSpec.Http;
using Azure.ResourceManager;

@armProviderNamespace
@service(#{ title: "Inherited encoded PATCH discriminator" })
namespace Microsoft.TestService;

/** Authored payload inherited by the discriminated model. */
model Base {
/** Object represented as kind in the JSON payload. */
@encodedName("application/json", "kind")
payload?: {
/** Property missing from the scalar resource kind. */
extra?: string;
};
}

/** Discriminator metadata must not replace the inherited authored object. */
@discriminator("kind")
model Details extends Base {}

interface Operations extends Azure.ResourceManager.Operations {}

/** Update the resource. */
@route("/widgets")
@patch
op update(
/** Update body. */
@body body: {
/** Update details. */
details?: Details;
},
): {
/** Resource details. */
details?: {
/** Scalar kind. */
kind?: string;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
"swagger": "2.0",
"info": {
"title": "Inherited encoded PATCH discriminator",
"version": "0000-00-00",
"x-typespec-generated": [
{
"emitter": "@azure-tools/typespec-autorest"
}
]
},
"schemes": [
"https"
],
"host": "management.azure.com",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"security": [
{
"azure_auth": [
"user_impersonation"
]
}
],
"securityDefinitions": {
"azure_auth": {
"type": "oauth2",
"description": "Azure Active Directory OAuth2 Flow.",
"flow": "implicit",
"authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize",
"scopes": {
"user_impersonation": "impersonate your user account"
}
}
},
"tags": [
{
"name": "Operations"
}
],
"paths": {
"/providers/Microsoft.TestService/operations": {
"get": {
"operationId": "Operations_List",
"tags": [
"Operations"
],
"description": "List the operations for the provider",
"parameters": [
{
"$ref": "../../../../../common-types/resource-management/v3/types.json#/parameters/ApiVersionParameter"
}
],
"responses": {
"200": {
"description": "Azure operation completed successfully.",
"schema": {
"$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/OperationListResult"
}
},
"default": {
"description": "An unexpected error response.",
"schema": {
"$ref": "../../../../../common-types/resource-management/v3/types.json#/definitions/ErrorResponse"
}
}
},
"x-ms-pageable": {
"nextLinkName": "nextLink"
}
}
},
"/widgets": {
"patch": {
"operationId": "Update",
"description": "Update the resource.",
"parameters": [
{
"name": "body",
"in": "body",
"description": "Update body.",
"required": true,
"schema": {
"type": "object",
"properties": {
"details": {
"$ref": "#/definitions/Details",
"description": "Update details."
}
}
}
}
],
"responses": {
"200": {
"description": "The request has succeeded.",
"schema": {
"type": "object",
"properties": {
"details": {
"type": "object",
"description": "Resource details.",
"properties": {
"kind": {
"type": "string",
"description": "Scalar kind."
}
}
}
}
}
}
}
}
}
},
"definitions": {
"Base": {
"type": "object",
"description": "Authored payload inherited by the discriminated model.",
"properties": {
"kind": {
"type": "object",
"description": "Object represented as kind in the JSON payload.",
"properties": {
"extra": {
"type": "string",
"description": "Property missing from the scalar resource kind."
}
},
"x-ms-client-name": "payload"
}
}
},
"Details": {
"type": "object",
"description": "Discriminator metadata must not replace the inherited authored object.",
"properties": {
"kind": {
"type": "string",
"description": "Discriminator property for Details."
}
},
"discriminator": "kind",
"required": [
"kind"
],
"allOf": [
{
"$ref": "#/definitions/Base"
}
]
}
},
"parameters": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[
{
"code": "@azure-tools/typespec-azure-core/require-versioned",
"severity": "warning",
"message": "Azure services should use the versioning library to define versions for their services. Add the '@versioned' decorator to the service namespace."
},
{
"code": "@azure-tools/typespec-azure-resource-manager/arm-common-types-version",
"severity": "warning",
"message": "Specify the ARM common-types version using the @armCommonTypesVersion decorator on the service namespace or on each version of the service version enum."
},
{
"code": "tsp-lintdiff-local-linter/latest-version-of-common-types-must-be-used",
"severity": "warning",
"message": "Use the latest ARM common-types version 'v6' instead of 'v3'."
},
{
"code": "tsp-lintdiff-local-linter/consistent-patch-properties",
"severity": "warning",
"message": "The property 'details.kind.extra' in the request body either does not appear in the resource model or is nested at the wrong level."
},
{
"code": "@azure-tools/typespec-azure-core/composition-over-inheritance",
"severity": "warning",
"message": "Model 'Details' is extending 'Microsoft.TestService.Base' that doesn't define a discriminator. If 'Microsoft.TestService.Base' is meant to be used:\n - For composition consider using spread `...` or `model is` instead.\n - As a polymorphic relation, add the `@discriminator` decorator on the base model."
},
{
"code": "@azure-tools/typespec-azure-core/no-string-discriminator",
"severity": "warning",
"message": "Use an extensible union instead of a plain string (ex: `union PetKind { cat: \"cat\", dog: \"dog\", string };`)"
},
{
"code": "@azure-tools/typespec-azure-resource-manager/no-empty-model",
"severity": "warning",
"message": "Properties with type:object must have definition of a reference model."
},
{
"code": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation",
"severity": "warning",
"message": "All operations must be inside an interface declaration."
},
{
"code": "@azure-tools/typespec-azure-resource-manager/arm-resource-operation",
"severity": "warning",
"message": "All Resource operations must use an api-version parameter. Please include Azure.ResourceManager.ApiVersionParameter in the operation parameter list using the spread (...ApiVersionParameter) operator, or using one of the common resource parameter models."
},
{
"code": "tsp-lintdiff-local-linter/avoid-anonymous-parameter",
"severity": "warning",
"message": "Operation request body should use a named model instead of an anonymous inline model expression."
},
{
"code": "tsp-lintdiff-local-linter/avoid-anonymous-types",
"severity": "warning",
"message": "Operation response body should use a named model instead of an anonymous inline model expression."
},
{
"code": "tsp-lintdiff-local-linter/patch-body-parameters-schema",
"severity": "warning",
"message": "Properties of a PATCH request body must not be required, property:details.kind."
},
{
"code": "tsp-lintdiff-local-linter/xms-examples-required",
"severity": "warning",
"message": "Please provide x-ms-examples describing minimum/maximum property set for response/request payloads for operations."
},
{
"code": "@azure-tools/typespec-azure-core/no-unnamed-types",
"severity": "warning",
"message": "Anonymous model should be defined as a named model declaration."
},
{
"code": "@azure-tools/typespec-azure-core/no-unnamed-types",
"severity": "warning",
"message": "Anonymous model should be defined as a named model declaration."
},
{
"code": "@azure-tools/typespec-azure-core/no-unnamed-types",
"severity": "warning",
"message": "Anonymous model should be defined as a named model declaration."
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"violation": false,
"validatorDiagnostics": [{ "code": "ConsistentPatchProperties", "count": 1 }]
}
Loading