Skip to content

Commit

Permalink
Merge pull request #365 from StefanMaron/development
Browse files Browse the repository at this point in the history
Rule0035 Explicitly set AllowInCustomizations
  • Loading branch information
Arthurvdv authored Nov 26, 2023
2 parents 93d89d2 + a964192 commit f804a39
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 0 deletions.
104 changes: 104 additions & 0 deletions Design/Rule0035ExplicitSetAllowInCustomizations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using Microsoft.Dynamics.Nav.Analyzers.Common.AppSourceCopConfiguration;
using Microsoft.Dynamics.Nav.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics;
using Microsoft.Dynamics.Nav.CodeAnalysis.Symbols;
using System.Collections.Immutable;
using System.Collections.ObjectModel;

namespace BusinessCentral.LinterCop.Design
{
[DiagnosticAnalyzer]
public class Rule0035ExplicitSetAllowInCustomizations : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create<DiagnosticDescriptor>(DiagnosticDescriptors.Rule0035ExplicitSetAllowInCustomizations);

public override void Initialize(AnalysisContext context)
=> context.RegisterSymbolAction(new Action<SymbolAnalysisContext>(this.AnalyzeAllowInCustomization), new SymbolKind[] {
SymbolKind.Table,
SymbolKind.TableExtension,
});

private void AnalyzeAllowInCustomization(SymbolAnalysisContext ctx)
{
// The AllowInCustomizations property is supported from runtime versions 12.0 or greater.
var manifest = AppSourceCopConfigurationProvider.GetManifest(ctx.Compilation);
if (manifest.Runtime < RuntimeVersion.Fall2023) return;

if (ctx.Symbol.IsObsoletePending || ctx.Symbol.IsObsoleteRemoved) return;
if (ctx.Symbol.GetContainingObjectTypeSymbol().IsObsoletePending || ctx.Symbol.GetContainingObjectTypeSymbol().IsObsoleteRemoved) return;

ICollection<IFieldSymbol> tableFields = GetTableFields(ctx.Symbol).Where(x => x.Id > 0 && x.Id < 2000000000)
.Where(x => x.GetProperty(PropertyKind.AllowInCustomizations) is null)
.ToList();
if (!tableFields.Any()) return;

IEnumerable<IApplicationObjectTypeSymbol> relatedPages = GetRelatedPages(ctx);
if (!relatedPages.Any()) return;

NavTypeKind navTypeKind = ctx.Symbol.GetContainingObjectTypeSymbol().GetNavTypeKindSafe();
ICollection<IFieldSymbol> pageFields = GetPageFields(navTypeKind, relatedPages);
ICollection<IFieldSymbol> fieldsNotReferencedOnPage = tableFields.Except(pageFields).ToList();
foreach (IFieldSymbol field in fieldsNotReferencedOnPage)
ctx.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.Rule0035ExplicitSetAllowInCustomizations, field.Location));
}

private static ICollection<IFieldSymbol> GetTableFields(ISymbol symbol)
{
switch (symbol.GetContainingObjectTypeSymbol().GetNavTypeKindSafe())
{
case NavTypeKind.Table:
return ((ITableTypeSymbol)symbol).Fields;
case NavTypeKind.TableExtension:
return ((ITableExtensionTypeSymbol)symbol).AddedFields;
default:
return new Collection<IFieldSymbol>();
}
}

private static ICollection<IFieldSymbol> GetPageFields(NavTypeKind navTypeKind, IEnumerable<IApplicationObjectTypeSymbol> relatedPages)
{
ICollection<IFieldSymbol> pageFields = new Collection<IFieldSymbol>();
switch (navTypeKind)
{
case NavTypeKind.Table:
foreach (IPageTypeSymbol page in relatedPages.Cast<IPageTypeSymbol>())
{
IEnumerable<IFieldSymbol> fields = page.FlattenedControls.Where(x => x.ControlKind == ControlKind.Field && x.RelatedFieldSymbol != null)
.Select(x => (IFieldSymbol)x.RelatedFieldSymbol.OriginalDefinition);

pageFields = pageFields.Union(fields).Distinct().ToList();
}
return pageFields;
case NavTypeKind.TableExtension:
foreach (IPageExtensionTypeSymbol page in relatedPages.Cast<IPageExtensionTypeSymbol>())
{
IEnumerable<IFieldSymbol> fields = page.AddedControlsFlattened.Where(x => x.ControlKind == ControlKind.Field && x.RelatedFieldSymbol != null)
.Select(x => (IFieldSymbol)x.RelatedFieldSymbol.OriginalDefinition);

pageFields = pageFields.Union(fields).Distinct().ToList();
}
return pageFields;
default:
return pageFields;
}
}

