fix(xdto-compile,xdto-validate): не терять конструкции молча; вычитать инструкции

xdto-compile терял свойства без единого слова: на реалистичной чужой схеме
из шести объявленных доезжало одно. Вложенные xs:sequence/xs:choice теперь
уплощаются (модель хранит плоский список), xs:all трактуется как
последовательность, xs:group и xs:attributeGroup раскрываются по ссылке —
и о каждом приближении навык пишет предупреждение. Молчаливая потеря — тот же
класс дефекта, что мы ловим у платформы, лечится так же: сообщением, не отказом.

xdto-validate получил проверки на грабли, найденные при разработке: порядок
элементов верхнего уровня (платформа отвергает пакет, не называя причины),
конфликты объявлений (name+ref, type+вложенный тип, тип без разновидности),
несовпадение рода базового типа, дубли имён свойств.

Новые правила прогнаны по всем 760 пакетам выгрузок: всё, что породила
платформа, валидно по определению, поэтому каждая ошибка там — ошибка правила.
Первый прогон дал 7, и все три класса оказались реальным поведением платформы:
length вместе с minLength/maxLength встречается, два пакета делят один
targetNamespace (Envelope и SOAP_Envelope_1_1 в БП), form="Text" называется
не только __content. Правила понижены до предупреждений либо сняты. Заодно
убран шум: предупреждение о неиспользуемом import срабатывало на четверти
корпуса — теперь только вместе с anyType, где оно и означает проблему.
Итог: 0 ошибок на корпусе, предупреждений 53 вместо 242.

Инструкции переписаны под читателя-исполнителя: убраны детали реализации
и наши мерки, каталог проверок валидатора (его вывод самодостаточен),
локальные пути в примерах заменены нейтральными. Таблица соответствий
XSD и справочник аннотаций вынесены в xdto-compile/xsd-reference.md.

