Add analyzer/code fix for updating or removing old attribute types (#641)

This replaces the AllowHtmlAttribute analyzer and code fixer with a more general purpose analzyers/code fix provider that will update or remove any attribute types listed in typemaps.

Fixes #493
Fixes #499
Fixes #501
Fixes #504
Fixes #638
This commit is contained in:
Mike Rousos 2021-06-23 12:12:32 -04:00 коммит произвёл GitHub
Родитель 0bfbdf260b
Коммит 723cc4b64a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 698 добавлений и 307 удалений

Просмотреть файл

@ -9,10 +9,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- New project readiness checks evaluate if the project contains unsupported technologies to increase awareness before users invest significant time trying to upgrade. [#617](https://github.com/dotnet/upgrade-assistant/pull/617)
- Usage telemetry has been added to help guide product development. See [https://aka.ms/upgrade-assistant-telemetry](https://aka.ms/upgrade-assistant-telemetry) for details [#644](https://github.com/dotnet/upgrade-assistant/pull/644).
- Command line option to pass options through in the form of `--option KEY=Value` [#651](https://github.com/dotnet/upgrade-assistant/pull/651)
- Added an analyzer and code fix provider to remove unnecessary attributes and upgrade changed attributes (based on type mappings in registered typemap files) [#641](https://github.com/dotnet/upgrade-assistant/pull/641)
### Fixed
- Updated `HttpContext.Current` analyzer to more correctly identify uses of `HttpContext.Current` that need replaced [#628](https://github.com/dotnet/upgrade-assistant/pull/628).
- The Upgrade Assistant analzyer package no longer adds a WebTypeReplacements.typemap file to projects it's added to (more precisely, the file is present and available for analyzers to use but isn't visible in the solution explorer anymore) [#632](https://github.com/dotnet/upgrade-assistant/pull/632).
- The Upgrade Assistant analyzer package no longer adds a WebTypeReplacements.typemap file to projects it's added to (more precisely, the file is present and available for analyzers to use but isn't visible in the solution explorer anymore) [#632](https://github.com/dotnet/upgrade-assistant/pull/632).
## Version 0.2.231403 - 2021-06-14 ([Link](https://www.nuget.org/packages/upgrade-assistant/0.2.231403))

Просмотреть файл

@ -9,7 +9,7 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<ItemGroup Condition="'$(Language)'=='C#'">
<Compile Include="$(MSBuildThisFileDirectory)/shared/**/*.cs" />
</ItemGroup>

Просмотреть файл

@ -47,7 +47,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
{
// Add source analyzers and code fix providers (note that order doesn't matter as they're run alphabetically)
// Analyzers
services.AddTransient<DiagnosticAnalyzer, AllowHtmlAttributeAnalyzer>();
services.AddTransient<DiagnosticAnalyzer, AttributeUpgradeAnalyzer>();
services.AddTransient<DiagnosticAnalyzer, BinaryFormatterUnsafeDeserializeAnalyzer>();
services.AddTransient<DiagnosticAnalyzer, HtmlHelperAnalyzer>();
services.AddTransient<DiagnosticAnalyzer, HttpContextCurrentAnalyzer>();
@ -57,7 +57,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
services.AddTransient<DiagnosticAnalyzer, UsingSystemWebAnalyzer>();
// Code fix providers
services.AddTransient<CodeFixProvider, AllowHtmlAttributeCodeFixer>();
services.AddTransient<CodeFixProvider, AttributeUpgradeCodeFixer>();
services.AddTransient<CodeFixProvider, BinaryFormatterUnsafeDeserializeCodeFixer>();
services.AddTransient<CodeFixProvider, HtmlHelperCodeFixer>();
services.AddTransient<CodeFixProvider, HttpContextCurrentCodeFixer>();

Просмотреть файл

@ -3,7 +3,10 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Common;
using CS = Microsoft.CodeAnalysis.CSharp;
@ -151,31 +154,6 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
|| VisualBasicExtensions.IsKind(trivia, VB.SyntaxKind.WhitespaceTrivia);
}
/// <summary>
/// Determines if a syntax node includes a using or import statement for a given namespace.
/// Will not return true if the node's children include the specified using/import but the node itself does not.
/// </summary>
/// <param name="node">The node to analyze.</param>
/// <param name="namespaceName">The namespace name to check for.</param>
/// <returns>True if the node has a direct import or using statement for the given namespace. False otherwise.</returns>
public static bool HasUsingStatement(this SyntaxNode node, string namespaceName)
{
if (node is null)
{
throw new ArgumentNullException(nameof(node));
}
// Descend only into VB import statements
var nodes = node.DescendantNodesAndSelf(n => VisualBasicExtensions.IsKind(n, VB.SyntaxKind.ImportsStatement), false);
var children = node.ChildNodes();
var usings = children.OfType<CSSyntax.UsingDirectiveSyntax>().Select(u => u.Name.ToString())
.Concat(children.OfType<VBSyntax.SimpleImportsClauseSyntax>().Select(i => i.Name.ToString()))
.Concat(children.OfType<VBSyntax.ImportsStatementSyntax>().SelectMany(i => i.ImportsClauses.OfType<VBSyntax.SimpleImportsClauseSyntax>().Select(i => i.Name.ToString())));
return usings.Any(n => n.Equals(namespaceName, StringComparison.Ordinal));
}
/// <summary>
/// Determines if a syntax node is in a scope that includes a using or import statement for a given namespace.
/// </summary>
@ -189,28 +167,104 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
throw new ArgumentNullException(nameof(node));
}
return node.AncestorsAndSelf().Any(n => n.HasUsingStatement(namespaceName));
return node.AncestorsAndSelf().Any(n => IncludesImport(n, namespaceName));
/// <summary>
/// Determines if a syntax node includes a using or import statement for a given namespace.
/// Will not return true if the node's children include the specified using/import but the node itself does not.
/// </summary>
/// <param name="node">The node to analyze.</param>
/// <param name="namespaceName">The namespace name to check for.</param>
/// <returns>True if the node has a direct import or using statement for the given namespace. False otherwise.</returns>
static bool IncludesImport(SyntaxNode node, string namespaceName)
{
if (node is null)
{
throw new ArgumentNullException(nameof(node));
}
if (node is CSSyntax.CompilationUnitSyntax || node is VBSyntax.CompilationUnitSyntax)
{
return node.RootIncludesImport(namespaceName);
}
// Descend only into VB import statements
var nodes = node.DescendantNodesAndSelf(n => VisualBasicExtensions.IsKind(n, VB.SyntaxKind.ImportsStatement), false);
var children = node.ChildNodes();
var usings = children.OfType<CSSyntax.UsingDirectiveSyntax>().Select(u => u.Name.ToString())
.Concat(children.OfType<VBSyntax.SimpleImportsClauseSyntax>().Select(i => i.Name.ToString()))
.Concat(children.OfType<VBSyntax.ImportsStatementSyntax>().SelectMany(i => i.ImportsClauses.OfType<VBSyntax.SimpleImportsClauseSyntax>().Select(i => i.Name.ToString())));
return usings.Any(n => n.Equals(namespaceName, node.GetStringComparison()));
}
}
/// <summary>
/// Adds a using directive for a given namespace to the document root only if the directive is not already present.
/// </summary>
/// <param name="documentRoot">The document to add the directive to.</param>
/// <param name="namespaceName">The namespace to reference with the using directive.</param>
/// <returns>An updated document root with the specific using directive.</returns>
public static CSSyntax.CompilationUnitSyntax AddUsingIfMissing(this CSSyntax.CompilationUnitSyntax documentRoot, string namespaceName)
public static CSSyntax.CompilationUnitSyntax AddImportIfMissing(this CSSyntax.CompilationUnitSyntax documentRoot, string namespaceName)
{
if (documentRoot is null)
{
throw new ArgumentNullException(nameof(documentRoot));
}
// TODO - remove this helper and use ImportAdder instead.
var anyUsings = documentRoot.Usings.Any(u => u.Name.ToString().Equals(namespaceName, StringComparison.Ordinal));
var usingDirective = CS.SyntaxFactory.UsingDirective(CS.SyntaxFactory.ParseName(namespaceName).WithLeadingTrivia(CS.SyntaxFactory.Whitespace(" ")));
var result = anyUsings ? documentRoot : documentRoot.AddUsings(usingDirective);
if (string.IsNullOrEmpty(namespaceName))
{
throw new ArgumentException($"'{nameof(namespaceName)}' cannot be null or empty.", nameof(namespaceName));
}
return result;
if (documentRoot.RootIncludesImport(namespaceName))
{
return documentRoot;
}
var usingDirective = CS.SyntaxFactory.UsingDirective(CS.SyntaxFactory.ParseName(namespaceName).WithLeadingTrivia(CS.SyntaxFactory.Whitespace(" ")))
.WithTrailingTrivia(CS.SyntaxFactory.CarriageReturnLineFeed);
return documentRoot.AddUsings(usingDirective);
}
public static VBSyntax.CompilationUnitSyntax AddImportIfMissing(this VBSyntax.CompilationUnitSyntax documentRoot, string namespaceName)
{
if (documentRoot is null)
{
throw new ArgumentNullException(nameof(documentRoot));
}
if (string.IsNullOrEmpty(namespaceName))
{
throw new ArgumentException($"'{nameof(namespaceName)}' cannot be null or empty.", nameof(namespaceName));
}
if (documentRoot.RootIncludesImport(namespaceName))
{
return documentRoot;
}
var importsStatement = VB.SyntaxFactory.ImportsStatement(VB.SyntaxFactory.SingletonSeparatedList<VBSyntax.ImportsClauseSyntax>(VB.SyntaxFactory.SimpleImportsClause(VB.SyntaxFactory.ParseName(namespaceName).WithLeadingTrivia(VB.SyntaxFactory.Whitespace(" ")))))
.WithTrailingTrivia(VB.SyntaxFactory.CarriageReturnLineFeed);
return documentRoot.AddImports(importsStatement);
}
/// <summary>
/// Determines if a document root element contains a using/import statement for a given namespace.
/// </summary>
/// <param name="documentRoot">The document root to analyze.</param>
/// <param name="namespaceName">The namespace name to look for an import for.</param>
/// <returns>True if the documentRoot is a root node and contains a top-level using/import statement for the specified namespace name.</returns>
private static bool RootIncludesImport(this SyntaxNode documentRoot, string namespaceName)
{
if (documentRoot is null)
{
throw new ArgumentNullException(nameof(documentRoot));
}
return documentRoot switch
{
CSSyntax.CompilationUnitSyntax csRoot =>
csRoot.Usings.Any(u => u.Name.ToString().Equals(namespaceName, StringComparison.Ordinal)),
VBSyntax.CompilationUnitSyntax vbRoot =>
vbRoot.Imports.SelectMany(i => i.ImportsClauses.OfType<VBSyntax.SimpleImportsClauseSyntax>()).Any(i => i.Name.ToString().Equals(namespaceName, StringComparison.OrdinalIgnoreCase)),
_ => false
};
}
public static SyntaxNode? GetInvocationExpression(this SyntaxNode callerNode)
@ -226,5 +280,21 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
throw new NotImplementedException(Resources.UnknownLanguage);
}
public static StringComparison GetStringComparison(this SyntaxNode? node)
=> node?.Language switch
{
LanguageNames.CSharp => StringComparison.Ordinal,
LanguageNames.VisualBasic => StringComparison.OrdinalIgnoreCase,
_ => throw new NotImplementedException(Resources.UnknownLanguage),
};
public static StringComparer GetStringComparer(this SyntaxNode? node)
=> node?.Language switch
{
LanguageNames.CSharp => StringComparer.Ordinal,
LanguageNames.VisualBasic => StringComparer.OrdinalIgnoreCase,
_ => throw new NotImplementedException(Resources.UnknownLanguage),
};
}
}

Просмотреть файл

@ -12,13 +12,18 @@ System.Web.Mvc.IActionFilter Microsoft.AspNetCore.Mvc.Filters.IActionFilter
System.Web.WebPages.HelperResult Microsoft.AspNetCore.Mvc.Razor.HelperResult
System.Web.HtmlString Microsoft.AspNetCore.Html.HtmlString
System.Web.IHtmlString Microsoft.AspNetCore.Html.HtmlString
System.Web.Mvc.AuthorizeAttribute Microsoft.AspNetCore.Authorization.AuthorizeAttribute
System.Web.Mvc.BindAttribute Microsoft.AspNetCore.Mvc.BindAttribute
System.Web.Mvc.MvcHtmlString Microsoft.AspNetCore.Html.HtmlString
System.Web.Mvc.ActionResult Microsoft.AspNetCore.Mvc.ActionResult
System.Web.Mvc.AllowHtmlAttribute
System.Web.Mvc.ContentResult Microsoft.AspNetCore.Mvc.ContentResult
System.Web.Mvc.FileResult Microsoft.AspNetCore.Mvc.FileResult
System.Web.Mvc.HttpNotFoundResult Microsoft.AspNetCore.Mvc.NotFoundResult
System.Web.Mvc.HttpStatusCodeResult Microsoft.AspNetCore.Mvc.StatusCodeResult
System.Web.Mvc.HttpUnauthorizedResult Microsoft.AspNetCore.Mvc.UnauthorizedResult
System.Web.Mvc.OutputCacheAttribute Microsoft.AspNetCore.Mvc.ResponseCacheAttribute
System.Web.Mvc.RedirectResult Microsoft.AspNetCore.Mvc.RedirectResult
System.Web.Mvc.PartialViewResult Microsoft.AspNetCore.Mvc.PartialViewResult
System.Web.Mvc.ValidateInputAttribute
System.Web.Mvc.ViewResult Microsoft.AspNetCore.Mvc.ViewResult

Просмотреть файл

@ -1,75 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers
{
[ApplicableComponents(ProjectComponents.AspNetCore)]
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class AllowHtmlAttributeAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "UA0010";
private const string Category = "Upgrade";
private const string AllowHtmlAttributeName = "System.Web.Mvc.AllowHtmlAttribute";
private static readonly string[] DisallowedNames = new[] { "AllowHtml", "AllowHtmlAttribute" };
public override void Initialize(AnalysisContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterSyntaxNodeAction(AnalyzeAttribute, SyntaxKind.Attribute);
}
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AllowHtmlAttributeTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AllowHtmlAttributeMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AllowHtmlAttributeDescription), Resources.ResourceManager, typeof(Resources));
private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
private void AnalyzeAttribute(SyntaxNodeAnalysisContext context)
{
var attribute = (AttributeSyntax)context.Node;
// Get the attribute's simple name
var name = attribute.Name.ToString();
var simpleNameStart = name.LastIndexOf('.');
if (simpleNameStart > 0)
{
name = name.Substring(simpleNameStart + 1);
}
// If the attribute isn't [AllowHtml], bail out
if (!DisallowedNames.Contains(name, StringComparer.Ordinal))
{
return;
}
// If the identifier resolves to an actual symbol that isn't System.Web.Mvc.AllowHtmlAttribute, then bail out
var attrNameSymbol = context.SemanticModel.GetTypeInfo(attribute.Name).Type;
if (attrNameSymbol is INamedTypeSymbol symbol
&& attrNameSymbol is not IErrorTypeSymbol
&& !symbol.ToDisplayString(NullableFlowState.NotNull).Equals(AllowHtmlAttributeName, StringComparison.Ordinal))
{
return;
}
var diagnostic = Diagnostic.Create(Rule, attribute.GetLocation(), attribute.Name.ToString());
context.ReportDiagnostic(diagnostic);
}
}
}

