diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index 9d5105837a9bb..8967e6b3e69b3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -310,6 +310,20 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M }) }; } + var direction = GetDirectionFromOptions(generatedComInterfaceAttributeData.Options); + + // Ensure the size of collections are known at marshal / unmarshal in time. + // A collection that is marshalled in cannot have a size that is an 'out' parameter. + foreach (TypePositionInfo parameter in signatureContext.ManagedParameters) + { + MarshallerHelpers.ValidateCountInfoAvailableAtCall( + direction, + parameter, + generatorDiagnostics, + symbol, + GeneratorDiagnostics.SizeOfInCollectionMustBeDefinedAtCallOutParam, + GeneratorDiagnostics.SizeOfInCollectionMustBeDefinedAtCallReturnValue); + } var containingSyntaxContext = new ContainingSyntaxContext(syntax); @@ -322,7 +336,7 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M var declaringType = ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol.ContainingType); - var virtualMethodIndexData = new VirtualMethodIndexData(index, ImplicitThisParameter: true, GetDirectionFromOptions(generatedComInterfaceAttributeData.Options), true, ExceptionMarshalling.Com); + var virtualMethodIndexData = new VirtualMethodIndexData(index, ImplicitThisParameter: true, direction, true, ExceptionMarshalling.Com); return new IncrementalMethodStubGenerationContext( signatureContext, diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs index 0f3ec3d047d00..621e4c9b17001 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs @@ -444,6 +444,26 @@ public class Ids WellKnownDiagnosticTags.Unnecessary }); + /// + public static readonly DiagnosticDescriptor SizeOfInCollectionMustBeDefinedAtCallOutParam = + new DiagnosticDescriptor( + Ids.InvalidGeneratedComInterfaceAttributeUsage, + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeTitle)), + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeMessageOutParam)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public static readonly DiagnosticDescriptor SizeOfInCollectionMustBeDefinedAtCallReturnValue = + new DiagnosticDescriptor( + Ids.InvalidGeneratedComInterfaceAttributeUsage, + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeTitle)), + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeMessageReturnValue)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + /// public static readonly DiagnosticDescriptor ComMethodManagedReturnWillBeOutVariable = new DiagnosticDescriptor( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx index 5dd131c0afa37..a096158343d16 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/Strings.resx @@ -700,6 +700,15 @@ COM Interop APIs on 'System.Runtime.InteropServices.Marshal' do not support source-generated COM + + The size of a collection that is marshalled to the callee must be defined when the method is called. + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf index 5dab1b24b59f6..e2c18dab20458 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.cs.xlf @@ -997,6 +997,21 @@ Abstraktní typ odvozený ze SafeHandle nelze zařadit pomocí odkazu. Poskytnutý typ musí být konkrétní. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Stavový zařazovač musí mít instanční metodu s názvem Free, která nemá žádné parametry a vrací void. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf index 49a475c5dcaaf..d085560facf7f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.de.xlf @@ -997,6 +997,21 @@ Ein abstrakter Typ, der von \"SafeHandle\" abgeleitet wird, kann nicht als Verweis gemarshallt werden. Der angegebene Typ muss ein konkretes Element sein. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Ein statusbehafteter Marshaller muss eine Instanzmethode mit dem Namen „Free“ mit null Parametern haben, die „nichtig“ zurückgibt. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf index 21b41f24a8385..0307795f45eb2 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.es.xlf @@ -997,6 +997,21 @@ Un tipo abstracto derivado de “SafeHandle” no se puede serializar por referencia. El tipo proporcionado debe ser concreto. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Un serializador con estado debe tener un método de instancia de devolución void de parámetro cero denominado 'Free'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf index f3aa7dac375ec..fdbcb443261ef 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.fr.xlf @@ -997,6 +997,21 @@ Un type abstrait dérivé de « SafeHandle » ne peut pas être marshalé par référence. Le type fourni doit être concret. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Un marshaleur avec état doit avoir une méthode d’instance de retour void de paramètre zéro nommée 'Free'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf index ac87db54081a4..a0aa417a60164 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.it.xlf @@ -997,6 +997,21 @@ Non è possibile effettuare il marshalling per riferimento di un tipo astratto derivato da 'SafeHandle'. Il tipo specificato deve essere concreto. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Un gestore di marshalling con stato deve avere un metodo di istanza con restituzione void con parametro zero denominato 'Free'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf index 867f5472f698a..85834a4023adc 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ja.xlf @@ -997,6 +997,21 @@ 'SafeHandle' から派生した抽象型は、参照でマーシャリングできません。指定される型は具象型である必要があります。 + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. ステートフル マーシャラーには、'Free' という名前で、パラメーターがなく、‘void’ を返すインスタンス メソッドが必要です。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf index c04b1253058eb..546cdfa094717 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ko.xlf @@ -997,6 +997,21 @@ 'SafeHandle'에서 파생된 추상 형식은 참조로 마샬링할 수 없습니다. 제공된 형식은 구체적이어야 합니다. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. 상태 저장 마샬러에는 'Free'라는 0 매개 변수 반환 인스턴스 메서드가 있어야 합니다. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf index 6a4a6f7ba9840..e1adb5d335b29 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pl.xlf @@ -997,6 +997,21 @@ Typ abstrakcyjny pochodzący od elementu „SafeHandle” nie może być skierowany przez odwołanie. Podany typ musi być konkretny. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Stanowy marshaller musi mieć metodę wystąpienia zwracającą wartość nieważną o parametrze 0 o nazwie „Free”. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf index 6ae9c3061d4b5..7bd7f73064878 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.pt-BR.xlf @@ -997,6 +997,21 @@ Um tipo abstrato derivado de 'SafeHandle' não pode ser marshalled por referência. O tipo fornecido deve ser concreto. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Um marshaller com estado deve ter um método de instância de retorno nulo de parâmetro zero chamado 'Gratuito'. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf index 2f16b177e2231..5bcb3d31ebae0 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.ru.xlf @@ -997,6 +997,21 @@ Абстрактный тип, производный от \"SafeHandle\", нельзя маршализировать по ссылке. Указанный тип должен быть конкретным. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Маршалер с отслеживанием состояния должен иметь метод экземпляра с нулевым параметром, возвращающий void, с именем "Free". diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf index 521f0b8cb7356..b9e3a106e9537 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.tr.xlf @@ -997,6 +997,21 @@ 'SafeHandle' özelliğinden türetilen soyut türler, başvuruya göre sıralanamaz. Sağlanan tür somut olmalıdır. + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. Durum bilgisi olan bir hazırlayıcının 'Free' adlı sıfır parametreli hükümsüz dönen bir örnek yöntemi olmalıdır. diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf index a303503da5f30..5c452bbf91561 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -997,6 +997,21 @@ 无法通过引用封送派生自 “SafeHandle” 的抽象类型。提供的类型必须是具体的。 + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. 监控状态的封送程序必须具有名为 “Free” 的零参数 void 返回实例方法。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf index adf2ea22b5353..8a8dd483eaab6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/System.Runtime.InteropServices/gen/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -997,6 +997,21 @@ 衍生自 'SafeHandle' 的抽象類型無法依參考排列。提供的類型必須是實體。 + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but count parameter '{1}' is an 'out' parameter. + + + + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + The size of parameter '{0}' that is marshalled to the callee must be defined when the method is called, but the return value is used as the size of the collection. + + + + The size of a collection that is marshalled to the callee must be defined when the method is called. + The size of a collection that is marshalled to the callee must be defined when the method is called. + + A stateful marshaller must have a zero-parameter void-returning instance method named 'Free'. 具狀態的封送處理常式必須有名稱為 'Free' 的零參數無效傳回執行個體方法。 diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/GeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/GeneratorDiagnostics.cs index 140775be274cc..5e59f2d5bf8e3 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/GeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/GeneratorDiagnostics.cs @@ -233,6 +233,26 @@ public class Ids WellKnownDiagnosticTags.Unnecessary }); + /// + public static readonly DiagnosticDescriptor SizeOfInCollectionMustBeDefinedAtCallOutParam = + new DiagnosticDescriptor( + Ids.InvalidLibraryImportAttributeUsage, + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeTitle)), + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeMessageOutParam)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public static readonly DiagnosticDescriptor SizeOfInCollectionMustBeDefinedAtCallReturnValue = + new DiagnosticDescriptor( + Ids.InvalidLibraryImportAttributeUsage, + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeTitle)), + GetResourceString(nameof(SR.SizeOfCollectionMustBeKnownAtMarshalTimeMessageReturnValue)), + Category, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + /// /// Report diagnostic for invalid configuration for string marshalling. /// diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index 77cd004720dea..ebb1f01bf8d3f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -404,5 +404,46 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo } throw new UnreachableException("An element is either a return value or passed by value or by ref."); } + + /// + /// Ensure that the count of a collection is available at call time if the parameter is not an out parameter. + /// It only looks at an indirection level of 0 (the size of the outer array), so there are some holes in + /// analysis if the parameter is a multidimensional array, but that case seems very unlikely to be hit. + /// + public static void ValidateCountInfoAvailableAtCall(MarshalDirection stubDirection, TypePositionInfo info, GeneratorDiagnosticsBag generatorDiagnostics, IMethodSymbol symbol, DiagnosticDescriptor outParamDescriptor, DiagnosticDescriptor returnValueDescriptor) + { + // In managed to unmanaged stubs, we can always just get the length of managed object + // We only really need to be concerned about unmanaged to managed stubs + if (stubDirection is MarshalDirection.ManagedToUnmanaged) + return; + + if (info.MarshallingAttributeInfo is NativeLinearCollectionMarshallingInfo collectionMarshallingInfo + && collectionMarshallingInfo.ElementCountInfo is CountElementCountInfo countInfo + && !(info.RefKind is RefKind.Out + || info.ManagedIndex is TypePositionInfo.ReturnIndex)) + { + if (countInfo.ElementInfo.IsByRef && countInfo.ElementInfo.RefKind is RefKind.Out) + { + Location location = TypePositionInfo.GetLocation(info, symbol); + generatorDiagnostics.ReportDiagnostic( + DiagnosticInfo.Create( + outParamDescriptor, + location, + info.InstanceIdentifier, + countInfo.ElementInfo.InstanceIdentifier)); + } + else if (countInfo.ElementInfo.ManagedIndex is TypePositionInfo.ReturnIndex) + { + Location location = TypePositionInfo.GetLocation(info, symbol); + generatorDiagnostics.ReportDiagnostic( + DiagnosticInfo.Create( + returnValueDescriptor, + location, + info.InstanceIdentifier)); + } + // If the parameter is multidimensional and a higher indirection level parameter is ByValue [Out], then we should warn. + } + } + } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs index bb40116731362..76697b0e6dd6a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs @@ -96,6 +96,17 @@ public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, return typeInfo; } + public static Location GetLocation(TypePositionInfo info, IMethodSymbol methodSymbol) + { + if (info.ManagedIndex is UnsetIndex) + return Location.None; + + if (info.ManagedIndex is ReturnIndex or ExceptionIndex) + return methodSymbol.Locations[0]; + + return methodSymbol.Parameters[info.ManagedIndex].Locations[0]; + } + private static (ByValueContentsMarshalKind, Location? inAttribute, Location? outAttribute) GetByValueContentsMarshalKind(IEnumerable attributes, Compilation compilation) { INamedTypeSymbol outAttributeType = compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_OutAttribute)!; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs index 04ba3b30e83d0..b013d8c26c459 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs @@ -89,7 +89,7 @@ internal struct IntStruct [CustomMarshaller(typeof(IntStruct), MarshalMode.Default, typeof(IntStructMarshaller))] internal static class IntStructMarshaller { - public static nint ConvertToUnmanaged(int managed) => (nint)0; + public static nint ConvertToUnmanaged(IntStruct managed) => (nint)0; public static IntStruct ConvertToManaged(nint unmanaged) => default; } """; @@ -268,6 +268,53 @@ partial interface INativeAPI {{_attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}} """; + public string CollectionMarshallingWithCountRefKinds( + (string parameterType, string parameterModifiers, string[] countNames) returnType, + params (string parameterType, string parameterModifiers, string parameterName, string[] countNames)[] parameters) + { + List parameterSources = new(); + int i = 1; + foreach (var (parameterType, parameterModifiers, parameterName, countNames) in parameters) + { + List marshalUsings = new(); + int j = 0; + foreach (var countName in countNames) + { + marshalUsings.Add($"[MarshalUsing(CountElementName = {countName}, ElementIndirectionDepth = {j})]"); + j++; + } + parameterSources.Add($$""" + {{string.Join(' ', marshalUsings)}} {{parameterModifiers}} {{parameterType}} {|#{{i}}:{{parameterName}}|} + """); + i++; + } + string returnTypeSource; + { + List marshalUsings = new(); + var (parameterType, parameterModifiers, countNames) = returnType; + foreach (var countName in countNames) + { + marshalUsings.Add($"[return: MarshalUsing(CountElementName = nameof({countName}))]"); + } + returnTypeSource = $"{string.Join(' ', marshalUsings)} {parameterModifiers} {parameterType}"; + } + var parametersSource = string.Join(',', parameterSources); + return $$""" + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + [assembly:DisableRuntimeMarshalling] + + {{UnmanagedObjectUnwrapper(typeof(UnmanagedObjectUnwrapper.TestUnwrapper))}} + {{GeneratedComInterface()}} + partial interface INativeAPI + { + {{VirtualMethodIndex(0)}} + {{returnTypeSource}} {|#0:Method|}({{parametersSource}}); + } + """; + } + public string BasicReturnTypeComExceptionHandling(string typeName, string preDeclaration = "") => $$""" using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -423,17 +470,17 @@ partial interface INativeAPI public static event EventHandler StaticEvent; } - + {{_attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}} interface IOtherInterface { int Property { get; set; } - + public static int StaticProperty { get; set; } - + event EventHandler Event; - + public static event EventHandler StaticEvent; } """; diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs index a0f117690734d..ef9d76ff4c920 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs @@ -975,6 +975,102 @@ partial interface {|#0:J|} await test.RunAsync(); } + public static IEnumerable CountParameterIsOutSnippets() + { + var g = GetAttributeProvider(GeneratorKind.ComInterfaceGenerator); + CodeSnippets a = new(g); + DiagnosticResult returnValueDiag = new DiagnosticResult(GeneratorDiagnostics.SizeOfInCollectionMustBeDefinedAtCallReturnValue) + .WithLocation(1) + .WithArguments("arr"); + DiagnosticResult outParamDiag = new DiagnosticResult(GeneratorDiagnostics.SizeOfInCollectionMustBeDefinedAtCallOutParam) + .WithLocation(1) + .WithArguments("arr", "size"); + + var voidReturn = ("void", "", Array.Empty()); + + var size = ("int", "", "size", Array.Empty()); + var outSize = ("int", "out", "size", Array.Empty()); + var inSize = ("int", "in", "size", Array.Empty()); + var refSize = ("int", "ref", "size", Array.Empty()); + + var arr = ("IntStruct[]", "", "arr", new[] { "nameof(size)" }); + var outArr = ("IntStruct[]", "out", "arr", new[] { "nameof(size)" }); + var inArr = ("IntStruct[]", "in", "arr", new[] { "nameof(size)" }); + var refArr = ("IntStruct[]", "ref", "arr", new[] { "nameof(size)" }); + var contentsOutArr = ("IntStruct[]", "[OutAttribute]", "arr", new[] { "nameof(size)" }); + var contentsInOutArr = ("IntStruct[]", "[InAttribute, OutAttribute]", "arr", new[] { "nameof(size)" }); + + yield return new object[] { ID(), Source(voidReturn, arr, size) }; + yield return new object[] { ID(), Source(voidReturn, arr, inSize) }; + yield return new object[] { ID(), Source(voidReturn, arr, outSize), outParamDiag }; + yield return new object[] { ID(), Source(voidReturn, arr, refSize) }; + + yield return new object[] { ID(), Source(voidReturn, inArr, size) }; + yield return new object[] { ID(), Source(voidReturn, inArr, inSize) }; + yield return new object[] { ID(), Source(voidReturn, inArr, outSize), outParamDiag }; + yield return new object[] { ID(), Source(voidReturn, inArr, refSize) }; + + yield return new object[] { ID(), Source(voidReturn, outArr, size) }; + yield return new object[] { ID(), Source(voidReturn, outArr, inSize) }; + yield return new object[] { ID(), Source(voidReturn, outArr, outSize) }; + yield return new object[] { ID(), Source(voidReturn, outArr, refSize) }; + + yield return new object[] { ID(), Source(voidReturn, refArr, size) }; + yield return new object[] { ID(), Source(voidReturn, refArr, inSize) }; + yield return new object[] { ID(), Source(voidReturn, refArr, outSize), outParamDiag }; + yield return new object[] { ID(), Source(voidReturn, refArr, refSize) }; + + yield return new object[] { ID(), Source(voidReturn, contentsOutArr, size) }; + yield return new object[] { ID(), Source(voidReturn, contentsOutArr, inSize) }; + yield return new object[] { ID(), Source(voidReturn, contentsOutArr, outSize), outParamDiag }; + yield return new object[] { ID(), Source(voidReturn, contentsOutArr, refSize) }; + + yield return new object[] { ID(), Source(voidReturn, contentsInOutArr, size) }; + yield return new object[] { ID(), Source(voidReturn, contentsInOutArr, inSize) }; + yield return new object[] { ID(), Source(voidReturn, contentsInOutArr, outSize), outParamDiag }; + yield return new object[] { ID(), Source(voidReturn, contentsInOutArr, refSize) }; + + var sizeReturn = ("int", "", Array.Empty()); + + var arrReturnSize = ("IntStruct[]", "", "arr", new[] { "MarshalUsingAttribute.ReturnsCountValue" }); + var outArrReturnSize = ("IntStruct[]", "out", "arr", new[] { "MarshalUsingAttribute.ReturnsCountValue" }); + var inArrReturnSize = ("IntStruct[]", "in", "arr", new[] { "MarshalUsingAttribute.ReturnsCountValue" }); + var refArrReturnSize = ("IntStruct[]", "ref", "arr", new[] { "MarshalUsingAttribute.ReturnsCountValue" }); + var contentsOutArrReturnSize = ("IntStruct[]", "[OutAttribute]", "arr", new[] { "MarshalUsingAttribute.ReturnsCountValue" }); + var contentsInOutArrReturnSize = ("IntStruct[]", "[InAttribute, OutAttribute]", "arr", new[] { "MarshalUsingAttribute.ReturnsCountValue" }); + + yield return new object[] { ID(), Source(sizeReturn, arrReturnSize), returnValueDiag }; + yield return new object[] { ID(), Source(sizeReturn, outArrReturnSize) }; + yield return new object[] { ID(), Source(sizeReturn, inArrReturnSize), returnValueDiag }; + yield return new object[] { ID(), Source(sizeReturn, refArrReturnSize), returnValueDiag }; + yield return new object[] { ID(), Source(sizeReturn, contentsOutArrReturnSize), returnValueDiag }; + yield return new object[] { ID(), Source(sizeReturn, contentsInOutArrReturnSize), returnValueDiag }; + + var returnArr = ("IntStruct[]", "", new[] { "size" }); + + yield return new object[] { ID(), Source(returnArr, size) }; + yield return new object[] { ID(), Source(returnArr, inSize) }; + yield return new object[] { ID(), Source(returnArr, outSize) }; + yield return new object[] { ID(), Source(returnArr, refSize) }; + + string Source( + (string type, string modifiers, string[] counts) returnValue, + params (string type, string modifiers, string name, string[] counts)[] parameters) + { + return a.CollectionMarshallingWithCountRefKinds(returnValue, parameters) + + "[NativeMarshalling(typeof(IntStructMarshaller))]" + + CodeSnippets.IntStructAndMarshaller; + } + } + + [Theory] + [MemberData(nameof(CountParameterIsOutSnippets))] + public async Task ValidateSizeParameterRefKindDiagnostics(string ID, string source, params DiagnosticResult[] diagnostics) + { + _ = ID; + await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, diagnostics); + } + public static IEnumerable IntAndEnumReturnTypeSnippets() { var diagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ComMethodManagedReturnWillBeOutVariable).WithLocation(0); diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 8eabb88e6db2f..e1039f70a90ef 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Interop.UnitTests; @@ -985,6 +986,70 @@ out int pOutSize } """; + public const string IntStructAndMarshaller = IntStructDefinition + IntStructMarshallerDefinition; + public const string IntStructDefinition = """ + internal struct IntStruct + { + public int Field; + } + """; + public const string IntStructMarshallerDefinition = """ + [CustomMarshaller(typeof(IntStruct), MarshalMode.Default, typeof(IntStructMarshaller))] + internal static class IntStructMarshaller + { + public static nint ConvertToUnmanaged(IntStruct managed) => (nint)0; + public static IntStruct ConvertToManaged(nint unmanaged) => default; + } + """; + + public string CollectionMarshallingWithCountRefKinds( + (string parameterType, string parameterModifiers, string[] countNames) returnType, + params (string parameterType, string parameterModifiers, string parameterName, string[] countNames)[] parameters) + { + List parameterSources = new(); + int i = 1; + foreach (var (parameterType, parameterModifiers, parameterName, countNames) in parameters) + { + List marshalUsings = new(); + int j = 0; + foreach (var countName in countNames) + { + marshalUsings.Add($"[MarshalUsing(CountElementName = {countName}, ElementIndirectionDepth = {j})]"); + j++; + } + parameterSources.Add($$""" + {{string.Join(' ', marshalUsings)}} {{parameterModifiers}} {{parameterType}} {|#{{i}}:{{parameterName}}|} + """); + i++; + } + string returnTypeSource; + string returnAttributes; + { + List marshalUsings = new(); + var (parameterType, parameterModifiers, countNames) = returnType; + foreach (var countName in countNames) + { + marshalUsings.Add($"[return: MarshalUsing(CountElementName = nameof({countName}))]"); + } + returnAttributes = string.Join(' ', marshalUsings); + returnTypeSource = $"{parameterModifiers} {parameterType}"; + } + var parametersSource = string.Join(',', parameterSources); + return $$""" + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + using System.Runtime.InteropServices.Marshalling; + {{DisableRuntimeMarshalling}} + + partial class Test + { + [LibraryImport("DoesNotExist")] + {{returnAttributes}} + public static partial {{returnTypeSource}} {|#0:Method|}({{parametersSource}}); + } + """; + } + public static string MarshalUsingArrayParameterWithSizeParam(string sizeParamType, bool isByRef) => $$""" using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling;