Skip to content

Commit

Permalink
added tests and changed analyzer to check for empty properties (Marim…
Browse files Browse the repository at this point in the history
…erLLC#4097)

Co-authored-by: Rockford Lhotka <rocky@lhotka.net>
  • Loading branch information
luizfbicalho and rockfordlhotka committed Jul 13, 2024
1 parent 74987e6 commit 97474c1
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public class A : BusinessBase<A>
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(
code,
[Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule]);
[Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule,Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule]);
}

[TestMethod]
Expand All @@ -233,7 +233,141 @@ public string Data
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, [Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule]);
}
[TestMethod]
public async Task AnalyzeWhenClassHasEmptyGetterAndSetter()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> DataProperty = RegisterProperty<string>(_ => _.Data);

public string Data
{
get;
set;
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, [Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule, Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule]);
}
[TestMethod]
public async Task AnalyzeWhenClassHasEmptyGetter()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> DataProperty = RegisterProperty<string>(_ => _.Data);

public string Data
{
get;
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, [Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule]);
}
[TestMethod]
public async Task AnalyzeWhenClassHasPrivateField()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> CityProperty = RegisterProperty<string>(nameof(City), RelationshipTypes.PrivateField);
private string _city = CityProperty.DefaultValue;
public string City
{
get => GetProperty(CityProperty, _city);
set => SetProperty(CityProperty, ref _city, value);
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, []);
}
[TestMethod]
public async Task AnalyzeWhenClassHasMethodExpression()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> CityProperty = RegisterProperty<string>(nameof(City));

public string City
{
get => GetProperty(CityProperty);
set => SetProperty(CityProperty, value);
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, []);
}
[TestMethod]
public async Task AnalyzeWhenClassHasGetterExpression()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> CityProperty = RegisterProperty<string>(nameof(City));

public string City => GetProperty(CityProperty);
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, []);
}
[TestMethod]
public async Task AnalyzeWhenClassBody()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> CityProperty = RegisterProperty<string>(nameof(City));

public string City
{
get { return GetProperty(CityProperty); }
set { SetProperty(CityProperty, value); }
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, []);
}
[TestMethod]
public async Task AnalyzeWhenClassHasOnlySetMethodExpression()
{
var code =
"""
using Csla;

public class A : BusinessBase<A>
{
public static readonly PropertyInfo<string> CityProperty = RegisterProperty<string>(nameof(City));

public string City
{
set => SetProperty(CityProperty, value);
}
}
""";
await TestHelpers.RunAnalysisAsync<EvaluatePropertiesForSimplicityAnalyzer>(code, [Constants.AnalyzerIdentifiers.OnlyUseCslaPropertyMethodsInGetSetRule]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,22 @@ private static void AnalyzePropertyDeclaration(SyntaxNodeAnalysisContext context
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyNode);
var classSymbol = propertySymbol.ContainingType;

var fields = GetFieldDeclarations(propertyNode,context.SemanticModel);

if (propertySymbol != null && classSymbol != null &&
classSymbol.IsStereotype() && !propertySymbol.IsAbstract &&
!propertySymbol.IsStatic)
!propertySymbol.IsStatic && fields.Any())
{
if (propertySymbol.GetMethod != null)
{
AnalyzePropertyGetter(propertyNode, context);
}
else
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
propertyNode.GetLocation()));
}

if (propertySymbol.SetMethod != null)
{
Expand Down Expand Up @@ -88,84 +96,164 @@ private static void AnalyzePropertyGetterWithExpressionBody(PropertyDeclarationS

private static void AnalyzePropertyGetterWithGet(PropertyDeclarationSyntax propertyNode, SyntaxNodeAnalysisContext context)
{
var getter = propertyNode.AccessorList.Accessors.Single(
_ => _.IsKind(SyntaxKind.GetAccessorDeclaration)).Body;
var accessor = propertyNode.AccessorList.Accessors.Single(_ => _.IsKind(SyntaxKind.GetAccessorDeclaration));
var getterBody = accessor.Body;
var getterExpression = accessor.ExpressionBody;

var getterWalker = new FindGetOrReadInvocationsWalker(getter, context.SemanticModel);
var getterWalkerBody = new FindGetOrReadInvocationsWalker(getterBody, context.SemanticModel);
var getterWalkerExpression = new FindGetOrReadInvocationsWalker(getterExpression, context.SemanticModel);

if (getterWalker.Invocation != null)
if (getterWalkerBody.Invocation != null)
{
var getterStatements = getter.Statements;
var getterStatements = getterBody.Statements;

if (getterStatements.Count != 1)
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
getter.GetLocation()));
getterBody.GetLocation()));
}
else
{

if (!(getterStatements[0] is ReturnStatementSyntax returnNode))
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
getter.GetLocation()));
getterBody.GetLocation()));
}
else
{

if (!(returnNode.ChildNodes().SingleOrDefault(
_ => _.IsKind(SyntaxKind.InvocationExpression)) is InvocationExpressionSyntax invocation) || invocation != getterWalker.Invocation)
_ => _.IsKind(SyntaxKind.InvocationExpression)) is InvocationExpressionSyntax invocation) || invocation != getterWalkerBody.Invocation)
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
getter.GetLocation()));
getterBody.GetLocation()));
}
}
}
}
else if (getterWalkerExpression.Invocation != null)
{
var getterStatements = getterExpression.Expression;
if (!(getterExpression.Expression is InvocationExpressionSyntax invocation) || invocation != getterWalkerExpression.Invocation)
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
getterExpression.GetLocation()));
}
}
else
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
accessor.GetLocation()));
}
}