Просмотреть файл

@ -0,0 +1,110 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using CS = Microsoft.CodeAnalysis.CSharp;
using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax;
using VB = Microsoft.CodeAnalysis.VisualBasic;
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers
{
[ApplicableComponents(ProjectComponents.AspNetCore)]
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class AttributeUpgradeAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "UA0010";
private const string Category = "Upgrade";
private const string AttributeSuffix = "Attribute";
/// <summary>
/// Key name for the diagnostic property containing the full name of the
/// attribute type the code fix provider should use to replace the syntax
/// node identified in the diagnostic.
/// </summary>
public const string NewTypeKey = "NewAttributeType";
public override void Initialize(AnalysisContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.RegisterCompilationStartAction(context =>
{
// Load analyzer configuration defining the attribute types that should be mapped.
var mappings = TypeMapLoader.LoadMappings(context.Options.AdditionalFiles)
.Where(m => m.OldName.EndsWith(AttributeSuffix, StringComparison.Ordinal));
// If attribute type maps are present, register syntax node actions to analyze for those attributes
if (mappings.Any())
{
// Register actions for handling both C# and VB identifiers
context.RegisterSyntaxNodeAction(context => AnalyzeCSharpAttribute(context, mappings), CS.SyntaxKind.Attribute);
context.RegisterSyntaxNodeAction(context => AnalyzeVBAttribute(context, mappings), VB.SyntaxKind.Attribute);
}
});
}
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AttributeUpgradeTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AttributeUpgradeMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AttributeUpgradeDescription), Resources.ResourceManager, typeof(Resources));
private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
private static void AnalyzeCSharpAttribute(SyntaxNodeAnalysisContext context, IEnumerable<TypeMapping> mappings) =>
AnalyzeAttribute(context, mappings, ((CSSyntax.AttributeSyntax)context.Node).Name.ToString());
private static void AnalyzeVBAttribute(SyntaxNodeAnalysisContext context, IEnumerable<TypeMapping> mappings) =>
AnalyzeAttribute(context, mappings, ((VBSyntax.AttributeSyntax)context.Node).Name.ToString());
private static void AnalyzeAttribute(SyntaxNodeAnalysisContext context, IEnumerable<TypeMapping> mappings, string? attributeName)
{
if (attributeName is null)
{
return;
}
// If the attribute name isn't one of the mapped names, bail out
var mapping = mappings.FirstOrDefault(m =>
{
var matcher = NameMatcher.MatchType(m.OldName);
return matcher.MatchesPartiallyQualifiedType(attributeName) || matcher.MatchesPartiallyQualifiedType($"{attributeName}{AttributeSuffix}");
});
if (mapping is null)
{
return;
}
// Attempt to get the symbol for the attribute's type
var attrType = context.SemanticModel.GetTypeInfo(context.Node);
// Bail out if the node corresponds to a symbol that isn't the old type.
if (attrType.Type is INamedTypeSymbol typeSymbol
&& typeSymbol is not IErrorTypeSymbol
&& !typeSymbol.ToDisplayString(NullableFlowState.NotNull).Equals(mapping.OldName, StringComparison.Ordinal))
{
return;
}
// Store the new attribute type that this identifier should be replaced with (or null to remove the attribute)
// for use by the code fix provider.
var properties = ImmutableDictionary.Create<string, string?>().Add(NewTypeKey, mapping.NewName);
// Create and report the diagnostic
var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation(), properties, mapping.OldName, mapping.NewName);
context.ReportDiagnostic(diagnostic);
}
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using Microsoft.CodeAnalysis;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
@ -8,6 +9,8 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
private readonly string[] _typeName;
private readonly string? _memberName;
private ConcurrentDictionary<string, bool> _partialMatchCache;
private NameMatcher(string typeName, string? memberName = null)
{
if (typeName is null)
@ -17,12 +20,47 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default
_typeName = typeName.Split('.');
_memberName = memberName;
_partialMatchCache = new ConcurrentDictionary<string, bool>();
}
public static NameMatcher MatchType(string typeName) => new NameMatcher(typeName);
public static NameMatcher MatchPropertyAccess(string typeName, string memberName) => new(typeName, memberName);
public string TypeName => _typeName[_typeName.Length - 1];
public bool MatchesPartiallyQualifiedType(string partiallyQualifiedTypeName)
{
if (partiallyQualifiedTypeName is null)
{
throw new ArgumentNullException(nameof(partiallyQualifiedTypeName));
}
// If the partial type has been compared against before, return the cached value
// to save the work and string allocations.
return _partialMatchCache.GetOrAdd(partiallyQualifiedTypeName, p =>
{
// If the partial name has not been seen before, split it apart
// and compare the name components one at a time.
var partialName = p.Split('.');
if (partialName.Length > _typeName.Length)
{
return false;
}
for (var i = 1; i <= partialName.Length; i++)
{
if (!partialName[partialName.Length - i].Equals(_typeName[_typeName.Length - i], StringComparison.Ordinal))
{
return false;
}
}
return true;
});
}
public bool Matches(IPropertySymbol property)
{
if (property is null)

Просмотреть файл

@ -61,56 +61,29 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers {
}
/// <summary>
/// Looks up a localized string similar to [AllowHtmlAttribute] should be removed..
/// Looks up a localized string similar to This attribute type is not supported on .NET Core/.NET 5+ and should be replaced with a modern equivalent..
/// </summary>
internal static string AllowHtmlAttributeDescription {
internal static string AttributeUpgradeDescription {
get {
return ResourceManager.GetString("AllowHtmlAttributeDescription", resourceCulture);
return ResourceManager.GetString("AttributeUpgradeDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Attribute &apos;{0}&apos; should be removed.
/// Looks up a localized string similar to Attribute type &apos;{0}&apos; should be replaced with &apos;{1}&apos;.
/// </summary>
internal static string AllowHtmlAttributeMessageFormat {
internal static string AttributeUpgradeMessageFormat {
get {
return ResourceManager.GetString("AllowHtmlAttributeMessageFormat", resourceCulture);
return ResourceManager.GetString("AttributeUpgradeMessageFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [AllowHtmlAttribute] should be removed.
/// Looks up a localized string similar to Attributes should be upgraded.
/// </summary>
internal static string AllowHtmlAttributeTitle {
internal static string AttributeUpgradeTitle {
get {
return ResourceManager.GetString("AllowHtmlAttributeTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &apos;ApiController&apos; should be replaced with &apos;Microsoft.AspNetCore.Mvc.Controller&apos;..
/// </summary>
internal static string ApiControllerDescription {
get {
return ResourceManager.GetString("ApiControllerDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &apos;ApiController&apos; should be replaced with &apos;Microsoft.AspNetCore.Mvc.Controller&apos;.
/// </summary>
internal static string ApiControllerMessageFormat {
get {
return ResourceManager.GetString("ApiControllerMessageFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &apos;ApiController&apos; does not exist.
/// </summary>
internal static string ApiControllerTitle {
get {
return ResourceManager.GetString("ApiControllerTitle", resourceCulture);
return ResourceManager.GetString("AttributeUpgradeTitle", resourceCulture);
}
}
@ -349,7 +322,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers {
}
/// <summary>
/// Looks up a localized string similar to Type should be upgraded.
/// Looks up a localized string similar to Types should be upgraded.
/// </summary>
internal static string TypeUpgradeTitle {
get {

Просмотреть файл

@ -117,23 +117,14 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AllowHtmlAttributeDescription" xml:space="preserve">
<value>[AllowHtmlAttribute] should be removed.</value>
<data name="AttributeUpgradeDescription" xml:space="preserve">
<value>This attribute type is not supported on .NET Core/.NET 5+ and should be replaced with a modern equivalent.</value>
</data>
<data name="AllowHtmlAttributeMessageFormat" xml:space="preserve">
<value>Attribute '{0}' should be removed</value>
<data name="AttributeUpgradeMessageFormat" xml:space="preserve">
<value>Attribute type '{0}' should be replaced with '{1}'</value>
</data>
<data name="AllowHtmlAttributeTitle" xml:space="preserve">
<value>[AllowHtmlAttribute] should be removed</value>
</data>
<data name="ApiControllerDescription" xml:space="preserve">
<value>'ApiController' should be replaced with 'Microsoft.AspNetCore.Mvc.Controller'.</value>
</data>
<data name="ApiControllerMessageFormat" xml:space="preserve">
<value>'ApiController' should be replaced with 'Microsoft.AspNetCore.Mvc.Controller'</value>
</data>
<data name="ApiControllerTitle" xml:space="preserve">
<value>'ApiController' does not exist</value>
<data name="AttributeUpgradeTitle" xml:space="preserve">
<value>Attributes should be upgraded</value>
</data>
<data name="BinaryFormatterUnsafeDeserializeDescription" xml:space="preserve">
<value>'UnsafeDeserialize()' should be replaced with 'Deserialize()'.</value>
@ -214,7 +205,7 @@
<value>Type '{0}' should be replaced with '{1}'</value>
</data>
<data name="TypeUpgradeTitle" xml:space="preserve">
<value>Type should be upgraded</value>
<value>Types should be upgraded</value>
</data>
<data name="UrlHelperDescription" xml:space="preserve">
<value>UrlHelper should be replaced with Microsoft.AspNetCore.Mvc.IUrlHelper or Microsoft.AspNetCore.Mvc.Routing.UrlHelper.</value>

Просмотреть файл

@ -42,6 +42,10 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers
{
yield return new TypeMapping(components[0], components[1]);
}
else if (components.Length == 1)
{
yield return new TypeMapping(components[0], null);
}
}
}
}

Просмотреть файл

@ -7,14 +7,14 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers
{
public string OldName { get; init; }
public string NewName { get; init; }
public string? NewName { get; init; }
public string SimpleName { get; init; }
public TypeMapping(string oldName, string newName)
public TypeMapping(string oldName, string? newName)
{
OldName = oldName ?? throw new System.ArgumentNullException(nameof(oldName));
NewName = newName ?? throw new System.ArgumentNullException(nameof(newName));
NewName = newName;
SimpleName = OldName.LastIndexOf('.') < 0
? OldName
: OldName!.Substring(OldName.LastIndexOf('.') + 1);

Просмотреть файл

@ -88,12 +88,26 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers
private static void AnalyzeCSharpIdentifier(SyntaxNodeAnalysisContext context, IEnumerable<TypeMapping> mappings)
{
var identifier = (CSSyntax.IdentifierNameSyntax)context.Node;
// Replacing attributes is handled in a separate analyzer
if (identifier.Parent is CSSyntax.AttributeSyntax)
{
return;
}
AnalyzeIdentifier(context, mappings, identifier.Identifier.ValueText);
}
private static void AnalyzeVBIdentifier(SyntaxNodeAnalysisContext context, IEnumerable<TypeMapping> mappings)
{
var identifier = (VBSyntax.IdentifierNameSyntax)context.Node;
// Replacing attributes is handled in a separate analyzer
if (identifier.Parent is VBSyntax.AttributeSyntax)
{
return;
}
AnalyzeIdentifier(context, mappings, identifier.Identifier.ValueText);
}
@ -113,6 +127,12 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers
return;
}
// This analyzer requires a new type
if (mapping.NewName is null)
{
return;
}
// If the identifier resolves to an actual symbol that isn't the old identifier, bail out
// Also refrain from adding a diagnostic if the symbol is ambiguous but a candidate symbol matches the
// new type. This is useful in cases where the type is referenced as a simple name and the new namespace

Просмотреть файл

@ -1,78 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
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;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes
{
[ApplicableComponents(ProjectComponents.AspNetCore)]
[ExportCodeFixProvider(LanguageNames.CSharp, Name = "UA0010 CodeFix Provider")]
public class AllowHtmlAttributeCodeFixer : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AllowHtmlAttributeAnalyzer.DiagnosticId);
public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root is null)
{
return;
}
var node = root.FindNode(context.Span);
if (node is null)
{
return;
}
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
CodeFixResources.AllowHtmlAttributeTitle,
cancellationToken => RemoveNodeAsync(context.Document, node, cancellationToken),
nameof(CodeFixResources.AllowHtmlAttributeTitle)),
context.Diagnostics);
}
private static async Task<Document> RemoveNodeAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
// Remove the attribute or, if it's the only attribute in the attribute list, remove the attribute list
if (node.Parent is AttributeListSyntax list && list.Attributes.Count == 1)
{
// We want to remove trivia with the attribute list, but we want to *keep* leading end of line trivia
// so that we don't remove blank lines before the property that had the attribute list on it.
// To do that, we first remove any leading trivial except new lines and then remove the node keeping
// leading trivia.
var trimmedList = list.WithLeadingTrivia(list.GetLeadingTrivia().Where(t => t.IsKind(SyntaxKind.EndOfLineTrivia)));
editor.ReplaceNode(list, trimmedList);
editor.RemoveNode(trimmedList, SyntaxRemoveOptions.KeepLeadingTrivia);
}
else
{
editor.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
}
return editor.GetChangedDocument();
}
}
}

Просмотреть файл

@ -0,0 +1,139 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
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.Editing;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers;
using CS = Microsoft.CodeAnalysis.CSharp;
using CSSyntax = Microsoft.CodeAnalysis.CSharp.Syntax;
using VB = Microsoft.CodeAnalysis.VisualBasic;
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes
{
[ApplicableComponents(ProjectComponents.AspNetCore)]
[ExportCodeFixProvider(LanguageNames.CSharp, Name = "UA0010 CodeFix Provider")]
public class AttributeUpgradeCodeFixer : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AttributeUpgradeAnalyzer.DiagnosticId);
public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root is null)
{
return;
}
var node = root.FindNode(context.Span);
if (node is null)
{
return;
}
// Get the diagnostic to be fixed (the new attribute type will be a property of the diagnostic)
var diagnostic = context.Diagnostics.FirstOrDefault();
if (diagnostic is null)
{
return;
}
var newType = diagnostic.Properties.GetValueOrDefault(AttributeUpgradeAnalyzer.NewTypeKey);
if (newType is null)
{
// Register a code action that will remove the attribute.
context.RegisterCodeFix(
CodeAction.Create(
CodeFixResources.AttributeRemoveTitle,
cancellationToken => RemoveAttributeAsync(context.Document, node, cancellationToken),
nameof(CodeFixResources.AttributeRemoveTitle)),
context.Diagnostics);
}
else
{
// Register a code action that will replace the attribute.
context.RegisterCodeFix(
CodeAction.Create(
CodeFixResources.AttributeRemoveTitle,
cancellationToken => ReplaceAttributeAsync(context.Document, node, newType, cancellationToken),
nameof(CodeFixResources.AttributeRemoveTitle)),
context.Diagnostics);
}
}
private static async Task<Document> RemoveAttributeAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
// Remove the attribute or, if it's the only attribute in the attribute list, remove the attribute list
if ((node.Parent is CSSyntax.AttributeListSyntax csList && csList.Attributes.Count == 1)
|| (node.Parent is VBSyntax.AttributeListSyntax vbList && vbList.Attributes.Count == 1))
{
// We want to remove trivia with the attribute list, but we want to *keep* leading end of line trivia
// so that we don't remove blank lines before the property that had the attribute list on it.
// To do that, we first remove any leading trivia except new lines and then remove the node keeping
// leading trivia.
var trimmedList = node.Parent!.WithLeadingTrivia(node.Parent!.GetLeadingTrivia().Where(t => t.IsKind(CS.SyntaxKind.EndOfLineTrivia) || t.IsKind(VB.SyntaxKind.EndOfLineTrivia)));
editor.ReplaceNode(node.Parent, trimmedList);
editor.RemoveNode(trimmedList, SyntaxRemoveOptions.KeepLeadingTrivia);
}
else
{
editor.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia);
}
return editor.GetChangedDocument();
}
private static async Task<Document> ReplaceAttributeAsync(Document document, SyntaxNode node, string attributeType, CancellationToken cancellationToken)
{
var generator = SyntaxGenerator.GetGenerator(document);
// Create an updated Attribute node
var updatedNode = node switch
{
CSSyntax.AttributeSyntax csAttribute => (SyntaxNode)csAttribute.WithName((CSSyntax.NameSyntax)QualifiedNameBuilder.BuildQualifiedNameSyntax(generator, attributeType)),
VBSyntax.AttributeSyntax vbAttribute => vbAttribute.WithName((VBSyntax.NameSyntax)QualifiedNameBuilder.BuildQualifiedNameSyntax(generator, attributeType)),
_ => throw new InvalidOperationException($"Unexpected syntax node type: {node.GetType().FullName}")
};
updatedNode = updatedNode.WithAdditionalAnnotations(Simplifier.Annotation, Simplifier.AddImportsAnnotation);
// Get the document root
var documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
if (documentRoot is null)
{
return document;
}
// Replace the node
var updatedRoot = documentRoot.ReplaceNode(node, updatedNode);
var updatedDocument = document.WithSyntaxRoot(updatedRoot);
// Add using declaration if needed
updatedDocument = await ImportAdder.AddImportsAsync(updatedDocument, Simplifier.AddImportsAnnotation, null, cancellationToken).ConfigureAwait(false);
// Simplify the call, if possible
updatedDocument = await Simplifier.ReduceAsync(updatedDocument, Simplifier.Annotation, null, cancellationToken).ConfigureAwait(false);
return updatedDocument;
}
}
}

Просмотреть файл

@ -60,15 +60,6 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes {
}
}
/// <summary>
/// Looks up a localized string similar to Remove [AllowHtml] attribute.
/// </summary>
internal static string AllowHtmlAttributeTitle {
get {
return ResourceManager.GetString("AllowHtmlAttributeTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Replace with Microsoft.AspNetCore.Mvc.Controller.
/// </summary>
@ -78,6 +69,24 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes {
}
}
/// <summary>
/// Looks up a localized string similar to Remove attribute.
/// </summary>
internal static string AttributeRemoveTitle {
get {
return ResourceManager.GetString("AttributeRemoveTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Replace attribute.
/// </summary>
internal static string AttributeUpgradeTitle {
get {
return ResourceManager.GetString("AttributeUpgradeTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Replace with BinaryFormatter.Deserialize.
/// </summary>

Просмотреть файл

@ -117,8 +117,8 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AllowHtmlAttributeTitle" xml:space="preserve">
<value>Remove [AllowHtml] attribute</value>
<data name="AttributeUpgradeTitle" xml:space="preserve">
<value>Replace attribute</value>
</data>
<data name="ApiControllerTitle" xml:space="preserve">
<value>Replace with Microsoft.AspNetCore.Mvc.Controller</value>
@ -163,4 +163,7 @@
<value>Remove using directive</value>
<comment>The title of the code fix.</comment>
</data>
<data name="AttributeRemoveTitle" xml:space="preserve">
<value>Remove attribute</value>
</data>
</root>

Просмотреть файл

@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@ -84,17 +85,22 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes
// Update the HttpContext.Current reference
var replacementSyntax = ParseExpression($"{httpContextHelperClass.Name}.Current")
.WithTriviaFrom(node);
.WithTriviaFrom(node)
.WithAdditionalAnnotations(Simplifier.Annotation);
docRoot = docRoot.ReplaceNode(node, replacementSyntax);
// Add a using statement, if necessary
// This must be done after the diagnostic node is replaced since the node won't be the same
// after updating using statements.
docRoot = docRoot.AddUsingIfMissing(httpContextHelperClass.ContainingNamespace.ToString())!;
// Add an import to access the helper class, if needed
if (httpContextHelperClass.ContainingNamespace is not null)
{
docRoot = docRoot.AddImportIfMissing(httpContextHelperClass.ContainingNamespace.ToString());
}
docEditor.ReplaceNode(docEditor.OriginalRoot, docRoot);
var updatedDocument = docEditor.GetChangedDocument();
updatedDocument = await Simplifier.ReduceAsync(updatedDocument, Simplifier.Annotation, null, cancellationToken).ConfigureAwait(false);
return slnEditor.GetChangedSolution();
return slnEditor.GetChangedSolution()
.WithDocumentText(updatedDocument.Id, await updatedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false));
}
private static async Task<INamedTypeSymbol?> GetHttpContextHelperClassAsync(Project project)

Просмотреть файл

@ -19,7 +19,6 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes
[ExportCodeFixProvider(LanguageNames.CSharp, Name = "UA0006 CodeFix Provider")]
public class HttpContextIsDebuggingEnabledCodeFixer : CodeFixProvider
{
private const string DiagnosticsNamespace = "System.Diagnostics";
private const string DebuggerIsAttachedSyntax = "System.Diagnostics.Debugger.IsAttached";
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(HttpContextIsDebuggingEnabledAnalyzer.DiagnosticId);
@ -65,14 +64,15 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes
// Replace the member access expression with Debugger.IsAttached
var newExpression = SyntaxFactory.ParseExpression(DebuggerIsAttachedSyntax)
.WithTriviaFrom(node)
.WithAdditionalAnnotations(Simplifier.Annotation);
.WithAdditionalAnnotations(Simplifier.Annotation, Simplifier.AddImportsAnnotation);
documentRoot = documentRoot.ReplaceNode(node, newExpression)!;
// Add a using statement to System.Diagnostics, if necessary
documentRoot = documentRoot.AddUsingIfMissing(DiagnosticsNamespace);
editor.ReplaceNode(editor.OriginalRoot, documentRoot);
return editor.GetChangedDocument();
var updatedDocument = editor.GetChangedDocument();
// Add using declaration if needed
updatedDocument = await ImportAdder.AddImportsAsync(updatedDocument, Simplifier.AddImportsAnnotation, null, cancellationToken).ConfigureAwait(false);
return updatedDocument;
}
}
}

Просмотреть файл

@ -84,23 +84,9 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.CodeFixes
var generator = SyntaxGenerator.GetGenerator(document);
// Split the new idenfitier into namespace and name components
var namespaceDelimiterIndex = newIdentifier.LastIndexOf('.');
var qualifier = namespaceDelimiterIndex >= 0
? newIdentifier.Substring(0, namespaceDelimiterIndex)
: null;
// Create new identifier
var updatedNode = GetUpdatedNode(node, generator, newIdentifier)
.WithAdditionalAnnotations(Simplifier.Annotation);
// The simplifier does not recognize using statements within namespaces, so this
// checks whether the necessary using statement is present or not and adds the
// AddImportsAnnotation only if it's absent.
if (qualifier is not null && !node.HasAccessToNamespace(qualifier))
{
updatedNode = updatedNode.WithAdditionalAnnotations(Simplifier.AddImportsAnnotation);
}
.WithAdditionalAnnotations(Simplifier.Annotation, Simplifier.AddImportsAnnotation);
// Preserve white space and comments
updatedNode = updatedNode.WithTriviaFrom(node);

Просмотреть файл

@ -187,7 +187,11 @@ namespace Microsoft.DotNet.UpgradeAssistant.Steps.Source
if (Diagnostics.Any())
{
Logger.LogWarning("Completing source updates with {DiagnosticCount} diagnostics still unaddressed", Diagnostics.Count());
Logger.LogInformation("Source updates complete with {DiagnosticCount} diagnostics remaining which require manual updates", Diagnostics.Count());
foreach (var diagnostic in Diagnostics)
{
Logger.LogWarning("Manual updates needed to address: {DiagnosticId}@{DiagnosticLocation}: {DiagnosticMessage}", diagnostic.Id, diagnostic.Location, diagnostic.GetMessage());
}
}
return new UpgradeStepApplyResult(UpgradeStepStatus.Complete, string.Empty);

Просмотреть файл

@ -91,7 +91,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
{
new ExpectedDiagnostic("UA0010", new TextSpan(150, 9)),
new ExpectedDiagnostic("UA0010", new TextSpan(240, 18)),
new ExpectedDiagnostic("UA0010", new TextSpan(344, 13)),
new ExpectedDiagnostic("UA0010", new TextSpan(426, 24)),
}
},
@ -168,13 +168,13 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
"ControllerUpgrade",
new[]
{
new ExpectedDiagnostic("UA0002", new TextSpan(166, 13)),
new ExpectedDiagnostic("UA0002", new TextSpan(594, 29)),
new ExpectedDiagnostic("UA0002", new TextSpan(933, 14)),
new ExpectedDiagnostic("UA0002", new TextSpan(1009, 25)),
new ExpectedDiagnostic("UA0002", new TextSpan(1058, 10)),
new ExpectedDiagnostic("UA0002", new TextSpan(1081, 13)),
new ExpectedDiagnostic("UA0002", new TextSpan(1139, 25)),
new ExpectedDiagnostic("UA0002", new TextSpan(187, 13)),
new ExpectedDiagnostic("UA0002", new TextSpan(615, 29)),
new ExpectedDiagnostic("UA0002", new TextSpan(954, 14)),
new ExpectedDiagnostic("UA0002", new TextSpan(1030, 25)),
new ExpectedDiagnostic("UA0002", new TextSpan(1079, 10)),
new ExpectedDiagnostic("UA0002", new TextSpan(1102, 13)),
new ExpectedDiagnostic("UA0002", new TextSpan(1160, 25)),
new ExpectedDiagnostic("UA0002", new TextSpan(177, 13), Language.VisualBasic),
new ExpectedDiagnostic("UA0002", new TextSpan(463, 29), Language.VisualBasic),
@ -182,6 +182,29 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
new ExpectedDiagnostic("UA0002", new TextSpan(1265, 25), Language.VisualBasic),
}
},
{
"AttributesTest",
new[]
{
new ExpectedDiagnostic("UA0010", new TextSpan(370, 9)),
new ExpectedDiagnostic("UA0010", new TextSpan(458, 20)),
new ExpectedDiagnostic("UA0010", new TextSpan(527, 21)),
new ExpectedDiagnostic("UA0010", new TextSpan(549, 33)),
new ExpectedDiagnostic("UA0010", new TextSpan(684, 24)),
new ExpectedDiagnostic("UA0010", new TextSpan(716, 22)),
new ExpectedDiagnostic("UA0010", new TextSpan(782, 4)),
new ExpectedDiagnostic("UA0010", new TextSpan(874, 13)),
new ExpectedDiagnostic("UA0010", new TextSpan(295, 11), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(392, 22), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(475, 23), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(500, 33), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(638, 26), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(672, 24), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(733, 6), Language.VisualBasic),
new ExpectedDiagnostic("UA0010", new TextSpan(845, 13), Language.VisualBasic),
}
},
};
// No diagnostics expected to show up
@ -208,6 +231,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
[InlineData("UA0010")]
[InlineData("UA0012")]
[InlineData("ControllerUpgrade")]
[InlineData("AttributesTest")]
[Theory]
public async Task UpgradeAnalyzers(string scenarioName)
{
@ -238,6 +262,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
[InlineData("UA0010")]
[InlineData("UA0012")]
[InlineData("ControllerUpgrade")]
[InlineData("AttributesTest")]
[Theory]
public async Task UpgradeCodeFixer(string scenarioName)
{

Просмотреть файл

@ -27,7 +27,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
private const string TypeMapPath = "WebTypeReplacements.typemap";
internal static ImmutableArray<DiagnosticAnalyzer> AllAnalyzers => ImmutableArray.Create<DiagnosticAnalyzer>(
new AllowHtmlAttributeAnalyzer(),
new AttributeUpgradeAnalyzer(),
new BinaryFormatterUnsafeDeserializeAnalyzer(),
new HtmlHelperAnalyzer(),
new HttpContextCurrentAnalyzer(),
@ -37,7 +37,7 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
new UrlHelperAnalyzer());
internal static ImmutableArray<CodeFixProvider> AllCodeFixProviders => ImmutableArray.Create<CodeFixProvider>(
new AllowHtmlAttributeCodeFixer(),
new AttributeUpgradeCodeFixer(),
new BinaryFormatterUnsafeDeserializeCodeFixer(),
new HtmlHelperCodeFixer(),
new HttpContextCurrentCodeFixer(),

Просмотреть файл

@ -20,6 +20,8 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
new TestAdditionalText(@"C:\Foo\bar\x.typemap", @"a b
Test1 Test2
x
y
1 3 2
4 5
@ -40,6 +42,8 @@ namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test
{
new TypeMapping("a", "b"),
new TypeMapping("Test1", "Test2"),
new TypeMapping("x", null),
new TypeMapping("y", null),
new TypeMapping("4", "5"),
new TypeMapping("Foo", "Bar")
};

Просмотреть файл

@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test.assets.TestClasses
{
public class Class1
{
}
[System.AllowHtml] //
[ResponseCache]
public class Class2
{
}
[Authorize]
public class Class3
{
}
[Microsoft.AspNetCore.Mvc.Filters.ActionFilter]
[Authorize]
public class Class4
{
[Bind]
[Microsoft.AspNetCore.Authorization.Authorize]
public void Method1([BindAttribute] string s)
{
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
' Licensed to the .NET Foundation under one Or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Imports Microsoft.AspNetCore.Authorization
Imports Microsoft.AspNetCore.Mvc
Namespace TestClasses
Public Class Class1
End Class
<System.AllowHtml()>
<ResponseCache()> ' Comment2
Public Class Class2
End Class
<Authorize>
Public Class Class3
End Class
<Microsoft.AspNetCore.Mvc.Filters.ActionFilter()>
<Authorize()>
Public Class Class4
<Bind()>
<Microsoft.AspNetCore.Authorization.AuthorizeAttribute>
Public Sub Method1(
<BindAttribute> ByVal s As String)
End Sub
End Class
End Namespace

Просмотреть файл

@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test.assets.TestClasses
{
[AllowHtml] //
public class Class1
{
}
[System.AllowHtml] //
[OutputCacheAttribute]
public class Class2
{
}
[Web.Mvc.ValidateInput,System.Web.Mvc.AuthorizeAttribute]
public class Class3
{
}
[Microsoft.AspNetCore.Mvc.Filters.ActionFilter]
[System.Web.Mvc.Authorize]
[ValidateInputAttribute]
public class Class4
{
[Bind]
[Microsoft.AspNetCore.Authorization.Authorize]
public void Method1([BindAttribute] string s)
{
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
' Licensed to the .NET Foundation under one Or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Namespace TestClasses
<AllowHtml()> ' Comment
Public Class Class1
End Class
<System.AllowHtml()>
<OutputCacheAttribute()> ' Comment2
Public Class Class2
End Class
<Web.Mvc.ValidateInput(), System.Web.Mvc.AuthorizeAttribute>
Public Class Class3
End Class
<Microsoft.AspNetCore.Mvc.Filters.ActionFilter()>
<System.Web.Mvc.Authorize()>
<ValidateInputAttribute()>
Public Class Class4
<Bind()>
<Microsoft.AspNetCore.Authorization.AuthorizeAttribute>
Public Sub Method1(
<BindAttribute> ByVal s As String)
End Sub
End Class
End Namespace

Просмотреть файл

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
namespace TestProject.TestClasses
{
[ApiController]
public partial class ValuesController : Microsoft.AspNetCore.Mvc.ControllerBase
{
// GET api/values

Просмотреть файл

@ -4,6 +4,7 @@ using System.Collections.Generic;
namespace TestProject.TestClasses
{
[ApiController]
public partial class ValuesController : ApiController
{
// GET api/values

Просмотреть файл

@ -1,6 +1,6 @@
using System.Web;
using System.Diagnostics;
using System.Web;
using AspNetUpgrade;
using System.Diagnostics;
namespace TestProject.TestClasses
{

Просмотреть файл

@ -11,6 +11,9 @@ namespace TestProject.TestClasses
[MyNamespace.AllowHtml]
public int Property2 { get; }
[Foo.AllowHtml, Required]
public double Property3 { set { } }
[Required]
public double Property3 { set { } }
}

Просмотреть файл

@ -15,5 +15,8 @@ namespace TestProject.TestClasses
[Foo.AllowHtml, Required]
public double Property3 { set { } }
[System.Web.Mvc.AllowHtml, Required]
public double Property3 { set { } }
}
}

Просмотреть файл

@ -12,8 +12,8 @@
</ItemGroup>
<ItemGroup>
<Compile Remove="*\UA*.cs" />
<Compile Include="*\UA*.Fixed.cs" />
<Compile Remove="*\*.cs" />
<Compile Include="*\*.Fixed.cs" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -12,8 +12,8 @@
</ItemGroup>
<ItemGroup>
<Compile Remove="*\UA*.vb" />
<Compile Include="*\UA*.Fixed.vb" />
<Compile Remove="*\*.vb" />
<Compile Include="*\*.Fixed.vb" />
</ItemGroup>
<ItemGroup>