public void AnalyzeAttribute(SyntaxNodeAnalysisContext context) { AttributeSyntax syntax = (AttributeSyntax)context.Node; ISymbol attributedSymbol = context.ContainingSymbol !; AttributeData?attr = syntax.FindAttributeData(attributedSymbol); if (attr?.AttributeClass?.ToDisplayString() == TypeNames.CustomMarshallerAttribute && attr.AttributeConstructor is not null) { DiagnosticReporter managedTypeReporter = DiagnosticReporter.CreateForLocation(syntax.FindArgumentWithNameOrArity("managedType", 0).FindTypeExpressionOrNullLocation(), context.ReportDiagnostic); INamedTypeSymbol entryType = (INamedTypeSymbol)attributedSymbol; INamedTypeSymbol?managedTypeInAttribute = (INamedTypeSymbol?)attr.ConstructorArguments[0].Value; if (managedTypeInAttribute is null) { managedTypeReporter.CreateAndReportDiagnostic(ManagedTypeMustBeNonNullRule, entryType.ToDisplayString()); } if (!ManualTypeMarshallingHelper.TryResolveManagedType( entryType, managedTypeInAttribute, ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryType), (entryType, managedType) => managedTypeReporter.CreateAndReportDiagnostic(ManagedTypeMustBeClosedOrMatchArityRule, managedType, entryType), out ITypeSymbol managedType)) { return; } DiagnosticReporter marshallerTypeReporter = DiagnosticReporter.CreateForLocation(syntax.FindArgumentWithNameOrArity("marshallerType", 2).FindTypeExpressionOrNullLocation(), context.ReportDiagnostic); ITypeSymbol? marshallerTypeInAttribute = (ITypeSymbol?)attr.ConstructorArguments[2].Value; if (marshallerTypeInAttribute is null) { marshallerTypeReporter.CreateAndReportDiagnostic(MarshallerTypeMustBeNonNullRule); } if (!ManualTypeMarshallingHelper.TryResolveMarshallerType( entryType, marshallerTypeInAttribute, (entryType, marshallerType) => marshallerTypeReporter.CreateAndReportDiagnostic(MarshallerTypeMustBeClosedOrMatchArityRule, marshallerType, entryType), out ITypeSymbol marshallerType)) { return; } AnalyzeMarshallerType( marshallerTypeReporter, (INamedTypeSymbol)managedType, (MarshalMode)attr.ConstructorArguments[1].Value, (INamedTypeSymbol?)marshallerType, ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryType)); } }
private static (INamedTypeSymbol EntryPointType, INamedTypeSymbol ManagedType)? FindContainingEntryPointTypeAndManagedType(INamedTypeSymbol marshallerType) { for (INamedTypeSymbol containingType = marshallerType; containingType is not null; containingType = containingType.ContainingType) { AttributeData?attrData = containingType.GetAttributes().FirstOrDefault( attr => attr.AttributeClass?.ToDisplayString() == TypeNames.CustomMarshallerAttribute && attr.AttributeConstructor is not null && !attr.ConstructorArguments[0].IsNull && attr.ConstructorArguments[2].Value is INamedTypeSymbol marshallerTypeInAttribute && ManualTypeMarshallingHelper.TryResolveMarshallerType(containingType, marshallerTypeInAttribute, (_, _) => { }, out ITypeSymbol constructedMarshallerType) && SymbolEqualityComparer.Default.Equals(constructedMarshallerType, marshallerType)); if (attrData is not null) { return(containingType, (INamedTypeSymbol)attrData.ConstructorArguments[0].Value); } } return(null); }
private static void SuppressMarkMethodsAsStaticDiagnosticIfNeeded(SuppressionAnalysisContext context, Diagnostic diagnostic) { SemanticModel model = context.GetSemanticModel(diagnostic.Location.SourceTree); ISymbol diagnosedSymbol = model.GetDeclaredSymbol(diagnostic.Location.SourceTree.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan), context.CancellationToken); if (diagnosedSymbol.Kind != SymbolKind.Method) { return; } if (FindContainingEntryPointTypeAndManagedType(diagnosedSymbol.ContainingType) is (INamedTypeSymbol entryPointMarshallerType, INamedTypeSymbol managedType)) { bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointMarshallerType); (MarshallerShape _, StatefulMarshallerShapeHelper.MarshallerMethods methods) = StatefulMarshallerShapeHelper.GetShapeForType(diagnosedSymbol.ContainingType, managedType, isLinearCollectionMarshaller, context.Compilation); if (methods.IsShapeMethod((IMethodSymbol)diagnosedSymbol)) { // If we are a method of the shape on the stateful marshaller shape, then we need to be our current shape. // So, suppress the diagnostic to make this method static, as that would break the shape. context.ReportSuppression(Suppression.Create(MarkMethodsAsStaticSuppression, diagnostic)); } } }
private static async Task <Solution> AddMissingMembers(Document doc, SyntaxNode node, HashSet <string> missingMemberNames, CancellationToken ct) { var model = await doc.GetSemanticModelAsync(ct).ConfigureAwait(false); var entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(node.SpanStart, ct); // TODO: Convert to use the IOperation tree once IAttributeOperation is available var managedTypeSymbolInAttribute = GetManagedTypeInAttributeSyntax(node.GetLocation(), entryPointTypeSymbol); bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol); // Explicitly ignore the generic arity mismatch diagnostics as we will only reach here if there are no mismatches. // The analyzer will not for missing members if the managed type cannot be resolved. ManualTypeMarshallingHelper.TryResolveManagedType(entryPointTypeSymbol, ManualTypeMarshallingHelper.ReplaceGenericPlaceholderInType(managedTypeSymbolInAttribute, entryPointTypeSymbol, model.Compilation), isLinearCollectionMarshaller, IgnoreArityMismatch, out ITypeSymbol managedType); SymbolEditor editor = SymbolEditor.Create(doc.Project.Solution); INamedTypeSymbol marshallerType = (INamedTypeSymbol)model.GetSymbolInfo(node, ct).Symbol; await editor.EditOneDeclarationAsync(marshallerType, (editor, decl) => AddMissingMembers(editor, decl, marshallerType, managedType, missingMemberNames, isLinearCollectionMarshaller), ct).ConfigureAwait(false); return(editor.ChangedSolution); }
private void AnalyzeManagedTypeMarshallingInfo( ITypeSymbol managedType, DiagnosticReporter diagnosticFactory, INamedTypeSymbol?entryType) { if (entryType is null) { diagnosticFactory.CreateAndReportDiagnostic( MarshallerEntryPointTypeMustBeNonNullRule, managedType.ToDisplayString()); return; } if (!ManualTypeMarshallingHelper.HasEntryPointMarshallerAttribute(entryType)) { diagnosticFactory.CreateAndReportDiagnostic( MarshallerEntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeRule, entryType.ToDisplayString(), managedType.ToDisplayString()); return; } bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryType); if (entryType.IsUnboundGenericType) { if (managedType is not INamedTypeSymbol namedManagedType) { diagnosticFactory.CreateAndReportDiagnostic( GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule, entryType.ToDisplayString(), managedType.ToDisplayString()); return; } if (!ManualTypeMarshallingHelper.TryResolveEntryPointType( namedManagedType, entryType, isLinearCollectionMarshaller, (managedType, entryType) => diagnosticFactory.CreateAndReportDiagnostic( GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule, entryType.ToDisplayString(), managedType.ToDisplayString()), out ITypeSymbol resolvedEntryType)) { return; } entryType = (INamedTypeSymbol)resolvedEntryType; } if (!ManualTypeMarshallingHelper.TryGetMarshallersFromEntryTypeIgnoringElements( entryType, managedType, _compilation, (entryType, managedType) => diagnosticFactory.CreateAndReportDiagnostic( GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule, entryType.ToDisplayString(), managedType.ToDisplayString()), out _)) { diagnosticFactory.CreateAndReportDiagnostic( MarshallerEntryPointTypeMustHaveCustomMarshallerAttributeWithMatchingManagedTypeRule, entryType.ToDisplayString(), managedType.ToDisplayString()); } }
private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymbol type, AttributeData nativeMarshalerAttributeData, bool isNativeMarshallingAttribute) { if (nativeMarshalerAttributeData.ConstructorArguments.Length == 0) { // This is a MarshalUsing with just count information. return; } if (nativeMarshalerAttributeData.ConstructorArguments[0].IsNull) { context.ReportDiagnostic( nativeMarshalerAttributeData.CreateDiagnostic( NativeTypeMustBeNonNullRule, type.ToDisplayString())); return; } ITypeSymbol nativeType = (ITypeSymbol)nativeMarshalerAttributeData.ConstructorArguments[0].Value !; ISymbol nativeTypeDiagnosticsTargetSymbol = nativeType; if (nativeType is not INamedTypeSymbol marshalerType) { context.ReportDiagnostic( GetDiagnosticLocations(context, nativeType, nativeMarshalerAttributeData).CreateDiagnostic( NativeTypeMustHaveRequiredShapeRule, nativeType.ToDisplayString(), type.ToDisplayString())); return; } DiagnosticDescriptor requiredShapeRule = NativeTypeMustHaveRequiredShapeRule; ManualTypeMarshallingHelper.NativeTypeMarshallingVariant variant = ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.Standard; if (marshalerType.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(_genericContiguousCollectionMarshallerAttribute, a.AttributeClass))) { variant = ManualTypeMarshallingHelper.NativeTypeMarshallingVariant.ContiguousCollection; requiredShapeRule = CollectionNativeTypeMustHaveRequiredShapeRule; if (!ManualTypeMarshallingHelper.TryGetManagedValuesProperty(marshalerType, out _) || !ManualTypeMarshallingHelper.HasNativeValueStorageProperty(marshalerType, _spanOfByte)) { context.ReportDiagnostic( GetDiagnosticLocations(context, marshalerType, nativeMarshalerAttributeData).CreateDiagnostic( requiredShapeRule, nativeType.ToDisplayString(), type.ToDisplayString())); return; } } if (!nativeType.IsValueType) { context.ReportDiagnostic( GetDiagnosticLocations(context, nativeType, nativeMarshalerAttributeData).CreateDiagnostic( requiredShapeRule, nativeType.ToDisplayString(), type.ToDisplayString())); return; } if (marshalerType.IsUnboundGenericType) { if (!isNativeMarshallingAttribute) { context.ReportDiagnostic( nativeMarshalerAttributeData.CreateDiagnostic( NativeGenericTypeMustBeClosedOrMatchArityRule, nativeType.ToDisplayString(), type.ToDisplayString())); return; } if (type is not INamedTypeSymbol namedType || marshalerType.TypeArguments.Length != namedType.TypeArguments.Length) { context.ReportDiagnostic( nativeMarshalerAttributeData.CreateDiagnostic( NativeGenericTypeMustBeClosedOrMatchArityRule, nativeType.ToDisplayString(), type.ToDisplayString())); return; } // Construct the marshaler type around the same type arguments as the managed type. nativeType = marshalerType = marshalerType.ConstructedFrom.Construct(namedType.TypeArguments, namedType.TypeArgumentNullableAnnotations); } bool hasConstructor = false; bool hasCallerAllocSpanConstructor = false; foreach (IMethodSymbol ctor in marshalerType.Constructors) { if (ctor.IsStatic) { continue; } hasConstructor = hasConstructor || ManualTypeMarshallingHelper.IsManagedToNativeConstructor(ctor, type, variant); if (!hasCallerAllocSpanConstructor && ManualTypeMarshallingHelper.IsCallerAllocatedSpanConstructor(ctor, type, _spanOfByte, variant)) { hasCallerAllocSpanConstructor = true; IFieldSymbol bufferSizeField = nativeType.GetMembers(ManualTypeMarshallingHelper.BufferSizeFieldName).OfType <IFieldSymbol>().FirstOrDefault(); if (bufferSizeField is null or { DeclaredAccessibility: not Accessibility.Public } or { IsConst: false } or { Type: not { SpecialType: SpecialType.System_Int32 } })
private static SyntaxNode AddMissingMembers(SyntaxNode node, ITypeSymbol marshallerType, List <string> missingMemberNames, Compilation compilation, SyntaxGenerator gen) { INamedTypeSymbol @byte = compilation.GetSpecialType(SpecialType.System_Byte); INamedTypeSymbol @object = compilation.GetSpecialType(SpecialType.System_Object); INamedTypeSymbol spanOfT = compilation.GetTypeByMetadataName(TypeNames.System_Span_Metadata) !; INamedTypeSymbol spanOfByte = spanOfT.Construct(@byte) !; INamedTypeSymbol readOnlySpanOfT = compilation.GetTypeByMetadataName(TypeNames.System_ReadOnlySpan_Metadata) !; INamedTypeSymbol readOnlySpanOfByte = readOnlySpanOfT.Construct(@byte) !; INamedTypeSymbol int32 = compilation.GetSpecialType(SpecialType.System_Int32); SyntaxNode updatedDeclaration = node; (_, ITypeSymbol managedType, _) = ManualTypeMarshallingHelper.GetMarshallerShapeInfo(marshallerType); IMethodSymbol?fromNativeValueMethod = ManualTypeMarshallingHelper.FindFromNativeValueMethod(marshallerType); IMethodSymbol?toNativeValueMethod = ManualTypeMarshallingHelper.FindToNativeValueMethod(marshallerType); IMethodSymbol?getManagedValuesSourceMethod = ManualTypeMarshallingHelper.FindGetManagedValuesSourceMethod(marshallerType, readOnlySpanOfT); IMethodSymbol?getManagedValuesDestinationMethod = ManualTypeMarshallingHelper.FindGetManagedValuesDestinationMethod(marshallerType, spanOfT); SyntaxNode[] throwNotImplementedStatements = new[] { gen.ThrowStatement(gen.ObjectCreationExpression(gen.DottedName("System.NotImplementedException"))) }; foreach (string missingMemberName in missingMemberNames) { switch (missingMemberName) { case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueManagedToNativeConstructor: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( gen.GetName(node), new[] { gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)) }, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case CustomTypeMarshallerAnalyzer.MissingMemberNames.ValueCallerAllocatedBufferConstructor: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( gen.GetName(node), new[] { gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)) }, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionManagedToNativeConstructor: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( gen.GetName(node), new[] { gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) }, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionCallerAllocatedBufferConstructor: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( gen.GetName(node), new[] { gen.ParameterDeclaration("managed", type: gen.TypeExpression(managedType)), gen.ParameterDeclaration("buffer", type: gen.TypeExpression(spanOfByte)), gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) }, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case CustomTypeMarshallerAnalyzer.MissingMemberNames.CollectionNativeElementSizeConstructor: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.ConstructorDeclaration( gen.GetName(node), new[] { gen.ParameterDeclaration("nativeElementSize", type: gen.TypeExpression(int32)) }, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.Value.ToManaged: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.Value.ToManaged, returnType: gen.TypeExpression(managedType), accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.Value.FreeNative: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration(ShapeMemberNames.Value.FreeNative, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.Value.FromNativeValue: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.Value.FromNativeValue, parameters: new[] { gen.ParameterDeclaration("value", type: gen.TypeExpression(toNativeValueMethod?.ReturnType ?? @byte)) }, accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.Value.ToNativeValue: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.Value.ToNativeValue, returnType: gen.TypeExpression(fromNativeValueMethod?.Parameters[0].Type ?? @byte), accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.LinearCollection.GetManagedValuesSource: INamedTypeSymbol?getManagedValuesDestinationReturnType = (INamedTypeSymbol?)getManagedValuesDestinationMethod?.ReturnType; updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.LinearCollection.GetManagedValuesSource, returnType: gen.TypeExpression( readOnlySpanOfT.Construct( getManagedValuesDestinationReturnType?.TypeArguments[0] ?? @object)), accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.LinearCollection.GetNativeValuesDestination: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.LinearCollection.GetNativeValuesDestination, returnType: gen.TypeExpression(spanOfByte), accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.LinearCollection.GetNativeValuesSource: updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.LinearCollection.GetNativeValuesSource, parameters: new[] { gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32)) }, returnType: gen.TypeExpression(readOnlySpanOfByte), accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; case ShapeMemberNames.LinearCollection.GetManagedValuesDestination: INamedTypeSymbol?getManagedValuesSourceReturnType = (INamedTypeSymbol?)getManagedValuesSourceMethod?.ReturnType; updatedDeclaration = gen.AddMembers(updatedDeclaration, gen.MethodDeclaration( ShapeMemberNames.LinearCollection.GetNativeValuesDestination, parameters: new[] { gen.ParameterDeclaration("numElements", type: gen.TypeExpression(int32)) }, returnType: gen.TypeExpression( spanOfT.Construct( getManagedValuesSourceReturnType?.TypeArguments[0] ?? @object)), accessibility: Accessibility.Public, statements: throwNotImplementedStatements)); break; default: break; } } return(updatedDeclaration); }