private static IEnumerable<IApplicationObjectTypeSymbol> GetRelatedPages(SymbolAnalysisContext ctx)
{
switch (ctx.Symbol.GetContainingObjectTypeSymbol().GetNavTypeKindSafe())
{
case NavTypeKind.Table:
return ctx.Compilation.GetDeclaredApplicationObjectSymbols()
.Where(x => x.GetNavTypeKindSafe() == NavTypeKind.Page)
.Where(x => ((IPageTypeSymbol)x.GetTypeSymbol()).PageType != PageTypeKind.API)
.Where(x => ((IPageTypeSymbol)x.GetTypeSymbol()).RelatedTable == (ITableTypeSymbol)ctx.Symbol);
case NavTypeKind.TableExtension:
return ctx.Compilation.GetDeclaredApplicationObjectSymbols()
.Where(x => x.GetNavTypeKindSafe() == NavTypeKind.PageExtension)
.Where(x => ((IPageTypeSymbol)((IApplicationObjectExtensionTypeSymbol)x).Target.GetTypeSymbol()).RelatedTable == ((IApplicationObjectExtensionTypeSymbol)ctx.Symbol).Target);
default:
return null;
}
}
}
}
5 changes: 5 additions & 0 deletions LinterCop.ruleset.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@
"id": "LC0034",
"action": "Hidden",
"justification": "The property Extensible should be explicitly set for public objects."
},
{
"id": "LC0035",
"action": "Info",
"justification": "Explicitly set AllowInCustomizations for fields omitted on pages."
}
]
}
1 change: 1 addition & 0 deletions LinterCopAnalyzers.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ public static class DiagnosticDescriptors
public static readonly DiagnosticDescriptor Rule0032ClearCodeunitSingleInstance = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0032", (LocalizableString)new LocalizableResourceString("Rule0032ClearCodeunitSingleInstanceTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0032ClearCodeunitSingleInstanceFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Warning, true, (LocalizableString)new LocalizableResourceString("Rule0032ClearCodeunitSingleInstanceDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0032");
public static readonly DiagnosticDescriptor Rule0033AppManifestRuntimeBehind = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0033", (LocalizableString)new LocalizableResourceString("Rule0033AppManifestRuntimeBehindTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0033AppManifestRuntimeBehindFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0033AppManifestRuntimeBehindTitleDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0033");
public static readonly DiagnosticDescriptor Rule0034ExtensiblePropertyShouldAlwaysBeSet = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0034", (LocalizableString)new LocalizableResourceString("Rule0034ExtensiblePropertyShouldAlwaysBeSetTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0034ExtensiblePropertyShouldAlwaysBeSetFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Hidden, true, (LocalizableString)new LocalizableResourceString("Rule0034ExtensiblePropertyShouldAlwaysBeSetDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0034");
public static readonly DiagnosticDescriptor Rule0035ExplicitSetAllowInCustomizations = new DiagnosticDescriptor(LinterCopAnalyzers.AnalyzerPrefix + "0035", (LocalizableString)new LocalizableResourceString("Rule0035ExplicitSetAllowInCustomizationsTitle", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), (LocalizableString)new LocalizableResourceString("Rule0035ExplicitSetAllowInCustomizationsFormat", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "Design", DiagnosticSeverity.Info, true, (LocalizableString)new LocalizableResourceString("Rule0035ExplicitSetAllowInCustomizationsDescription", LinterCopAnalyzers.ResourceManager, typeof(LinterCopAnalyzers)), "https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0035");
}
}
9 changes: 9 additions & 0 deletions LinterCopAnalyzers.resx
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,13 @@
<data name="Rule0034ExtensiblePropertyShouldAlwaysBeSetDescription" xml:space="preserve">
<value>The property Extensible should be explicitly set for {0} objects.</value>
</data>
<data name="Rule0035ExplicitSetAllowInCustomizationsTitle" xml:space="preserve">
<value>Explicitly set AllowInCustomizations for fields omitted on pages.</value>
</data>
<data name="Rule0035ExplicitSetAllowInCustomizationsFormat" xml:space="preserve">
<value>Explicitly set AllowInCustomizations for fields omitted on pages.</value>
</data>
<data name="Rule0035ExplicitSetAllowInCustomizationsDescription" xml:space="preserve">
<value>Explicitly set AllowInCustomizations for fields omitted on pages.</value>
</data>
</root>
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Further note that you should have BcContainerHelper version 2.0.16 (or newer) in
|[LC0032](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0032)|Clear(All) does not affect or change values for global variables in single instance codeunits.|Warning|
|[LC0033](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0033)|The specified runtime version in app.json is falling behind.|Info|
|[LC0034](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0034)|The property `Extensible` should be explicitly set for public objects.|Disabled|
|[LC0035](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0035)|Explicitly set `AllowInCustomizations` for fields omitted on pages.|Info|

## Configuration

Expand Down

0 comments on commit f804a39

Please sign in to comment.