Skip to content

Commit b854a25

Browse files
authored
Merge pull request #621 from Acumatica/feature/ATR-728-dev-graph-declaration-diagnostic
Feature/atr 728 dev graph declaration diagnostic
2 parents 808f47a + a2e55fd commit b854a25

19 files changed

+645
-4
lines changed

docs/Summary.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,4 @@ Acuminator does not perform static analysis of projects whose names contain `Tes
112112
| [PX1102](diagnostics/PX1102.md) | The additional delegate parameter of the method with the `PXOverride` attribute must have a name conforming Acumatica naming conventions. | Warning | Available |
113113
| [PX1110](diagnostics/PX1110.md) | The DAC has a DAC field with the `PXDBLocalizableString` attribute. Therefore, this DAC must declare a `NoteID` DAC field. | Error | Available |
114114
| [PX1111](diagnostics/PX1111.md) | The primary DAC of a processing view must contain the `NoteID` field. | Error | Unavailable |
115+
| [PX1112](diagnostics/PX1112.md) | Graphs and graph extensions with generic type parameters must be abstract. | Error | Available |

docs/diagnostics/PX1112.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# PX1112
2+
This document describes the PX1112 diagnostic.
3+
4+
## Summary
5+
6+
| Code | Short Description | Type | Code Fix |
7+
| ------ | -------------------------------------------------------------------------- | ----- | --------- |
8+
| PX1112 | Graphs and graph extensions with generic type parameters must be abstract. | Error | Available |
9+
10+
## Diagnostic Description
11+
12+
The PX1112 diagnostic detects and reports non-abstract graphs and graph extensions with generic type parameters.
13+
14+
In Acumatica Framework, graphs and graph extensions with generic type parameters must be declared as `abstract`. This requirement ensures proper inheritance and instantiation patterns within the Acumatica Framework.
15+
Generic graphs and graph extensions serve as base classes that define common functionality to be shared across multiple derived implementations.
16+
Since they contain unresolved type parameters, they cannot be directly instantiated by the framework. The `abstract` modifier makes this constraint explicit, keeps graph extensions relationships consistent,
17+
and prevents accidental instantiation attempts.
18+
19+
## Code Fix Behavior
20+
21+
The PX1112 code fix automatically adds the `abstract` modifier to the graph or graph extension declaration.
22+
If the class has a `sealed` modifier, it is automatically removed (since `abstract` and `sealed` are mutually exclusive).
23+
24+
## Example of Incorrect Code
25+
26+
```C#
27+
using PX.Data;
28+
29+
namespace Examples
30+
{
31+
// Generic graph without abstract modifier - reports PX1112
32+
public class GenericOrderGraph<TOrderDac> : PXGraph<GenericOrderGraph<TOrderDac>>
33+
where TOrderDac : PXBqlTable, IBqlTable, new()
34+
{
35+
public PXSelect<TOrderDac> Orders = null!;
36+
37+
// Business logic
38+
}
39+
}
40+
```
41+
42+
## Example of Correct Code after Code Fix
43+
44+
```C#
45+
using PX.Data;
46+
47+
namespace Examples
48+
{
49+
public abstract class GenericOrderGraph<TOrderDac> : PXGraph<GenericOrderGraph<TOrderDac>>
50+
where TOrderDac : PXBqlTable, IBqlTable, new()
51+
{
52+
public PXSelect<TOrderDac> Orders = null!;
53+
54+
// Business logic
55+
}
56+
}
57+
```
58+
59+
## Related Articles
60+
61+
- [Business Logic Implementation](https://help.acumatica.com/Help?ScreenId=ShowWiki&pageid=b02bd27f-3e86-4210-9e0f-c0df7aa701d9)
62+
- [Use of Generic Graph Extensions by the System](https://help.acumatica.com/Help?ScreenId=ShowWiki&pageid=eece8358-f6ea-47e8-899a-d466eccbc14e)
63+
- [Reusing Business Logic](https://help.acumatica.com/Help?ScreenId=ShowWiki&pageid=76b8c160-89bd-4501-9f9f-1dabc648d417)

src/Acuminator/Acuminator.Analyzers/DiagnosticsShortName.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Acuminator/Acuminator.Analyzers/DiagnosticsShortName.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,4 +462,7 @@
462462
<data name="PX1111" xml:space="preserve">
463463
<value>MainDacOfProcessingViewMustContainNoteIdField</value>
464464
</data>
465+
<data name="PX1112" xml:space="preserve">
466+
<value>GenericNonAbstractGraphOrGraphExtension</value>
467+
</data>
465468
</root>

src/Acuminator/Acuminator.Analyzers/Resources.Designer.cs

Lines changed: 20 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Acuminator/Acuminator.Analyzers/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,12 @@ Reason: {2}</value>
869869
<data name="PX1111TitleFormat" xml:space="preserve">
870870
<value>The primary DAC "{0}" of the processing view "{1}" must contain the "NoteID" field</value>
871871
</data>
872+
<data name="PX1112Fix" xml:space="preserve">
873+
<value>Make the type abstract</value>
874+
</data>
875+
<data name="PX1112Title" xml:space="preserve">
876+
<value>Graphs and graph extensions with generic type parameters must be abstract</value>
877+
</data>
872878
<data name="SuppressDiagnosticGroupCodeActionTitle" xml:space="preserve">
873879
<value>Suppress the {0} diagnostic with Acuminator</value>
874880
</data>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Immutable;
4+
using System.Linq;
5+
using System.Threading;
6+
7+
using Acuminator.Analyzers.StaticAnalysis.PXGraph;
8+
using Acuminator.Utilities.Common;
9+
using Acuminator.Utilities.DiagnosticSuppression;
10+
using Acuminator.Utilities.Roslyn.Semantic;
11+
using Acuminator.Utilities.Roslyn.Semantic.PXGraph;
12+
13+
using Microsoft.CodeAnalysis;
14+
using Microsoft.CodeAnalysis.CSharp;
15+
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
using Microsoft.CodeAnalysis.Diagnostics;
17+
18+
namespace Acuminator.Analyzers.StaticAnalysis.DeclarationAnalysisGraphAndDac
19+
{
20+
public class GraphAndGraphExtensionDeclarationAnalyzer : PXGraphAggregatedAnalyzerBase
21+
{
22+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
23+
ImmutableArray.Create
24+
(
25+
Descriptors.PX1112_GenericGraphsAndGraphExtensionsMustBeAbstract
26+
);
27+
28+
public override void Analyze(SymbolAnalysisContext context, PXContext pxContext, PXGraphEventSemanticModel graphOrGraphExt)
29+
{
30+
context.CancellationToken.ThrowIfCancellationRequested();
31+
32+
CheckIfGraphOrGraphExtensionIsGenericNonAbstract(context, pxContext, graphOrGraphExt);
33+
}
34+
35+
protected virtual void CheckIfGraphOrGraphExtensionIsGenericNonAbstract(SymbolAnalysisContext context, PXContext pxContext,
36+
PXGraphEventSemanticModel graphOrGraphExt)
37+
{
38+
if (graphOrGraphExt.Symbol.IsAbstract || !graphOrGraphExt.Symbol.IsGenericType ||
39+
graphOrGraphExt.Symbol.TypeParameters.IsDefaultOrEmpty || graphOrGraphExt.Node == null)
40+
{
41+
return;
42+
}
43+
44+
var location = graphOrGraphExt.Node.Identifier.GetLocation().NullIfLocationKindIsNone() ??
45+
graphOrGraphExt.Node.GetLocation();
46+
var diagnostic = Diagnostic.Create(Descriptors.PX1112_GenericGraphsAndGraphExtensionsMustBeAbstract, location,
47+
graphOrGraphExt.Symbol.Name);
48+
49+
context.ReportDiagnosticWithSuppressionCheck(diagnostic, pxContext.CodeAnalysisSettings);
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)