Commit 90481eab authored by Nathan Bean's avatar Nathan Bean
Browse files

Refactored PropertySelfReferenceAnalyzer to use symbol matching

parent d81426fb
......@@ -9,12 +9,9 @@
<PropertyGroup>
<PackageId>KSU.CS.CodeAnalyzers</PackageId>
<PackageVersion>1.0.0.0</PackageVersion>
<PackageVersion>1.0.1.0</PackageVersion>
<Authors>Nathan H. Bean</Authors>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageProjectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</PackageProjectUrl>
<PackageIconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</PackageIconUrl>
<RepositoryUrl>http://REPOSITORY_URL_HERE_OR_DELETE_THIS_LINE</RepositoryUrl>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Description>A collection of code analyzers for verifying code style against KSU CS coding guidelines and for identifying common errors made by programming students.</Description>
<PackageReleaseNotes>Initial release</PackageReleaseNotes>
......@@ -45,6 +42,10 @@
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodeAnalyzers\CodeAnalyzers.csproj" />
</ItemGroup>
<Target Name="_AddAnalyzersToOutput">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\CodeAnalyzers.dll" PackagePath="analyzers/dotnet/cs" />
......
......@@ -56,86 +56,47 @@ namespace KSU.CS.Pendant.CodeAnalysis.DesignAnalyzers
// The context Node should be a PropertyDeclarationSyntax
var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
// Grab and evaluate any accessor declarations that go with this property
var accessorDeclarations = propertyDeclaration.DescendantNodes().OfType<AccessorDeclarationSyntax>();
foreach (var dec in accessorDeclarations)
{
// Check for a get self-reference
if (dec.IsKind(SyntaxKind.GetAccessorDeclaration))
{
// Get any self-references
var getSelfReference = dec.DescendantNodes()
.OfType<IdentifierNameSyntax>()
.Where(id => id.Identifier.ValueText == propertyDeclaration.Identifier.ValueText && !(
// Ignore enumeration access using the same identifier as the Enum type
id.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression) ||
// Ignore variable declaration using the same identifier as the Type
id.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
((VariableDeclarationSyntax)id.Parent).Type.ToString() == id.Identifier.ValueText
));
if (!getSelfReference.Any()) break;
// Create the diagnostic
var primaryLocation = propertyDeclaration.Identifier.GetLocation();
var additionalLocations = from sr in getSelfReference select sr.Identifier.GetLocation();
var diagnostic = Diagnostic.Create(
PropertySelfReferenceRule,
primaryLocation,
additionalLocations,
new string[] { propertyDeclaration.Identifier.ValueText, "get" }
);
context.ReportDiagnostic(diagnostic);
}
// Check for a set self-reference
if (dec.IsKind(SyntaxKind.SetAccessorDeclaration))
{
// Get any self-references
var setSelfReference = dec.DescendantNodes()
.OfType<IdentifierNameSyntax>()
.Where(id => id.Identifier.ValueText == propertyDeclaration.Identifier.ValueText && !(
// Ignore enumeration access using the same identifier as the Enum type
id.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression) ||
// Ignore variable declaration using the same identifier as the Type
id.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
((VariableDeclarationSyntax)id.Parent).Type.ToString() == id.Identifier.ValueText
));
if (!setSelfReference.Any()) break;
// Create the diagnostic
var primaryLocation = propertyDeclaration.Identifier.GetLocation();
var additionalLocations = from sr in setSelfReference select sr.Identifier.GetLocation();
var diagnostic = Diagnostic.Create(
PropertySelfReferenceRule,
primaryLocation,
additionalLocations,
new string[] { propertyDeclaration.Identifier.ValueText, "set" }
);
context.ReportDiagnostic(diagnostic);
// The symbol corresponding to the property
var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyDeclaration);
}
}
// If there are *no* accessors, we might have the lambda shorthand to check
if (!accessorDeclarations.Any())
// Find all identifiers that exist within the property declaration
var identifiers = propertyDeclaration.DescendantNodes()
.OfType<IdentifierNameSyntax>();
foreach (var identifier in identifiers)
{
var lambdaDec = propertyDeclaration.DescendantNodes().OfType<ArrowExpressionClauseSyntax>();
if (lambdaDec.Any())
// Get the symbol that corresponds to the identifier
var symbolInfo = context.SemanticModel.GetSymbolInfo(identifier);
// Check that the identifier symbol is not the property symbol
if (SymbolEqualityComparer.Default.Equals(propertySymbol, symbolInfo.Symbol))
{
var selfReference = lambdaDec.First().DescendantNodes()
.OfType<IdentifierNameSyntax>()
.Where(id => id.Identifier.ValueText == propertyDeclaration.Identifier.ValueText);
if (selfReference.Any())
// Determine if the self-reference occured in a getter or setter
AccessorDeclarationSyntax accessor = identifier.Ancestors()
.OfType<AccessorDeclarationSyntax>()
.FirstOrDefault();
if (accessor != null && accessor.IsKind(SyntaxKind.SetAccessorDeclaration))
{
// Create the diagnostic
var primaryLocation = propertyDeclaration.Identifier.GetLocation();
var additionalLocations = new Location[] { identifier.GetLocation() };
var diagnostic = Diagnostic.Create(
PropertySelfReferenceRule,
primaryLocation,
additionalLocations,
new string[] { propertyDeclaration.Identifier.ValueText, "set" }
);
context.ReportDiagnostic(diagnostic);
}
else
{
// Create the diagnostic
var primaryLocation = propertyDeclaration.Identifier.GetLocation();
var additionalLocations = from sr in selfReference select sr.Identifier.GetLocation();
var additionalLocations = new Location[] { identifier.GetLocation() };
var diagnostic = Diagnostic.Create(
PropertySelfReferenceRule,
primaryLocation,
additionalLocations,
new string[] { propertyDeclaration.Identifier.ValueText, "get" }
);
// Report the diagnostic
context.ReportDiagnostic(diagnostic);
}
}
......
......@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeAnalyzers.Test", "CodeA
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProgramVerifierTests", "ProgramVarifierTests\ProgramVerifierTests.csproj", "{5D88D78A-72C1-41BC-A165-D38DDAF29609}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeAnalyzers.Package", "CodeAnalyzers\CodeAnalyzers.Package\CodeAnalyzers.Package.csproj", "{A0C86E34-363D-4987-8771-8B6896C632A9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -71,6 +73,14 @@ Global
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|Any CPU.Build.0 = Release|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|x64.ActiveCfg = Release|Any CPU
{5D88D78A-72C1-41BC-A165-D38DDAF29609}.Release|x64.Build.0 = Release|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Debug|x64.ActiveCfg = Debug|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Debug|x64.Build.0 = Debug|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Release|Any CPU.Build.0 = Release|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Release|x64.ActiveCfg = Release|Any CPU
{A0C86E34-363D-4987-8771-8B6896C632A9}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment