Skip to content

Commit

Permalink
Merge pull request #1214 from JasonBock/1100-operation-return-types
Browse files Browse the repository at this point in the history
closes #1100 operation return types
  • Loading branch information
rockfordlhotka authored Jul 27, 2019
2 parents c615761 + 844f7dc commit 4cdd61a
Show file tree
Hide file tree
Showing 21 changed files with 488 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Csla.Analyzers.IntegrationTests
{
[Serializable]
public sealed class BusyProperties
: BusinessBase<BusyProperties>
{
public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(c => c.Id);
public int Id
{
get
{
var x = 42;
return ReadProperty(IdProperty) + x;
}
private set { LoadProperty(IdProperty, value); }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;

namespace Csla.Analyzers.IntegrationTests
{
[Serializable]
public sealed class OperationReturnValues
: BusinessBase<OperationReturnValues>
{
private void Foo() { }
private void DataPortal_Fetch(Guid id) { }
private Task DataPortal_Fetch(int id) => Task.CompletedTask;
private string DataPortal_Fetch() => string.Empty;
}

public sealed class OperationReturnValuesNotCsla
{
private string DataPortal_Fetch() => string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Csla.Core;

namespace Csla.Analyzers.IntegrationTests
{
public interface PublicForInterface
: IBusinessObject
{
void DataPortal_Fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Csla.Analyzers.IntegrationTests
{
[Serializable]
public sealed class PublicOperation
: BusinessBase<PublicOperation>
{
public void DataPortal_Fetch() { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Csla.Analyzers.Tests
{
[TestClass]
public sealed class FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFixTests
{
[TestMethod]
public void VerifyGetFixableDiagnosticIds()
{
var fix = new FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFix();
var ids = fix.FixableDiagnosticIds.ToList();

Assert.AreEqual(1, ids.Count, nameof(ids.Count));
Assert.AreEqual(ids[0], Constants.AnalyzerIdentifiers.FindOperationsWithIncorrectReturnTypes,
nameof(Constants.AnalyzerIdentifiers.FindOperationsWithIncorrectReturnTypes));
}

[TestMethod]
public async Task VerifyGetFixesWhenChangingToVoid()
{
var code =
@"using Csla;
public class A : BusinessBase<A>
{
public string DataPortal_Fetch() { }
}";

var document = TestHelpers.Create(code);
var tree = await document.GetSyntaxTreeAsync();
var diagnostics = await TestHelpers.GetDiagnosticsAsync(code, new FindOperationsWithIncorrectReturnTypesAnalyzer());
var sourceSpan = diagnostics[0].Location.SourceSpan;

var actions = new List<CodeAction>();
var codeActionRegistration = new Action<CodeAction, ImmutableArray<Diagnostic>>(
(a, _) => { actions.Add(a); });

var fix = new FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFix();
var codeFixContext = new CodeFixContext(document, diagnostics[0],
codeActionRegistration, new CancellationToken(false));
await fix.RegisterCodeFixesAsync(codeFixContext);

Assert.AreEqual(1, actions.Count, nameof(actions.Count));

await TestHelpers.VerifyActionAsync(actions,
FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFixConstants.ChangeReturnTypeToVoidDescription, document,
tree, new[] { "void" });
}

[TestMethod]
public async Task VerifyGetFixesWhenChangingToTask()
{
var code =
@"using Csla;
using System.Threading.Tasks;
public class A : BusinessBase<A>
{
public async string DataPortal_Fetch() { }
}";

var document = TestHelpers.Create(code);
var tree = await document.GetSyntaxTreeAsync();
var diagnostics = await TestHelpers.GetDiagnosticsAsync(code, new FindOperationsWithIncorrectReturnTypesAnalyzer());
var sourceSpan = diagnostics[0].Location.SourceSpan;

var actions = new List<CodeAction>();
var codeActionRegistration = new Action<CodeAction, ImmutableArray<Diagnostic>>(
(a, _) => { actions.Add(a); });

var fix = new FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFix();
var codeFixContext = new CodeFixContext(document, diagnostics[0],
codeActionRegistration, new CancellationToken(false));
await fix.RegisterCodeFixesAsync(codeFixContext);

Assert.AreEqual(1, actions.Count, nameof(actions.Count));

await TestHelpers.VerifyActionAsync(actions,
FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFixConstants.ChangeReturnTypeToTaskDescription, document,
tree, new[] { "Task" });
}

[TestMethod]
public async Task VerifyGetFixesWhenChangingToTaskAndUsingDoesNotExist()
{
var code =
@"using Csla;
public class A : BusinessBase<A>
{
public async string DataPortal_Fetch() { }
}";

var document = TestHelpers.Create(code);
var tree = await document.GetSyntaxTreeAsync();
var diagnostics = await TestHelpers.GetDiagnosticsAsync(code, new FindOperationsWithIncorrectReturnTypesAnalyzer());
var sourceSpan = diagnostics[0].Location.SourceSpan;

var actions = new List<CodeAction>();
var codeActionRegistration = new Action<CodeAction, ImmutableArray<Diagnostic>>(
(a, _) => { actions.Add(a); });

var fix = new FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFix();
var codeFixContext = new CodeFixContext(document, diagnostics[0],
codeActionRegistration, new CancellationToken(false));
await fix.RegisterCodeFixesAsync(codeFixContext);

Assert.AreEqual(1, actions.Count, nameof(actions.Count));

await TestHelpers.VerifyActionAsync(actions,
FindOperationsWithIncorrectReturnTypeResolveCorrectTypeCodeFixConstants.ChangeReturnTypeToTaskDescription, document,
tree, new[] { "using System.Threading.Tasks;", "Task" });
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Csla.Analyzers.Tests
{
[TestClass]
public sealed class FindOperationsWithIncorrectReturnTypesAnalyzerTests
{
[TestMethod]
public void VerifySupportedDiagnostics()
{
var analyzer = new FindOperationsWithIncorrectReturnTypesAnalyzer();
var diagnostics = analyzer.SupportedDiagnostics;
Assert.AreEqual(1, diagnostics.Length);

var diagnostic = diagnostics[0];
Assert.AreEqual(Constants.AnalyzerIdentifiers.FindOperationsWithIncorrectReturnTypes, diagnostic.Id,
nameof(DiagnosticDescriptor.Id));
Assert.AreEqual(FindOperationsWithIncorrectReturnTypesAnalyzerConstants.Title, diagnostic.Title.ToString(),
nameof(DiagnosticDescriptor.Title));
Assert.AreEqual(FindOperationsWithIncorrectReturnTypesAnalyzerConstants.Message, diagnostic.MessageFormat.ToString(),
nameof(DiagnosticDescriptor.MessageFormat));
Assert.AreEqual(Constants.Categories.Design, diagnostic.Category,
nameof(DiagnosticDescriptor.Category));
Assert.AreEqual(DiagnosticSeverity.Error, diagnostic.DefaultSeverity,
nameof(DiagnosticDescriptor.DefaultSeverity));
Assert.AreEqual(HelpUrlBuilder.Build(Constants.AnalyzerIdentifiers.FindOperationsWithIncorrectReturnTypes, nameof(FindOperationsWithIncorrectReturnTypesAnalyzer)),
diagnostic.HelpLinkUri,
nameof(DiagnosticDescriptor.HelpLinkUri));
}

[TestMethod]
public async Task AnalyzeWithNotMobileObject()
{
var code = "public class A { }";
await TestHelpers.RunAnalysisAsync<FindOperationsWithIncorrectReturnTypesAnalyzer>(
code, Array.Empty<string>());
}

[TestMethod]
public async Task AnalyzeWithMobileObjectAndMethodIsNotOperation()
{
var code =
@"using Csla;
public class A : BusinessBase<A>
{
public void Foo() { }
}";
await TestHelpers.RunAnalysisAsync<FindOperationsWithIncorrectReturnTypesAnalyzer>(
code, Array.Empty<string>());
}

[TestMethod]
public async Task AnalyzeWithMobileObjectAndMethodIsOperationReturningVoid()
{
var code =
@"using Csla;
public class A : BusinessBase<A>
{
private void DataPortal_Fetch() { }
}";
await TestHelpers.RunAnalysisAsync<FindOperationsWithIncorrectReturnTypesAnalyzer>(
code, Array.Empty<string>());
}

[TestMethod]
public async Task AnalyzeWithMobileObjectAndMethodIsOperationReturningTask()
{
var code =
@"using Csla;
using System.Threading.Tasks;
public class A : BusinessBase<A>
{
private async Task DataPortal_Fetch() { }
}";
await TestHelpers.RunAnalysisAsync<FindOperationsWithIncorrectReturnTypesAnalyzer>(
code, Array.Empty<string>());
}

[TestMethod]
public async Task AnalyzeWithMobileObjectAndMethodIsOperationReturningIncorrectType()
{
var code =
@"using Csla;
public class A : BusinessBase<A>
{
private string DataPortal_Fetch() { }
}";
await TestHelpers.RunAnalysisAsync<FindOperationsWithIncorrectReturnTypesAnalyzer>(
code, new[] { Constants.AnalyzerIdentifiers.FindOperationsWithIncorrectReturnTypes });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ public sealed class CheckConstructorsAnalyzer
publicNoArgumentConstructorIsMissingRule,
constructorHasParametersRule);

public override void Initialize(AnalysisContext context) =>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeClassDeclaration, SyntaxKind.ClassDeclaration);
}

private static void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context)
{
Expand Down
3 changes: 2 additions & 1 deletion Source/Csla.Analyzers/Csla.Analyzers/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ public static class AnalyzerIdentifiers
public const string IsOperationMethodPublic = "CSLA0002";
public const string PublicNoArgumentConstructorIsMissing = "CSLA0003";
public const string ConstructorHasParameters = "CSLA0004";
public const string FindBusinessObjectCreation = "CSLA0011";
public const string FindSaveAssignmentIssue = "CSLA0005";
public const string FindSaveAsyncAssignmentIssue = "CSLA0006";
public const string OnlyUseCslaPropertyMethodsInGetSetRule = "CSLA0007";
public const string EvaluateManagedBackingFields = "CSLA0008";
public const string IsOperationMethodPublicForInterface = "CSLA0009";
public const string FindOperationsWithNonSerializableArguments = "CSLA0010";
public const string FindBusinessObjectCreation = "CSLA0011";
public const string FindOperationsWithIncorrectReturnTypes = "CSLA0012";
}

public static class Categories
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ public sealed class EvaluateManagedBackingFieldsAnalayzer
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(mustBePublicStaticAndReadonlyRule);

public override void Initialize(AnalysisContext context) =>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeFieldDeclaration, SyntaxKind.FieldDeclaration);
}

private static void AnalyzeFieldDeclaration(SyntaxNodeAnalysisContext context)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node)
foreach (var argument in node.ArgumentList.Arguments)
{
var argumentSymbol = Model.GetSymbolInfo(argument.Expression).Symbol;
UsesField = argumentSymbol != null && argumentSymbol == FieldSymbol;
UsesField = argumentSymbol != null && Equals(argumentSymbol, FieldSymbol);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ public sealed class EvaluatePropertiesForSimplicityAnalyzer

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(onlyUseCslaPropertyMethodsInGetSetRule);

public override void Initialize(AnalysisContext context) =>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzePropertyDeclaration, SyntaxKind.PropertyDeclaration);
}

private static void AnalyzePropertyDeclaration(SyntaxNodeAnalysisContext context)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
using Microsoft.CodeAnalysis;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using static System.Reflection.IntrospectionExtensions;

namespace Csla.Analyzers.Extensions
{
Expand Down Expand Up @@ -62,21 +58,6 @@ internal static bool IsEditableStereotype(this ITypeSymbol @this)
@this.BaseType.IsEditableStereotype());
}

private static ImmutableArray<PropertyInfo> GetAllProperties(this ITypeSymbol @this)
{
var properties = new List<PropertyInfo>();

var type = @this.GetType().GetTypeInfo();

while(type != null)
{
properties.AddRange(type.DeclaredProperties);
type = type.BaseType?.GetTypeInfo();
}

return properties.ToImmutableArray();
}

internal static bool IsStereotype(this ITypeSymbol @this)
{
return @this != null &&
Expand Down
Loading

0 comments on commit 4cdd61a

Please sign in to comment.