private static void AnalyzePropertySetter(PropertyDeclarationSyntax propertyNode, SyntaxNodeAnalysisContext context)
{
var setter = propertyNode.AccessorList.Accessors.Single(
_ => _.IsKind(SyntaxKind.SetAccessorDeclaration)).Body;
var accessor = propertyNode.AccessorList.Accessors.Single(_ => _.IsKind(SyntaxKind.SetAccessorDeclaration));
var setterBody = accessor.Body;
var setterExpression = accessor.ExpressionBody;

var setterWalker = new FindSetOrLoadInvocationsWalker(setter, context.SemanticModel);
var setterWalkerBody = new FindSetOrLoadInvocationsWalker(setterBody, context.SemanticModel);
var setterWalkerExpression = new FindGetOrReadInvocationsWalker(setterExpression, context.SemanticModel);

if (setterWalker.Invocation != null)
if (setterWalkerBody.Invocation != null)
{
var setterStatements = setter.Statements;
var setterStatements = setterBody.Statements;

if (setterStatements.Count != 1)
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
setter.GetLocation()));
setterBody.GetLocation()));
}
else
{

if (!(setterStatements[0] is ExpressionStatementSyntax expressionNode))
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
setter.GetLocation()));
setterBody.GetLocation()));
}
else
{

if (!(expressionNode.ChildNodes().SingleOrDefault(
_ => _.IsKind(SyntaxKind.InvocationExpression)) is InvocationExpressionSyntax invocation) || invocation != setterWalker.Invocation)
_ => _.IsKind(SyntaxKind.InvocationExpression)) is InvocationExpressionSyntax invocation) || invocation != setterWalkerBody.Invocation)
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
setter.GetLocation()));
setterBody.GetLocation()));
}
}
}
}
else if (setterWalkerExpression.Invocation != null)
{
var getterStatements = setterExpression.Expression;
if (!(setterExpression.Expression is InvocationExpressionSyntax invocation) || invocation != setterWalkerExpression.Invocation)
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
setterExpression.GetLocation()));
}
}
else
{
context.ReportDiagnostic(Diagnostic.Create(
onlyUseCslaPropertyMethodsInGetSetRule,
accessor.GetLocation()));
}
}
public static IEnumerable<FieldDeclarationSyntax> GetFieldDeclarations(PropertyDeclarationSyntax propertyDeclaration,SemanticModel semanticModel)
{
var classDeclaration = propertyDeclaration.FirstAncestorOrSelf<ClassDeclarationSyntax>();
var propertyType = semanticModel.GetTypeInfo(propertyDeclaration.Type).Type;
// Check if the classDeclaration is null
if (classDeclaration == null)
{
throw new ArgumentNullException(nameof(classDeclaration));
}

// Find all field declarations in the class
var fieldDeclarations = classDeclaration.Members
.OfType<FieldDeclarationSyntax>();

// Filter for static fields
return fieldDeclarations
.Where(field => FilterField(field,propertyDeclaration, propertyType,semanticModel));
}

private static bool FilterField(FieldDeclarationSyntax fieldDeclaration, PropertyDeclarationSyntax propertyDeclaration, ITypeSymbol propertyType,SemanticModel semanticModel)
{
var fieldType = semanticModel.GetTypeInfo(fieldDeclaration.Declaration.Type).Type;
if (fieldType != null && fieldType.OriginalDefinition.ToString() == "Csla.PropertyInfo<T>")
{
var typeArgument = ((INamedTypeSymbol)fieldType).TypeArguments[0];
if (SymbolEqualityComparer.Default.Equals(typeArgument, propertyType))
{
var initializer = fieldDeclaration.Declaration.Variables
.Select(a => a.Initializer.Value)
.OfType<InvocationExpressionSyntax>().Select(w=>w.ArgumentList.Arguments.FirstOrDefault()?.Expression);
var lambda = initializer
.OfType<SimpleLambdaExpressionSyntax>()
.Select(s => s.Body)
.OfType<MemberAccessExpressionSyntax>()
.Any(a=>a.Name.Identifier.ValueText == propertyDeclaration.Identifier.ValueText);

var name = initializer
.OfType<InvocationExpressionSyntax>()
.Select(s=>s.ArgumentList.Arguments.FirstOrDefault()?.Expression)
.OfType<IdentifierNameSyntax>()
.Any(a=>a.Identifier.ValueText == propertyDeclaration.Identifier.ValueText);
return name || lambda;
}
}
return false;
}
}
}

0 comments on commit 97474c1

Please sign in to comment.