Round-trip 760/760 сохранён, паритет PS/PY сохранён.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-25 21:48:48 +03:00
co-authored by Claude Opus 5
parent d05aef54b4
commit 7ca6dfa6b2
24 changed files with 1156 additions and 132 deletions
@@ -0,0 +1,14 @@
{
"name": "вложенные частицы и группы: уплощение с предупреждениями, ни одно свойство не потеряно",
"caseFiles": ["nested-particles.xsd"],
"params": { "xsdFile": "nested-particles.xsd" },
"validatePath": "XDTOPackages/nested_particles",
"expect": {
"files": ["XDTOPackages/nested_particles/Ext/Package.bin"],
"stdoutContains": [
"вложенная xs:choice уплощена",
"кратность на вложенной частице",
"xs:all трактуется как последовательность"
]
}
}
@@ -0,0 +1,28 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="urn:t" targetNamespace="urn:t">
<xs:group name="Общие">
<xs:sequence><xs:element name="ИзГруппы" type="xs:string"/></xs:sequence>
</xs:group>
<xs:attributeGroup name="Служебные">
<xs:attribute name="Версия" type="xs:string"/>
</xs:attributeGroup>
<xs:complexType name="Документ">
<xs:sequence>
<xs:element name="Первый" type="xs:string"/>
<xs:choice>
<xs:element name="ВариантА" type="xs:string"/>
<xs:element name="ВариантБ" type="xs:string"/>
</xs:choice>
<xs:group ref="tns:Общие"/>
<xs:sequence maxOccurs="unbounded">
<xs:element name="Повторяемый" type="xs:string"/>
</xs:sequence>
</xs:sequence>
<xs:attributeGroup ref="tns:Служебные"/>
</xs:complexType>
<xs:complexType name="Всё">
<xs:all><xs:element name="Любой" type="xs:string"/></xs:all>
</xs:complexType>
<xs:complexType name="ЧерезГруппу">
<xs:group ref="tns:Общие"/>
</xs:complexType>
</xs:schema>
@@ -0,0 +1,252 @@
<?xml version="1.0" encoding="utf-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<Configuration uuid="UUID-001">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>UUID-002</xr:ClassId>
<xr:ObjectId>UUID-003</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-004</xr:ClassId>
<xr:ObjectId>UUID-005</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-006</xr:ClassId>
<xr:ObjectId>UUID-007</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-008</xr:ClassId>
<xr:ObjectId>UUID-009</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-010</xr:ClassId>
<xr:ObjectId>UUID-011</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-012</xr:ClassId>
<xr:ObjectId>UUID-013</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-014</xr:ClassId>
<xr:ObjectId>UUID-015</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<Name>TestConfig</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>TestConfig</v8:content>
</v8:item>
</Synonym>
<Comment />
<NamePrefix />
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
<AdditionalFullTextSearchDictionaries />
<CommonSettingsStorage />
<ReportsUserSettingsStorage />
<ReportsVariantsStorage />
<FormDataSettingsStorage />
<DynamicListsUserSettingsStorage />
<URLExternalDataStorage />
<Content />
<DefaultReportForm />
<DefaultReportVariantForm />
<DefaultReportSettingsForm />
<DefaultReportAppearanceTemplate />
<DefaultDynamicListSettingsForm />
<DefaultSearchForm />
<DefaultDataHistoryChangeHistoryForm />
<DefaultDataHistoryVersionDataForm />
<DefaultDataHistoryVersionDifferencesForm />
<DefaultCollaborationSystemUsersChoiceForm />
<RequiredMobileApplicationPermissions />
<UsedMobileApplicationFunctionalities>
<app:functionality>
<app:functionality>Biometrics</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Location</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundLocation</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BluetoothPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>WiFiPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Contacts</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Calendars</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PushNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>LocalNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InAppPurchases</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PersonalComputerFileExchange</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Ads</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NumberDialing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AutoSendSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ReceiveSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SMSLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Camera</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Microphone</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>MusicLibrary</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PictureAndVideoLibraries</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InstallPackages</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>OSBackup</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ApplicationUsageStatistics</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BarcodeScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioRecording</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllFilesAccess</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Videoconferences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NFC</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>DocumentScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SpeechToText</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Geofences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>IncomingShareRequests</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllIncomingShareRequestsTypesProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
</UsedMobileApplicationFunctionalities>
<StandaloneConfigurationRestrictionRoles />
<MobileApplicationURLs />
<AllowedIncomingShareRequestTypes />
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
<DefaultInterface />
<DefaultStyle />
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation />
<DetailedInformation />
<Copyright />
<VendorInformationAddress />
<ConfigurationInformationAddress />
<DataLockControlMode>Managed</DataLockControlMode>
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
<ModalityUseMode>DontUse</ModalityUseMode>
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultConstantsForm />
</Properties>
<ChildObjects>
<Language>Русский</Language>
<XDTOPackage>nested_particles</XDTOPackage>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<ClientApplicationInterface xmlns="http://v8.1c.ru/8.2/managed-application/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InterfaceLayouter">
<top>
<panel id="UUID-001">
<uuid>UUID-002</uuid>
</panel>
</top>
<left>
<panel id="UUID-003">
<uuid>UUID-004</uuid>
</panel>
</left>
<panelDef id="UUID-004"/>
<panelDef id="UUID-005"/>
<panelDef id="UUID-006"/>
<panelDef id="UUID-002"/>
<panelDef id="UUID-007"/>
</ClientApplicationInterface>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<Language uuid="UUID-001">
<Properties>
<Name>Русский</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Русский</v8:content>
</v8:item>
</Synonym>
<Comment/>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<XDTOPackage uuid="UUID-001">
<Properties>
<Name>nested_particles</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>nested_particles</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Namespace>urn:t</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,16 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:t">
<objectType name="Документ">
<property name="Версия" type="xs:string" form="Attribute"/>
<property name="Первый" type="xs:string"/>
<property name="ВариантА" type="xs:string"/>
<property name="ВариантБ" type="xs:string"/>
<property name="ИзГруппы" type="xs:string"/>
<property name="Повторяемый" type="xs:string"/>
</objectType>
<objectType name="Всё">
<property name="Любой" type="xs:string"/>
</objectType>
<objectType name="ЧерезГруппу">
<property name="ИзГруппы" type="xs:string"/>
</objectType>
</package>
@@ -0,0 +1,28 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="urn:t" targetNamespace="urn:t">
<xs:group name="Общие">
<xs:sequence><xs:element name="ИзГруппы" type="xs:string"/></xs:sequence>
</xs:group>
<xs:attributeGroup name="Служебные">
<xs:attribute name="Версия" type="xs:string"/>
</xs:attributeGroup>
<xs:complexType name="Документ">
<xs:sequence>
<xs:element name="Первый" type="xs:string"/>
<xs:choice>
<xs:element name="ВариантА" type="xs:string"/>
<xs:element name="ВариантБ" type="xs:string"/>
</xs:choice>
<xs:group ref="tns:Общие"/>
<xs:sequence maxOccurs="unbounded">
<xs:element name="Повторяемый" type="xs:string"/>
</xs:sequence>
</xs:sequence>
<xs:attributeGroup ref="tns:Служебные"/>
</xs:complexType>
<xs:complexType name="Всё">
<xs:all><xs:element name="Любой" type="xs:string"/></xs:all>
</xs:complexType>
<xs:complexType name="ЧерезГруппу">
<xs:group ref="tns:Общие"/>
</xs:complexType>
</xs:schema>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:v8="http://v8.1c.ru/8.1/data/core" version="2.17">
<XDTOPackage uuid="11111111-2222-3333-4444-555555555555">
<Properties>
<Name>badorder</Name>
<Synonym><v8:item><v8:lang>ru</v8:lang><v8:content>badorder</v8:content></v8:item></Synonym>
<Comment/>
<Namespace>urn:test:badorder</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,6 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:test:badorder">
<objectType name="Документ">
<property name="Поле" type="xs:string"/>
</objectType>
<property xmlns:d2p1="urn:test:badorder" name="Корень" type="d2p1:Документ"/>
</package>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:v8="http://v8.1c.ru/8.1/data/core" version="2.17">
<XDTOPackage uuid="11111111-2222-3333-4444-555555555555">
<Properties>
<Name>conflict</Name>
<Synonym><v8:item><v8:lang>ru</v8:lang><v8:content>conflict</v8:content></v8:item></Synonym>
<Comment/>
<Namespace>urn:test:conflict</Namespace>
</Properties>
</XDTOPackage>
</MetaDataObject>
@@ -0,0 +1,11 @@
<package xmlns="http://v8.1c.ru/8.1/xdto" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="urn:test:conflict">
<valueType name="Простой" base="xs:string"/>
<objectType name="Документ" xmlns:d2p1="urn:test:conflict" base="d2p1:Простой">
<property xmlns:d3p1="urn:test:conflict" name="Поле" ref="d3p1:Поле" type="xs:string"/>
<property name="Дубль" type="xs:string"/>
<property name="Дубль" type="xs:string"/>
<property name="СВложенным" type="xs:string">
<typeDef base="xs:string"/>
</property>
</objectType>
</package>
@@ -8,7 +8,7 @@
"expect": {
"stdoutContains": [
"тихая деградация",
"ни один тип из этого пространства имён не используется"
"признак неразрешённой зависимости"
]
},
"noSnapshot": "навык только читает — сравнивать нечего, проверка через stdoutContains"
@@ -0,0 +1,18 @@
{
"name": "конфликты объявлений: name+ref, дубль свойства, typeDef без xsi:type, база другого рода",
"caseFiles": [
"pkg-conflict/XDTOPackages/conflict.xml",
"pkg-conflict/XDTOPackages/conflict/Ext/Package.bin"
],
"params": { "packagePath": "pkg-conflict/XDTOPackages/conflict" },
"expectError": true,
"expect": {
"stdoutContains": [
"заданы одновременно name и ref",
"дублирующееся имя свойства",
"не задан xsi:type",
"ссылается на valueType"
]
},
"noSnapshot": "навык только читает — проверка через stdoutContains"
}
@@ -0,0 +1,13 @@
{
"name": "нарушен порядок верхнего уровня — платформа отвергнет пакет",
"caseFiles": [
"pkg-badorder/XDTOPackages/badorder.xml",
"pkg-badorder/XDTOPackages/badorder/Ext/Package.bin"
],
"params": { "packagePath": "pkg-badorder/XDTOPackages/badorder" },
"expectError": true,
"expect": {
"stdoutContains": ["Нарушен порядок элементов верхнего уровня"]
},
"noSnapshot": "навык только читает — проверка через stdoutContains"
}