diff --git a/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/FieldTypeAttributesOnDacPropertyTests.cs b/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/FieldTypeAttributesOnDacPropertyTests.cs index 38e56156a..4628ae630 100644 --- a/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/FieldTypeAttributesOnDacPropertyTests.cs +++ b/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/FieldTypeAttributesOnDacPropertyTests.cs @@ -26,36 +26,36 @@ protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() => [Theory] [EmbeddedFileData("DacWithMultipleFieldTypeAttributes.cs")] - public virtual Task PropertyWithMultipleFieldTypeAttributes(string source) => + public virtual Task Property_WithMultiple_FieldTypeAttributes(string source) => VerifyCSharpDiagnosticAsync(source, Descriptors.PX1023_MultipleTypeAttributesOnProperty.CreateFor(line: 24, column: 4), Descriptors.PX1023_MultipleTypeAttributesOnProperty.CreateFor(line: 25, column: 4)); [Theory] [EmbeddedFileData("DacWithMultipleCalcedOnDbSideAttributes.cs")] - public virtual Task PropertyWithMultipleCalcedOnDbSideAttributes(string source) => + public virtual Task Property_WithMultiple_CalcedOnDbSideAttributes(string source) => VerifyCSharpDiagnosticAsync(source, Descriptors.PX1023_MultipleCalcedOnDbSideAttributesOnProperty.CreateFor(line: 16, column: 4), Descriptors.PX1023_MultipleCalcedOnDbSideAttributesOnProperty.CreateFor(line: 17, column: 4)); [Theory] [EmbeddedFileData("DacWithMultipleFieldTypeAttributes_Expected.cs")] - public virtual Task MultipleFieldTypeAttributes_ShouldNotShowDiagnostic(string source) => + public virtual Task Multiple_FieldTypeAttributes_ShouldNotShowDiagnostic(string source) => VerifyCSharpDiagnosticAsync(source); [Theory] [EmbeddedFileData("DacWithMultipleCalcedOnDbSideAttributes_Expected.cs")] - public virtual Task MultipleCalcedOnDbSideAttributes_ShouldNotShowDiagnostic(string source) => + public virtual Task Multiple_CalcedOnDbSideAttributes_ShouldNotShowDiagnostic(string source) => VerifyCSharpDiagnosticAsync(source); [Theory] [EmbeddedFileData("DacFieldAttributesTypeMismatch_Expected.cs")] - public virtual Task DacPropertyTypeNotMatchingAttributeType_ShouldNotShowDiagnostic(string source) => + public virtual Task DacPropertyType_NotMatching_AttributeType_ShouldNotShowDiagnostic(string source) => VerifyCSharpDiagnosticAsync(source); [Theory] [EmbeddedFileData("DacFieldAttributesTypeMismatch.cs")] - public virtual Task DacPropertyTypeNotMatchingAttributeType(string source) => + public virtual Task DacPropertyType_NotMatching_AttributeType(string source) => VerifyCSharpDiagnosticAsync(source, Descriptors.PX1021_PXDBFieldAttributeNotMatchingDacProperty.CreateFor((Line: 24, Column: 4), extraLocation: (Line: 26, Column: 10)), @@ -74,24 +74,29 @@ public virtual Task DacPropertyTypeNotMatchingAttributeType(string source) => [Theory] [EmbeddedFileData("DacWithInvalidAggregatorAttributes.cs")] - public virtual Task DacPropertyWithInvalidAggregatorAttributes(string source) => + public virtual Task DacProperty_WithInvalid_AggregatorAttributes(string source) => VerifyCSharpDiagnosticAsync(source, Descriptors.PX1023_MultipleCalcedOnDbSideAttributesOnAggregators.CreateFor(line: 41, column: 4), Descriptors.PX1023_MultipleTypeAttributesOnAggregators.CreateFor(line: 55, column: 4)); [Theory] [EmbeddedFileData("DacFieldTypeMismatchPXDBScalarAttr.cs")] - public virtual Task DacPropertyWithPXDBScalarAttribute(string source) => + public virtual Task DacProperty_WithPXDBScalarAttribute(string source) => VerifyCSharpDiagnosticAsync(source); [Theory] [EmbeddedFileData("DacWithValidAggregatorAttributes.cs")] - public virtual Task DacWithValidAggregatorAttributes(string source) => + public virtual Task Dac_WithValid_AggregatorAttributes(string source) => + VerifyCSharpDiagnosticAsync(source); + + [Theory] + [EmbeddedFileData("DacWithPXDBPackedIntegerArrayAttribute.cs")] + public virtual Task Dac_WithPXDBPackedIntegerArrayAttribute(string source) => VerifyCSharpDiagnosticAsync(source); [Theory] [EmbeddedFileData("DacFieldWithINUnitAttribute.cs")] - public virtual Task DacFieldWithINUnitAttribute(string source) => + public virtual Task DacField_WithINUnitAttribute(string source) => VerifyCSharpDiagnosticAsync(source); } } diff --git a/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/Sources/DacWithPXDBPackedIntegerArrayAttribute.cs b/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/Sources/DacWithPXDBPackedIntegerArrayAttribute.cs new file mode 100644 index 000000000..18c991e4c --- /dev/null +++ b/src/Acuminator/Acuminator.Tests/Tests/StaticAnalysis/DacPropertyAttributes/Sources/DacWithPXDBPackedIntegerArrayAttribute.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using PX.Data; + +namespace PX.Objects.HackathonDemo +{ + // Acuminator disable once PX1069 MissingMandatoryDacFields No need in unit test + [PXHidden] + public class SomeDac : PXBqlTable, IBqlTable + { + #region DacID + public abstract class dacID : PX.Data.BQL.BqlInt.Field { } + + [PXDBInt] + public virtual int? DacID + { + get; + set; + } + #endregion + + #region DiscountsAppliedToLine + public abstract class discountsAppliedToLine : IBqlField { } + + [PXDBPackedIntegerArray] + public virtual ushort[]? DiscountsAppliedToLine { get; set; } + #endregion + } +} \ No newline at end of file diff --git a/src/Acuminator/Acuminator.Utilities/Roslyn/PXFieldAttributes/FieldTypeAttributesMetadataProvider.cs b/src/Acuminator/Acuminator.Utilities/Roslyn/PXFieldAttributes/FieldTypeAttributesMetadataProvider.cs index 040cf8ed2..a9394f0f4 100644 --- a/src/Acuminator/Acuminator.Utilities/Roslyn/PXFieldAttributes/FieldTypeAttributesMetadataProvider.cs +++ b/src/Acuminator/Acuminator.Utilities/Roslyn/PXFieldAttributes/FieldTypeAttributesMetadataProvider.cs @@ -5,7 +5,6 @@ using Acuminator.Utilities.Common; using Acuminator.Utilities.Roslyn.Semantic; -using Acuminator.Utilities.Roslyn.Semantic.Attribute; using Microsoft.CodeAnalysis; @@ -30,6 +29,8 @@ public class FieldTypeAttributesMetadataProvider public ImmutableArray WellKnownNonDataTypeAttributes { get; } + public ImmutableDictionary MetadataForWellKnownSpecialDataTypeAttributes { get; } + private readonly INamedTypeSymbol _pxDBCalcedAttribute; private readonly INamedTypeSymbol _pxDBScalarAttribute; private readonly INamedTypeSymbol _pxDBFieldAttribute; @@ -59,6 +60,7 @@ public FieldTypeAttributesMetadataProvider(PXContext pxContext) .ToImmutableArray(); WellKnownNonDataTypeAttributes = GetWellKnownNonDataTypeAttributes(_pxContext); + MetadataForWellKnownSpecialDataTypeAttributes = GetMetadataForWellKnownSpecialDataTypeAttributes(_pxContext); } public bool IsWellKnownNonDataTypeAttribute(ITypeSymbol attribute) @@ -90,6 +92,11 @@ internal IReadOnlyCollection GetDacFieldTypeAttributeInfo if (flattenedAttributes.Count == 0) return []; + var metadataForWellKnownSpecialDataTypeAttribute = TryGetMetadataForWellKnownSpecialDataTypeAttribute(originalAttribute); + + if (metadataForWellKnownSpecialDataTypeAttribute != null) + return [metadataForWellKnownSpecialDataTypeAttribute]; + var mixedDbBoundnessAttributeInfos = GetMixedDbBoundnessAttributeInfosInFlattenedSet(originalAttribute, flattenedAttributes); bool hasMixedBoundnessAttributes = mixedDbBoundnessAttributeInfos?.Count > 0; var typeAttributeInfos = hasMixedBoundnessAttributes @@ -127,6 +134,27 @@ internal IReadOnlyCollection GetDacFieldTypeAttributeInfo return typeAttributeInfos as IReadOnlyCollection ?? []; } + private DataTypeAttributeInfo? TryGetMetadataForWellKnownSpecialDataTypeAttribute(ITypeSymbol attribute) + { + if (MetadataForWellKnownSpecialDataTypeAttributes.Count == 0) + return null; + else if (MetadataForWellKnownSpecialDataTypeAttributes.TryGetValue(attribute, out var metadata)) + return metadata; + + var baseAttributeTypes = attribute.GetBaseTypes(); + + foreach (ITypeSymbol baseAttributeType in baseAttributeTypes) + { + if (baseAttributeType.SpecialType == SpecialType.System_Object) + return null; + + if (MetadataForWellKnownSpecialDataTypeAttributes.TryGetValue(baseAttributeType, out var metadata)) + return metadata; + } + + return null; + } + /// /// Gets mixed database boundness attribute infos in the flattened set with consideration of type hierarchy.
/// If there are multiple mixed boundness attributes in the type hierarchy then only the most derived one should be included into results
@@ -255,11 +283,11 @@ private bool DacFieldTypeAttributeCapturedByMixedBoundnessAttributesTypeHierarch { pxContext.FieldAttributes.PXDBDataLengthAttribute, pxContext.SystemTypes.Int64 }, }; - var packagedIntegerAttribute = pxContext.FieldAttributes.PXDBPackedIntegerArrayAttribute; + var packedIntegerAttribute = pxContext.FieldAttributes.PXDBPackedIntegerArrayAttribute; - if (packagedIntegerAttribute != null) + if (packedIntegerAttribute != null) { - types.Add(packagedIntegerAttribute, pxContext.SystemTypes.UInt16Array); + types.Add(packedIntegerAttribute, pxContext.SystemTypes.UInt16Array); } return types; @@ -290,7 +318,7 @@ private static IEnumerable GetDacFieldTypeAttribu private static ImmutableArray GetWellKnownNonDataTypeAttributes(PXContext pxContext) { - var wellKnownNonDataTypeAttributes = ImmutableArray.CreateBuilder(initialCapacity: 6); + var wellKnownNonDataTypeAttributes = ImmutableArray.CreateBuilder(initialCapacity: 9); wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXUIFieldAttribute.Type); wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXDefaultAttribute); @@ -298,8 +326,34 @@ private static ImmutableArray GetWellKnownNonDataTypeAttributes(PXC wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXIntListAttribute.Type); wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXSelectorAttribute.Type); wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXForeignReferenceAttribute); + wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXParentAttribute); + wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.PXDBDefaultAttribute); + + if (pxContext.AttributeTypes.AutoNumberAttribute.IsDefined) + wellKnownNonDataTypeAttributes.Add(pxContext.AttributeTypes.AutoNumberAttribute.Type!); return wellKnownNonDataTypeAttributes.ToImmutable(); } + + private static ImmutableDictionary GetMetadataForWellKnownSpecialDataTypeAttributes(PXContext pxContext) + { + ImmutableDictionary? metadataForWellKnownSpecialDataTypeAttributes = null; + var packedIntegerAttribute = pxContext.FieldAttributes.PXDBPackedIntegerArrayAttribute; + + if (packedIntegerAttribute != null) + { + // PXDBPackedIntegerArrayAttribute is a special attribute written in a very hacky way. + // It derives from PXDBBinaryAttribute which works with byte[] but in reality PXDBPackedIntegerArrayAttribute works with ushort[]. + // This breaks Acumatica design principle where derived attribute work on properties with the same property type as their base attribute. + // Thus, the attribute needs special handling in Acuminator. + var packedIntegerMetadata = new DataTypeAttributeInfo(FieldTypeAttributeKind.BoundTypeAttribute, packedIntegerAttribute, + pxContext.SystemTypes.UInt16Array); + metadataForWellKnownSpecialDataTypeAttributes = + ImmutableDictionary.CreateRange(SymbolEqualityComparer.Default, + new[] { KeyValuePair.Create(packedIntegerAttribute as ITypeSymbol, packedIntegerMetadata) }); + } + + return metadataForWellKnownSpecialDataTypeAttributes ?? ImmutableDictionary.Empty; + } } } \ No newline at end of file diff --git a/src/Acuminator/Acuminator.Utilities/Roslyn/Semantic/Symbols/Attributes/DataTypeAttributeSymbols.cs b/src/Acuminator/Acuminator.Utilities/Roslyn/Semantic/Symbols/Attributes/DataTypeAttributeSymbols.cs index b493a7bc8..a647feffc 100644 --- a/src/Acuminator/Acuminator.Utilities/Roslyn/Semantic/Symbols/Attributes/DataTypeAttributeSymbols.cs +++ b/src/Acuminator/Acuminator.Utilities/Roslyn/Semantic/Symbols/Attributes/DataTypeAttributeSymbols.cs @@ -49,8 +49,8 @@ public class DataTypeAttributeSymbols : SymbolsSetBase public INamedTypeSymbol PXDBLongIdentityAttribute => Compilation.GetTypeByMetadataName(TypeFullNames.PXDBLongIdentityAttribute)!; public INamedTypeSymbol PXDBBinaryAttribute => Compilation.GetTypeByMetadataName(TypeFullNames.PXDBBinaryAttribute)!; - public INamedTypeSymbol PXDBPackedIntegerArrayAttribute => - Compilation.GetTypeByMetadataName(TypeFullNames.PXDBPackedIntegerArrayAttributeFullName_Acumatica2018R2)!; + public INamedTypeSymbol? PXDBPackedIntegerArrayAttribute => + Compilation.GetTypeByMetadataName(TypeFullNames.PXDBPackedIntegerArrayAttributeFullName_Acumatica2018R2); public INamedTypeSymbol PXDBUserPasswordAttribute => Compilation.GetTypeByMetadataName(TypeFullNames.PXDBUserPasswordAttribute)!;