-
Notifications
You must be signed in to change notification settings - Fork 468
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial prototype of AvoidPotentiallyExpensiveCallWhenLoggingAnalyzer
- Loading branch information
1 parent
c2fd19b
commit 9b954ae
Showing
3 changed files
with
115 additions
and
2 deletions.
There are no files selected for viewing
113 changes: 113 additions & 0 deletions
113
...crosoft.NetCore.Analyzers/Performance/AvoidPotentiallyExpensiveCallWhenLoggingAnalyzer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using Analyzer.Utilities; | ||
using Analyzer.Utilities.Extensions; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Operations; | ||
|
||
namespace Microsoft.NetCore.Analyzers.Performance | ||
{ | ||
using static MicrosoftNetCoreAnalyzersResources; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] | ||
internal sealed class AvoidPotentiallyExpensiveCallWhenLoggingAnalyzer : DiagnosticAnalyzer | ||
{ | ||
private static DiagnosticDescriptor s_rule = DiagnosticDescriptorHelper.Create( | ||
"CA1862", | ||
"Title TODO", | ||
"Message TODO", | ||
DiagnosticCategory.Performance, | ||
RuleLevel.IdeSuggestion, | ||
"Description TODO", | ||
isPortedFxCopRule: false, | ||
isDataflowRule: false); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_rule); | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.EnableConcurrentExecution(); | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); | ||
context.RegisterCompilationStartAction(context => | ||
{ | ||
var loggerMessageAttribute = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftExtensionsLoggingLoggerMessageAttribute); | ||
var loggerExtensionsSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftExtensionsLoggingLoggerExtensions); | ||
var iloggerLogMethodSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftExtensionsLoggingILogger)?.GetMembers("Log").FirstOrDefault() as IMethodSymbol; | ||
context.RegisterOperationAction(context => AnalyzeInvocationOperation(context, loggerMessageAttribute, loggerExtensionsSymbol, iloggerLogMethodSymbol), OperationKind.Invocation); | ||
}); | ||
|
||
} | ||
|
||
private static void AnalyzeInvocationOperation(OperationAnalysisContext context, INamedTypeSymbol? loggerMessageAttribute, INamedTypeSymbol? loggerExtensionsSymbol, IMethodSymbol? iloggerLogMethodSymbol) | ||
{ | ||
var invocation = (IInvocationOperation)context.Operation; | ||
if (!IsValidLoggingInvocation(invocation, loggerMessageAttribute, loggerExtensionsSymbol, iloggerLogMethodSymbol)) | ||
{ | ||
return; | ||
} | ||
|
||
foreach (var argument in invocation.Arguments) | ||
{ | ||
if (!IsGoodArgument(argument)) | ||
{ | ||
context.ReportDiagnostic(argument.CreateDiagnostic(s_rule)); | ||
} | ||
} | ||
} | ||
|
||
private static bool IsGoodArgument(IArgumentOperation argumentOperation) | ||
{ | ||
if (argumentOperation.Value is ILiteralOperation or ILocalReferenceOperation) | ||
{ | ||
return true; | ||
} | ||
|
||
return IsGoodArgumentRecursive(argumentOperation.Value); | ||
} | ||
|
||
private static bool IsGoodArgumentRecursive(IOperation operationValue) | ||
{ | ||
if (operationValue is null) | ||
{ | ||
return true; | ||
} | ||
|
||
if (operationValue is IMemberReferenceOperation memberReference) | ||
{ | ||
return IsGoodArgumentRecursive(memberReference.Instance); | ||
} | ||
|
||
if (operationValue is IArrayElementReferenceOperation arrayElementReference) | ||
{ | ||
return IsGoodArgumentRecursive(arrayElementReference.ArrayReference); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private static bool IsValidLoggingInvocation(IInvocationOperation invocation, INamedTypeSymbol? loggerMessageAttribute, INamedTypeSymbol? loggerExtensionsSymbol, IMethodSymbol? iloggerLogMethodSymbol) | ||
{ | ||
var method = invocation.TargetMethod; | ||
if (method.Equals(iloggerLogMethodSymbol, SymbolEqualityComparer.Default)) | ||
{ | ||
return true; | ||
} | ||
|
||
if (method.ContainingType.Equals(loggerExtensionsSymbol, SymbolEqualityComparer.Default)) | ||
{ | ||
return true; | ||
} | ||
|
||
if (loggerMessageAttribute is not null && | ||
method.GetAttributes().Any((att, arg) => att.AttributeClass.Equals(arg, SymbolEqualityComparer.Default), loggerMessageAttribute)) | ||
{ | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters