Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorN. Taylor Mullen <nimullen@microsoft.com>2019-05-17 01:08:10 +0300
committerN. Taylor Mullen <nimullen@microsoft.com>2019-05-17 01:08:10 +0300
commitd05e19ae6e994f2fecc0628cfe92b4d323d0ac1d (patch)
tree177ebedce6fdb9bcc4d610e1e7288423c1bcee28
parentbabfb20188f3be92ed6fde13dab08b638f3d3bd0 (diff)
-rw-r--r--src/Mvc/Mvc.Analyzers/src/SymbolNames.cs2
-rw-r--r--src/Mvc/Mvc.Analyzers/src/TagHelpersInCodeBlocksAnalyzer.cs278
-rw-r--r--src/Mvc/Mvc.Analyzers/src/ViewFeaturesAnalyzerContext.cs3
3 files changed, 198 insertions, 85 deletions
diff --git a/src/Mvc/Mvc.Analyzers/src/SymbolNames.cs b/src/Mvc/Mvc.Analyzers/src/SymbolNames.cs
index 5b579bac6c..944d2af448 100644
--- a/src/Mvc/Mvc.Analyzers/src/SymbolNames.cs
+++ b/src/Mvc/Mvc.Analyzers/src/SymbolNames.cs
@@ -78,5 +78,7 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
public const string TaskTypeName = "System.Threading.Tasks.Task";
public const string TagHelperRunnerFieldName = "__tagHelperRunner";
+
+ public const string TagHelperRunnerTypeName = "Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner";
}
}
diff --git a/src/Mvc/Mvc.Analyzers/src/TagHelpersInCodeBlocksAnalyzer.cs b/src/Mvc/Mvc.Analyzers/src/TagHelpersInCodeBlocksAnalyzer.cs
index 288b0d0f46..b5ab6aa03b 100644
--- a/src/Mvc/Mvc.Analyzers/src/TagHelpersInCodeBlocksAnalyzer.cs
+++ b/src/Mvc/Mvc.Analyzers/src/TagHelpersInCodeBlocksAnalyzer.cs
@@ -2,136 +2,244 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Immutable;
using System.Diagnostics;
+using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.AspNetCore.Mvc.Analyzers
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
- public class TagHelpersInCodeBlocksAnalyzer : ViewFeatureAnalyzerBase
+ public class TagHelpersInCodeBlocksAnalyzer : DiagnosticAnalyzer
{
public TagHelpersInCodeBlocksAnalyzer()
- : base(DiagnosticDescriptors.MVC1006_FunctionsContainingTagHelpersMustBeAsyncAndReturnTask)
{
+ TagHelperInCodeBlockDiagnostic = DiagnosticDescriptors.MVC1006_FunctionsContainingTagHelpersMustBeAsyncAndReturnTask;
+ SupportedDiagnostics = ImmutableArray.Create(new[] { TagHelperInCodeBlockDiagnostic });
}
- protected override void InitializeWorker(ViewFeaturesAnalyzerContext analyzerContext)
+ private DiagnosticDescriptor TagHelperInCodeBlockDiagnostic { get; }
+
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
+
+ public override void Initialize(AnalysisContext context)
{
- analyzerContext.Context.RegisterSyntaxNodeAction(context =>
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics);
+ context.RegisterCompilationStartAction(context =>
{
- var invocationExpression = (InvocationExpressionSyntax)context.Node;
- var symbol = context.SemanticModel.GetSymbolInfo(invocationExpression, context.CancellationToken).Symbol;
- if (symbol == null || symbol.Kind != SymbolKind.Method)
+ var symbolCache = new SymbolCache(context.Compilation);
+
+ if (symbolCache.TagHelperRunnerRunAsyncMethodSymbol == null)
{
+ // No-op if we can't find bits we care about.
+ Debugger.Launch();
return;
}
- var method = (IMethodSymbol)symbol;
- if (!IsTagHelperRunnerRunAsync(invocationExpression, method))
+ var diagnostics = context.Compilation.GetDiagnostics();
+ Debugger.Launch();
+
+ context.RegisterOperationAction(context =>
{
- return;
- }
+ Debugger.Launch();
+ }, OperationKind.Await, OperationKind.Invocation);
+ });
+ }
+
+ internal void InitializeWorker(CompilationStartAnalysisContext context, SymbolCache symbolCache)
+ {
+ context.RegisterOperationAction(context =>
+ {
+ Debugger.Launch();
+ }, OperationKind.Await, OperationKind.Invocation);
+
+ context.RegisterOperationAction(context =>
+ {
+ var awaitOperation = (IAwaitOperation)context.Operation;
+
+ //if (!IsTagHelperRunnerRunAsync(awaitOperation.TargetMethod, symbolCache))
+ //{
+ // return;
+ //}
+
+ //var parent = context.Operation.Parent;
+ //while (parent != null && !IsParentMethod(parent))
+ //{
+ // parent = parent.Parent;
+ //}
+
+ //if (parent == null)
+ //{
+ // return;
+ //}
+
+ //// I'd like to register for invocation operations so I can detect awaits inside of this local function.
+
+ //bool IsParentMethod(IOperation operation)
+ //{
+ // if (operation.Kind == OperationKind.LocalFunction)
+ // {
+ // return true;
+ // }
+
+ // if (operation.Kind == OperationKind.MethodBody)
+ // {
+ // return true;
+ // }
- var containingFunction = context.Node.FirstAncestorOrSelf<SyntaxNode>(node =>
- node.IsKind(SyntaxKind.ParenthesizedLambdaExpression) ||
- node.IsKind(SyntaxKind.AnonymousMethodExpression) ||
- node.IsKind(SyntaxKind.LocalFunctionStatement) ||
- node.IsKind(SyntaxKind.MethodDeclaration));
+ // if (operation.Kind == OperationKind.AnonymousFunction)
+ // {
+ // return true;
+ // }
- if (containingFunction == null)
+ // return false;
+ //}
+
+ }, OperationKind.Await);
+
+ context.RegisterSymbolStartAction(context =>
+ {
+ var method = (IMethodSymbol)context.Symbol;
+
+ if (method.IsAsync)
{
- // In practice should never happen because the Razor bits at the bare minimum should be encompassed by a method declaration.
- // That being said, if a user were to write malformed code that fulfilled our TagHelper lookup outside of a method block we
- // would get a null here.
return;
}
- switch (containingFunction)
+ context.RegisterOperationAction(context =>
{
- case ParenthesizedLambdaExpressionSyntax parenthesizedLambda:
- var lambdaSymbol = (IMethodSymbol)context.SemanticModel.GetSymbolInfo(parenthesizedLambda).Symbol;
- if (!lambdaSymbol.IsAsync ||
- !analyzerContext.TaskType.IsAssignableFrom(lambdaSymbol.ReturnType))
- {
- context.ReportDiagnostic(Diagnostic.Create(
- SupportedDiagnostic,
- parenthesizedLambda.ParameterList.GetLocation(),
- new[] { "lambda" }));
- }
-
- break;
- case AnonymousMethodExpressionSyntax anonymousMethod:
- var anonymousMethodSymbol = (IMethodSymbol)context.SemanticModel.GetSymbolInfo(anonymousMethod).Symbol;
- if (!anonymousMethodSymbol.IsAsync ||
- !analyzerContext.TaskType.IsAssignableFrom(anonymousMethodSymbol.ReturnType))
- {
- context.ReportDiagnostic(Diagnostic.Create(
- SupportedDiagnostic,
- anonymousMethod.DelegateKeyword.GetLocation(),
- new[] { "method" }));
- }
-
- break;
- case LocalFunctionStatementSyntax localFunction:
- var localFunctionReturnType = (INamedTypeSymbol)context.SemanticModel.GetSymbolInfo(localFunction.ReturnType).Symbol;
- if (!analyzerContext.TaskType.IsAssignableFrom(localFunctionReturnType) ||
- localFunction.Modifiers.IndexOf(SyntaxKind.AsyncKeyword) == -1)
- {
- context.ReportDiagnostic(Diagnostic.Create(
- SupportedDiagnostic,
- localFunction.Identifier.GetLocation(),
- new[] { "local function" }));
- }
- break;
- case MethodDeclarationSyntax methodDeclaration:
- var methodDeclarationReturnType = (INamedTypeSymbol)context.SemanticModel.GetSymbolInfo(methodDeclaration.ReturnType).Symbol;
- if (!analyzerContext.TaskType.IsAssignableFrom(methodDeclarationReturnType) ||
- methodDeclaration.Modifiers.IndexOf(SyntaxKind.AsyncKeyword) == -1)
- {
- context.ReportDiagnostic(Diagnostic.Create(
- SupportedDiagnostic,
- methodDeclaration.Identifier.GetLocation(),
- new[] { "method" }));
- }
- break;
- }
-
- }, SyntaxKind.InvocationExpression);
+ var invocationOperation = (IInvocationOperation)context.Operation;
+
+ if (!IsTagHelperRunnerRunAsync(invocationOperation.TargetMethod, symbolCache))
+ {
+ return;
+ }
+
+ //context.ReportDiagnostic(Diagnostic.Create(
+ // TagHelperInCodeBlockDiagnostic,
+ // method.Identifier.GetLocation(),
+ // new[] { "method" }));
+ }, OperationKind.Invocation);
+
+ }, SymbolKind.Method);
+
+ /*
+ * void Foo()
+ * {
+ * await __tagHelperRunner.RunAsync...
+ * }
+ */
+
+ //context.RegisterSyntaxNodeAction(context =>
+ //{
+ // var invocationExpression = (InvocationExpressionSyntax)context.Node;
+ // var symbol = context.SemanticModel.GetSymbolInfo(invocationExpression, context.CancellationToken).Symbol;
+
+ // if (symbol == null || symbol.Kind != SymbolKind.Method)
+ // {
+ // return;
+ // }
+
+ // var method = (IMethodSymbol)symbol;
+
+ // if (!IsTagHelperRunnerRunAsync(method, symbolCache))
+ // {
+ // return;
+ // }
+
+ // var containingFunction = context.Node.FirstAncestorOrSelf<SyntaxNode>(node =>
+ // node.IsKind(SyntaxKind.ParenthesizedLambdaExpression) ||
+ // node.IsKind(SyntaxKind.AnonymousMethodExpression) ||
+ // node.IsKind(SyntaxKind.LocalFunctionStatement) ||
+ // node.IsKind(SyntaxKind.MethodDeclaration));
+
+ // if (containingFunction == null)
+ // {
+ // // In practice should never happen because the Razor bits at the bare minimum should be encompassed by a method declaration.
+ // // That being said, if a user were to write malformed code that fulfilled our TagHelper lookup outside of a method block we
+ // // would get a null here.
+ // return;
+ // }
+
+ // switch (containingFunction)
+ // {
+ // case ParenthesizedLambdaExpressionSyntax parenthesizedLambda:
+ // var lambdaSymbol = (IMethodSymbol)context.SemanticModel.GetSymbolInfo(parenthesizedLambda).Symbol;
+ // if (!lambdaSymbol.IsAsync)
+ // {
+ // context.ReportDiagnostic(Diagnostic.Create(
+ // TagHelperInCodeBlockDiagnostic,
+ // parenthesizedLambda.ParameterList.GetLocation(),
+ // new[] { "lambda" }));
+ // }
+
+ // break;
+ // case AnonymousMethodExpressionSyntax anonymousMethod:
+ // var anonymousMethodSymbol = (IMethodSymbol)context.SemanticModel.GetSymbolInfo(anonymousMethod).Symbol;
+ // if (!anonymousMethodSymbol.IsAsync)
+ // {
+ // context.ReportDiagnostic(Diagnostic.Create(
+ // TagHelperInCodeBlockDiagnostic,
+ // anonymousMethod.DelegateKeyword.GetLocation(),
+ // new[] { "method" }));
+ // }
+
+ // break;
+ // case LocalFunctionStatementSyntax localFunction:
+ // var localFunctionReturnType = (INamedTypeSymbol)context.SemanticModel.GetSymbolInfo(localFunction.ReturnType).Symbol;
+ // if (localFunction.Modifiers.IndexOf(SyntaxKind.AsyncKeyword) == -1)
+ // {
+ // context.ReportDiagnostic(Diagnostic.Create(
+ // TagHelperInCodeBlockDiagnostic,
+ // localFunction.Identifier.GetLocation(),
+ // new[] { "local function" }));
+ // }
+ // break;
+ // case MethodDeclarationSyntax methodDeclaration:
+ // var methodDeclarationReturnType = (INamedTypeSymbol)context.SemanticModel.GetSymbolInfo(methodDeclaration.ReturnType).Symbol;
+ // if (methodDeclaration.Modifiers.IndexOf(SyntaxKind.AsyncKeyword) == -1)
+ // {
+ // context.ReportDiagnostic(Diagnostic.Create(
+ // TagHelperInCodeBlockDiagnostic,
+ // methodDeclaration.Identifier.GetLocation(),
+ // new[] { "method" }));
+ // }
+ // break;
+ // }
+
+ //}, SyntaxKind.InvocationExpression);
}
- private bool IsTagHelperRunnerRunAsync(InvocationExpressionSyntax parentExpression, IMethodSymbol method)
+ private bool IsTagHelperRunnerRunAsync(IMethodSymbol method, SymbolCache symbolCache)
{
if (method.IsGenericMethod)
{
return false;
}
- if (!string.Equals(SymbolNames.RunAsyncMethodName, method.Name, StringComparison.Ordinal))
+ if (method != symbolCache.TagHelperRunnerRunAsyncMethodSymbol)
{
return false;
}
- if (!parentExpression.Expression.IsKind(SyntaxKind.SimpleMemberAccessExpression))
- {
- return false;
- }
+ return true;
+ }
- var memberAccessExpressionSyntax = (MemberAccessExpressionSyntax)parentExpression.Expression;
- if (!memberAccessExpressionSyntax.Expression.IsKind(SyntaxKind.IdentifierName))
+ internal readonly struct SymbolCache
+ {
+ public SymbolCache(Compilation compilation)
{
- return false;
- }
+ var tagHelperRunnerType = compilation.GetTypeByMetadataName(SymbolNames.TagHelperRunnerTypeName);
+ var members = tagHelperRunnerType.GetMembers(SymbolNames.RunAsyncMethodName);
- var identifier = (IdentifierNameSyntax)memberAccessExpressionSyntax.Expression;
- if (!string.Equals(SymbolNames.TagHelperRunnerFieldName, identifier.Identifier.ValueText, StringComparison.Ordinal))
- {
- return false;
+ TagHelperRunnerRunAsyncMethodSymbol = members.Length == 1 ? (IMethodSymbol)members[0] : null;
}
- return true;
+ public IMethodSymbol TagHelperRunnerRunAsyncMethodSymbol { get; }
}
}
}
diff --git a/src/Mvc/Mvc.Analyzers/src/ViewFeaturesAnalyzerContext.cs b/src/Mvc/Mvc.Analyzers/src/ViewFeaturesAnalyzerContext.cs
index 9f7323aa80..0caba5e3b5 100644
--- a/src/Mvc/Mvc.Analyzers/src/ViewFeaturesAnalyzerContext.cs
+++ b/src/Mvc/Mvc.Analyzers/src/ViewFeaturesAnalyzerContext.cs
@@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
HtmlHelperType = GetType(SymbolNames.IHtmlHelperType);
HtmlHelperPartialExtensionsType = GetType(SymbolNames.HtmlHelperPartialExtensionsType);
TaskType = GetType(SymbolNames.TaskTypeName);
+ TagHelperRunnerType = GetType(SymbolNames.TagHelperRunnerTypeName);
}
public CompilationStartAnalysisContext Context { get; }
@@ -26,6 +27,8 @@ namespace Microsoft.AspNetCore.Mvc.Analyzers
public INamedTypeSymbol TaskType { get; }
+ public INamedTypeSymbol TagHelperRunnerType { get; }
+
private INamedTypeSymbol GetType(string name) => Context.Compilation.GetTypeByMetadataName(name);
public bool IsHtmlHelperExtensionMethod(IMethodSymbol method)