-
-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #284 from reduckted/feature/options-page-analyzer
Code analyzer to check the type passed to ProvideOptionDialogPageAttribute and ProvideProfileAttribute
- Loading branch information
Showing
18 changed files
with
619 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
...it.Analyzers/Analyzers/CVST003UseCorrectTypeInProvideOptionDialogPageAttributeAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class CVST003UseCorrectTypeInProvideOptionDialogPageAttributeAnalyzer : IncorrectProvidedTypeAnalyzerBase | ||
{ | ||
internal const string DiagnosticId = Diagnostics.UseCorrectTypeInProvideOptionDialogPageAttribute; | ||
|
||
private static readonly DiagnosticDescriptor _rule = new( | ||
DiagnosticId, | ||
GetLocalizableString(nameof(Resources.CVST003_Title)), | ||
GetLocalizableString(nameof(Resources.IncorrectProvidedType_MessageFormat)), | ||
"Usage", | ||
DiagnosticSeverity.Error, | ||
isEnabledByDefault: true); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(_rule); | ||
|
||
protected override string AttributeTypeName => KnownTypeNames.ProvideOptionDialogPageAttribute; | ||
|
||
protected override string ExpectedTypeName => KnownTypeNames.DialogPage; | ||
|
||
protected override DiagnosticDescriptor Descriptor => _rule; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...yzers/Analyzers/CVST003UseCorrectTypeInProvideOptionDialogPageAttributeCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System.Composition; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
|
||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CVST003UseCorrectTypeInProvideOptionDialogPageAttributeCodeFixProvider))] | ||
[Shared] | ||
public class CVST003UseCorrectTypeInProvideOptionDialogPageAttributeCodeFixProvider : IncorrectProvidedTypeCodeFixProviderBase | ||
{ | ||
protected override string FixableDiagnosticId => CVST003UseCorrectTypeInProvideOptionDialogPageAttributeAnalyzer.DiagnosticId; | ||
|
||
protected override string ExpectedTypeName => KnownTypeNames.DialogPage; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...dio.Toolkit.Analyzers/Analyzers/CVST004UseCorrectTypeInProvideProfileAttributeAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class CVST004UseCorrectTypeInProvideProfileAttributeAnalyzer : IncorrectProvidedTypeAnalyzerBase | ||
{ | ||
internal const string DiagnosticId = Diagnostics.UseCorrectTypeInProvideProfileAttribute; | ||
|
||
private static readonly DiagnosticDescriptor _rule = new( | ||
DiagnosticId, | ||
GetLocalizableString(nameof(Resources.CVST004_Title)), | ||
GetLocalizableString(nameof(Resources.IncorrectProvidedType_MessageFormat)), | ||
"Usage", | ||
DiagnosticSeverity.Error, | ||
isEnabledByDefault: true); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(_rule); | ||
|
||
protected override string AttributeTypeName => KnownTypeNames.ProvideProfileAttribute; | ||
|
||
protected override string ExpectedTypeName => KnownTypeNames.IProfileManager; | ||
|
||
protected override DiagnosticDescriptor Descriptor => _rule; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...lkit.Analyzers/Analyzers/CVST004UseCorrectTypeInProvideProfileAttributeCodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using System.Composition; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
|
||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CVST004UseCorrectTypeInProvideProfileAttributeCodeFixProvider))] | ||
[Shared] | ||
public class CVST004UseCorrectTypeInProvideProfileAttributeCodeFixProvider : IncorrectProvidedTypeCodeFixProviderBase | ||
{ | ||
protected override string FixableDiagnosticId => CVST004UseCorrectTypeInProvideProfileAttributeAnalyzer.DiagnosticId; | ||
|
||
protected override string ExpectedTypeName => KnownTypeNames.IProfileManager; | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
...s/Community.VisualStudio.Toolkit.Analyzers/Analyzers/IncorrectProvidedTypeAnalyzerBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
public abstract class IncorrectProvidedTypeAnalyzerBase : AnalyzerBase | ||
{ | ||
protected abstract string AttributeTypeName { get; } | ||
|
||
protected abstract string ExpectedTypeName { get; } | ||
|
||
protected abstract DiagnosticDescriptor Descriptor { get; } | ||
|
||
public sealed override void Initialize(AnalysisContext context) | ||
{ | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
context.EnableConcurrentExecution(); | ||
|
||
context.RegisterCompilationStartAction(OnCompilationStart); | ||
} | ||
|
||
private void OnCompilationStart(CompilationStartAnalysisContext context) | ||
{ | ||
INamedTypeSymbol? attributeType = context.Compilation.GetTypeByMetadataName(AttributeTypeName); | ||
INamedTypeSymbol? expectedType = context.Compilation.GetTypeByMetadataName(ExpectedTypeName); | ||
|
||
if (attributeType is not null && expectedType is not null) | ||
{ | ||
context.RegisterSyntaxNodeAction( | ||
(c) => AnalyzeAttribute(c, attributeType, expectedType), | ||
SyntaxKind.Attribute | ||
); | ||
} | ||
} | ||
|
||
private void AnalyzeAttribute(SyntaxNodeAnalysisContext context, INamedTypeSymbol attributeType, INamedTypeSymbol expectedType) | ||
{ | ||
AttributeSyntax attribute = (AttributeSyntax)context.Node; | ||
if (context.SemanticModel.GetTypeInfo(attribute).Type?.IsAssignableTo(attributeType) == true) | ||
{ | ||
// The type that is provided is always the first argument to the attribute's constructor. | ||
AttributeArgumentSyntax? typeArgument = attribute.ArgumentList?.Arguments.FirstOrDefault(); | ||
if (typeArgument?.Expression is TypeOfExpressionSyntax typeOfExpression) | ||
{ | ||
ISymbol? argumentSymbol = context.SemanticModel.GetSymbolInfo(typeOfExpression.Type).Symbol; | ||
if (argumentSymbol is ITypeSymbol argumentTypeSymbol) | ||
{ | ||
if (!argumentTypeSymbol.IsAssignableTo(expectedType)) | ||
{ | ||
context.ReportDiagnostic( | ||
Diagnostic.Create( | ||
Descriptor, | ||
typeOfExpression.Type.GetLocation(), | ||
typeOfExpression.Type.GetText(), | ||
ExpectedTypeName | ||
) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
...nity.VisualStudio.Toolkit.Analyzers/Analyzers/IncorrectProvidedTypeCodeFixProviderBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Editing; | ||
|
||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
public abstract class IncorrectProvidedTypeCodeFixProviderBase : CodeFixProviderBase | ||
{ | ||
private ImmutableArray<string>? _fixableDiagnosticIds; | ||
|
||
protected abstract string ExpectedTypeName { get; } | ||
|
||
protected abstract string FixableDiagnosticId { get; } | ||
|
||
public sealed override ImmutableArray<string> FixableDiagnosticIds => _fixableDiagnosticIds ??= ImmutableArray.Create(FixableDiagnosticId); | ||
|
||
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
||
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
|
||
foreach (Diagnostic diagnostic in context.Diagnostics) | ||
{ | ||
// The convention used by the toolkit for some types is to define a container | ||
// class for implementations of the provided types. For example, you might | ||
// define a container class for all of the DialogPage implementations. | ||
// | ||
// So, find all of the nested types that inherit from | ||
// the expected type and use their name as a suggested fix. | ||
TypeSyntax? actualTypeSyntax = root.FindNode(diagnostic.Location.SourceSpan) as TypeSyntax; | ||
if (actualTypeSyntax is not null) | ||
{ | ||
SemanticModel semanticModel = await context.Document.GetSemanticModelAsync(); | ||
INamedTypeSymbol? expectedType = semanticModel.Compilation.GetTypeByMetadataName(ExpectedTypeName); | ||
if (expectedType is not null) | ||
{ | ||
if (semanticModel.GetSymbolInfo(actualTypeSyntax).Symbol is ITypeSymbol argumentType) | ||
{ | ||
IEnumerable<INamedTypeSymbol> nestedTypes = argumentType | ||
.GetTypeMembers() | ||
.Where((x) => x.IsAssignableTo(expectedType)) | ||
.OrderBy((x) => x.Name); | ||
|
||
foreach (INamedTypeSymbol nestedType in nestedTypes) | ||
{ | ||
string title = string.Format(Resources.IncorrectProvidedType_CodeFix, $"{argumentType.Name}.{nestedType.Name}"); | ||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
title, | ||
c => ChangeTypeNameAsync(context.Document, actualTypeSyntax, nestedType, c), | ||
equivalenceKey: $"{FixableDiagnosticId}:{title}" | ||
), | ||
diagnostic | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static async Task<Document> ChangeTypeNameAsync(Document document, SyntaxNode nodeToChange, INamedTypeSymbol newType, CancellationToken cancellationToken) | ||
{ | ||
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken); | ||
SyntaxEditor editor = new(root, document.Project.Solution.Workspace); | ||
SyntaxGenerator generator = editor.Generator; | ||
|
||
editor.ReplaceNode(nodeToChange, generator.NameExpression(newType)); | ||
|
||
return document.WithSyntaxRoot(editor.GetChangedRoot()); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
src/analyzers/Community.VisualStudio.Toolkit.Analyzers/KnownTypeNames.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace Community.VisualStudio.Toolkit.Analyzers | ||
{ | ||
internal static class KnownTypeNames | ||
{ | ||
public const string ComVisibleAttribute = "System.Runtime.InteropServices.ComVisibleAttribute"; | ||
public const string DialogPage = "Microsoft.VisualStudio.Shell.DialogPage"; | ||
public const string IProfileManager = "Microsoft.VisualStudio.Shell.IProfileManager"; | ||
public const string ProvideOptionDialogPageAttribute = "Microsoft.VisualStudio.Shell.ProvideOptionDialogPageAttribute"; | ||
public const string ProvideProfileAttribute = "Microsoft.VisualStudio.Shell.ProvideProfileAttribute"; | ||
} | ||
} |
Oops, something went wrong.