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:
Родитель
0bfbdf260b
Коммит
723cc4b64a
|
@ -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 '{0}' should be removed.
|
||||
/// Looks up a localized string similar to Attribute type '{0}' should be replaced with '{1}'.
|
||||
/// </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 'ApiController' should be replaced with 'Microsoft.AspNetCore.Mvc.Controller'..
|
||||
/// </summary>
|
||||
internal static string ApiControllerDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("ApiControllerDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 'ApiController' should be replaced with 'Microsoft.AspNetCore.Mvc.Controller'.
|
||||
/// </summary>
|
||||
internal static string ApiControllerMessageFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("ApiControllerMessageFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 'ApiController' 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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче