From cf3db52122dcc51e8e876e64eacd310dd49582a3 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sat, 8 Aug 2020 18:44:42 +0300 Subject: [PATCH 01/13] Redo pubternalyzer with Roslyn Operations * Rewrite most of the analyzer using Operations, for much better perf and VB.NET support. * Support some extra language scenarios which weren't being detected. Fixes #19648 Fixes #20206 --- .../InternalUsageDiagnosticAnalyzer.cs | 213 +++++++++++++----- ...ionBindingRemovingExpressionVisitorBase.cs | 3 + ...ssionVisitor.ReadItemQueryingEnumerable.cs | 8 +- .../Internal/MigrationsModelDiffer.cs | 2 + .../InternalUsageDiagnosticAnalyzerTest.cs | 175 +++++++------- 5 files changed, 250 insertions(+), 151 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index eb4dd635156..bfba5cbaa61 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -8,18 +8,19 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; namespace Microsoft.EntityFrameworkCore { - [DiagnosticAnalyzer(LanguageNames.CSharp)] + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public class InternalUsageDiagnosticAnalyzer : DiagnosticAnalyzer { public const string Id = "EF1001"; public const string MessageFormat - = "{0} is an internal API that supports the Entity Framework Core infrastructure and " + - "not subject to the same compatibility standards as public APIs. " + - "It may be changed or removed without notice in any release."; + = "{0} is an internal API that supports the Entity Framework Core infrastructure and " + + "not subject to the same compatibility standards as public APIs. " + + "It may be changed or removed without notice in any release."; protected const string DefaultTitle = "Internal EF Core API usage."; protected const string Category = "Usage"; @@ -40,84 +41,172 @@ private static readonly DiagnosticDescriptor _descriptor public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); - context.RegisterSyntaxNodeAction( - AnalyzeNode, - SyntaxKind.SimpleMemberAccessExpression, - SyntaxKind.ObjectCreationExpression, - SyntaxKind.ClassDeclaration); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterOperationAction(AnalyzeNode, + OperationKind.FieldReference, + OperationKind.PropertyReference, + OperationKind.MethodReference, + OperationKind.EventReference, + OperationKind.Invocation, + OperationKind.ObjectCreation, + OperationKind.VariableDeclaration, + OperationKind.TypeOf); + + context.RegisterSyntaxNodeAction(AnalyzeClassDeclaration, SyntaxKind.ClassDeclaration); } - private void AnalyzeNode(SyntaxNodeAnalysisContext context) + private static void AnalyzeNode(OperationAnalysisContext context) { - switch (context.Node) + switch (context.Operation.Kind) { - case MemberAccessExpressionSyntax memberAccessSyntax: - { - if (context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol is ISymbol symbol - && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly)) + case OperationKind.FieldReference: + AnalyzeMember(context, ((IFieldReferenceOperation)context.Operation).Field); + break; + case OperationKind.PropertyReference: + AnalyzeMember(context, ((IPropertyReferenceOperation)context.Operation).Property); + break; + case OperationKind.EventReference: + AnalyzeMember(context, ((IEventReferenceOperation)context.Operation).Event); + break; + case OperationKind.MethodReference: + AnalyzeMember(context, ((IMethodReferenceOperation)context.Operation).Method); + break; + case OperationKind.ObjectCreation: + AnalyzeMember(context, ((IObjectCreationOperation)context.Operation).Constructor); + break; + + case OperationKind.Invocation: + AnalyzeInvocation(context, (IInvocationOperation)context.Operation); + break; + + case OperationKind.VariableDeclaration: + AnalyzeVariableDeclaration(context, ((IVariableDeclarationOperation)context.Operation)); + break; + + case OperationKind.TypeOf: + AnalyzeTypeof(context, ((ITypeOfOperation)context.Operation)); + break; + + default: + throw new ArgumentException($"Unexpected {nameof(OperationKind)}: {context.Operation.Kind}"); + } + + } + + private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symbol) + { + if (Equals(symbol.ContainingAssembly, context.Compilation.Assembly)) + { + // Skip all methods inside the same assembly - internal access is fine + return; + } + + var containingType = symbol.ContainingType; + + switch (symbol) + { + case IMethodSymbol _: + case IFieldSymbol _: + case IPropertySymbol _: + case IEventSymbol _: + if (HasInternalAttribute(symbol)) { - var containingType = symbol.ContainingType; - - if (HasInternalAttribute(symbol)) - { - context.ReportDiagnostic( - Diagnostic.Create(_descriptor, memberAccessSyntax.Name.GetLocation(), $"{containingType}.{symbol.Name}")); - return; - } - - if (IsInInternalNamespace(containingType) - || HasInternalAttribute(containingType)) - { - context.ReportDiagnostic(Diagnostic.Create(_descriptor, memberAccessSyntax.Name.GetLocation(), containingType)); - return; - } + ReportDiagnostic(symbol.Name == ".ctor" ? (object)containingType : $"{containingType}.{symbol.Name}"); + return; } + break; + } - return; - } + if (IsTypeInternal(context, containingType)) + { + ReportDiagnostic(containingType); + } - case ObjectCreationExpressionSyntax creationSyntax: + void ReportDiagnostic(object messageArg) + { + // For C# member access expressions, report a narrowed-down diagnostic, otherwise take the whole invocation. + var syntax = context.Operation.Syntax switch { - if (context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol is ISymbol symbol - && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly)) - { - var containingType = symbol.ContainingType; - - if (HasInternalAttribute(symbol)) - { - context.ReportDiagnostic(Diagnostic.Create(_descriptor, creationSyntax.GetLocation(), containingType)); - return; - } - - if (IsInInternalNamespace(containingType) - || HasInternalAttribute(containingType)) - { - context.ReportDiagnostic(Diagnostic.Create(_descriptor, creationSyntax.Type.GetLocation(), containingType)); - return; - } - } + InvocationExpressionSyntax invocationSyntax + when invocationSyntax.Expression is MemberAccessExpressionSyntax memberAccessSyntax + => memberAccessSyntax.Name, + MemberAccessExpressionSyntax memberAccessSyntax + => memberAccessSyntax.Name, + ObjectCreationExpressionSyntax objectCreationSyntax + => objectCreationSyntax.Type, + _ + => context.Operation.Syntax + }; + + context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), messageArg)); + } + } - return; + private static void AnalyzeInvocation(OperationAnalysisContext context, IInvocationOperation invocation) + { + // First check for any internal type parameters + foreach (var a in invocation.TargetMethod.TypeArguments) + { + if (IsTypeInternal(context, a)) + { + context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Operation.Syntax.GetLocation(), a)); } + } - case ClassDeclarationSyntax declarationSyntax: - { - if (context.SemanticModel.GetDeclaredSymbol(declarationSyntax)?.BaseType is ISymbol symbol - && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly) - && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)) - && declarationSyntax.BaseList?.Types.Count > 0) - { - context.ReportDiagnostic(Diagnostic.Create(_descriptor, declarationSyntax.BaseList.Types[0].GetLocation(), symbol)); - } + // Then check the method being invoked + AnalyzeMember(context, invocation.TargetMethod); + } + private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, IVariableDeclarationOperation variableDeclaration) + { + foreach (var declarator in variableDeclaration.Declarators) + { + if (IsTypeInternal(context, declarator.Symbol.Type)) + { + context.ReportDiagnostic( + Diagnostic.Create( + _descriptor, + ((VariableDeclarationSyntax)context.Operation.Syntax).Type.GetLocation(), + declarator.Symbol.Type)); return; } } } + private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOperation typeOf) + { + if (IsTypeInternal(context, typeOf.TypeOperand)) + { + context.ReportDiagnostic( + Diagnostic.Create( + _descriptor, + ((TypeOfExpressionSyntax)context.Operation.Syntax).Type.GetLocation(), + typeOf.TypeOperand)); + } + } + + private static void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context) + { + var declarationSyntax = (ClassDeclarationSyntax)context.Node; + + if (context.SemanticModel.GetDeclaredSymbol(declarationSyntax)?.BaseType is ISymbol symbol + && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly) + && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)) + && declarationSyntax.BaseList?.Types.Count > 0) + { + context.ReportDiagnostic(Diagnostic.Create(_descriptor, declarationSyntax.BaseList.Types[0].GetLocation(), symbol)); + } + } + + private static bool IsTypeInternal(OperationAnalysisContext context, ISymbol symbol) + => !Equals(symbol.ContainingAssembly, context.Compilation.Assembly) + && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)); + private static bool HasInternalAttribute(ISymbol symbol) - => symbol != null && symbol.GetAttributes().Any(a => a.AttributeClass.Name == "EntityFrameworkInternalAttribute"); + => symbol != null + && symbol.GetAttributes().Any(a => + a.AttributeClass.ToDisplayString() == "Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkInternalAttribute"); private static bool IsInInternalNamespace(ISymbol symbol) { diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs index aac57f1d65f..74d8484fe88 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs @@ -365,9 +365,12 @@ private void AddInclude( var includeMethod = navigation.IsCollection ? _includeCollectionMethodInfo : _includeReferenceMethodInfo; var includingClrType = navigation.DeclaringEntityType.ClrType; var relatedEntityClrType = navigation.TargetEntityType.ClrType; +#pragma warning disable EF1001 // Internal EF Core API usage. var entityEntryVariable = _trackQueryResults ? shaperBlock.Variables.Single(v => v.Type == typeof(InternalEntityEntry)) : (Expression)Expression.Constant(null, typeof(InternalEntityEntry)); +#pragma warning restore EF1001 // Internal EF Core API usage. + var concreteEntityTypeVariable = shaperBlock.Variables.Single(v => v.Type == typeof(IEntityType)); var inverseNavigation = navigation.Inverse; var fixup = GenerateFixup( diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs index abfa48aeee5..3467fb06de3 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs @@ -254,10 +254,10 @@ private bool TryGenerateIdFromKeys(IProperty idProperty, out object value) { var entityEntry = Activator.CreateInstance(_readItemExpression.EntityType.ClrType); -#pragma warning disable EF1001 +#pragma warning disable EF1001 // Internal EF Core API usage. var internalEntityEntry = new InternalEntityEntryFactory().Create( _cosmosQueryContext.Context.GetDependencies().StateManager, _readItemExpression.EntityType, entityEntry); -#pragma warning restore EF1001 +#pragma warning restore EF1001 // Internal EF Core API usage. foreach (var keyProperty in _readItemExpression.EntityType.FindPrimaryKey().Properties) { @@ -265,17 +265,17 @@ private bool TryGenerateIdFromKeys(IProperty idProperty, out object value) if (TryGetParameterValue(property, out var parameterValue)) { +#pragma warning disable EF1001 // Internal EF Core API usage. internalEntityEntry[property] = parameterValue; +#pragma warning restore EF1001 // Internal EF Core API usage. } } #pragma warning disable EF1001 // Internal EF Core API usage. internalEntityEntry.SetEntityState(EntityState.Added); -#pragma warning restore EF1001 // Internal EF Core API usage. value = internalEntityEntry[idProperty]; -#pragma warning disable EF1001 // Internal EF Core API usage. internalEntityEntry.SetEntityState(EntityState.Detached); #pragma warning restore EF1001 // Internal EF Core API usage. diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index ae9a0cbdc10..5be1a9b9f56 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -88,7 +88,9 @@ public MigrationsModelDiffer( { Check.NotNull(typeMappingSource, nameof(typeMappingSource)); Check.NotNull(migrationsAnnotations, nameof(migrationsAnnotations)); +#pragma warning disable EF1001 // Internal EF Core API usage. Check.NotNull(changeDetector, nameof(changeDetector)); +#pragma warning restore EF1001 // Internal EF Core API usage. Check.NotNull(updateAdapterFactory, nameof(updateAdapterFactory)); Check.NotNull(commandBatchPreparerDependencies, nameof(commandBatchPreparerDependencies)); diff --git a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs index f7b4acc1950..2a7934727eb 100644 --- a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs +++ b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs @@ -15,74 +15,106 @@ public class InternalUsageDiagnosticAnalyzerTest : DiagnosticAnalyzerTestBase protected override DiagnosticAnalyzer CreateDiagnosticAnalyzer() => new InternalUsageDiagnosticAnalyzer(); [ConditionalFact] - public async Task No_warning_on_ef_non_internal() - => await AssertNoDiagnostics( - @" -var a = new Microsoft.EntityFrameworkCore.Infrastructure.Annotatable(); -var x = a.GetAnnotations(); -"); + public Task Invocation_on_type_in_internal_namespace() + => Test( + "var x = typeof(object).GetMethod(nameof(object.ToString), Type.EmptyTypes).DisplayName();", + "Microsoft.EntityFrameworkCore.Internal.MethodInfoExtensions", + "DisplayName"); - #region Namespace + [ConditionalFact] + public Task Instantiation_on_type_in_internal_namespace() + => Test( + "new CoreSingletonOptions();", + "Microsoft.EntityFrameworkCore.Internal.CoreSingletonOptions", + "CoreSingletonOptions"); [ConditionalFact] - public async Task Warning_on_ef_internal_namespace_invocation() + public async Task Subclassing_type_in_internal_namespace() { - var (diagnostics, source) = await GetDiagnosticsAsync( - @"var x = typeof(object).GetMethod(nameof(object.ToString), Type.EmptyTypes).DisplayName();"); - var diagnostic = diagnostics.Single(); + var source = @" +class MyClass : Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter { + MyClass() : base(null, null) {} +}"; - Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); - Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); - Assert.Equal( - string.Format(InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Internal.MethodInfoExtensions"), - diagnostic.GetMessage()); + var diagnostics = await GetDiagnosticsFullSourceAsync(source); - var span = diagnostic.Location.SourceSpan; - Assert.Equal("DisplayName", source[span.Start..span.End]); + Assert.Collection(diagnostics, + diagnostic => + { + Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); + Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); + Assert.Equal( + string.Format( + InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter"), + diagnostic.GetMessage()); + + var span = diagnostic.Location.SourceSpan; + Assert.Equal( + "Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter", + source[span.Start..span.End]); + }, + diagnostic => + { + Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); + Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); + Assert.Equal( + string.Format( + InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter"), + diagnostic.GetMessage()); + + var span = diagnostic.Location.SourceSpan; + Assert.Equal(": base(null, null)", source[span.Start..span.End]); + }); } [ConditionalFact] - public async Task Warning_on_ef_internal_namespace_instantiation() - { - var (diagnostics, source) = await GetDiagnosticsAsync(@"new CoreSingletonOptions();"); - var diagnostic = diagnostics.Single(); + public Task Access_property_with_internal_attribute() + => Test( + "var x = Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices.Count;", + "Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices", + "RelationalServices"); - Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); - Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); - Assert.Equal( - string.Format(InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Internal.CoreSingletonOptions"), - diagnostic.GetMessage()); + [ConditionalFact] + public Task Instantiation_with_ctor_with_internal_attribute() + => Test( + "new Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies(null, null);", + "Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies", + "Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies"); - var span = diagnostic.Location.SourceSpan; - Assert.Equal("CoreSingletonOptions", source[span.Start..span.End]); - } + [ConditionalFact] + public Task Variable_declaration() + => Test( + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager state = null;", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); [ConditionalFact] - public async Task Warning_on_ef_internal_namespace_subclass() - { - var source = @" -class MyClass : Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter { - MyClass() : base (null, null) {} -}"; + public Task Generic_type_parameter_in_method_call() + => Test( + @" +void SomeGenericMethod() {} - var diagnostics = await GetDiagnosticsFullSourceAsync(source); - var diagnostic = diagnostics.Single(); +SomeGenericMethod();", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "SomeGenericMethod()"); - Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); - Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); - Assert.Equal( - string.Format( - InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter"), - diagnostic.GetMessage()); + [ConditionalFact] + public Task Typeof() + => Test( + "var t = typeof(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager);", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); - var span = diagnostic.Location.SourceSpan; - Assert.Equal( - "Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter", - source[span.Start..span.End]); - } + [ConditionalFact] + public async Task No_warning_on_non_internal() + => await AssertNoDiagnostics( + @" +var a = new Microsoft.EntityFrameworkCore.Infrastructure.Annotatable(); +var x = a.GetAnnotations(); +"); [ConditionalFact] - public async Task No_warning_on_ef_internal_namespace_in_same_assembly() + public async Task No_warning_in_same_assembly() { var diagnostics = await GetDiagnosticsFullSourceAsync( @" @@ -107,51 +139,24 @@ public void Main(string[] args) { Assert.Empty(diagnostics); } - #endregion Namespace - - #region Attribute - - [ConditionalFact] - public async Task Warning_on_ef_internal_attribute_property_access() - { - var (diagnostics, source) = await GetDiagnosticsAsync( - @"var x = Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices.Count;"); - //throw new Exception("FOO: " + string.Join(", ", diagnostics.Select(d => d.ToString()))); - var diagnostic = diagnostics.Single(); - - Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); - Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); - Assert.Equal( - string.Format( - InternalUsageDiagnosticAnalyzer.MessageFormat, - "Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices"), - diagnostic.GetMessage()); - - var span = diagnostic.Location.SourceSpan; - Assert.Equal("RelationalServices", source[span.Start..span.End]); - } - - [ConditionalFact] - public async Task Warning_on_ef_internal_name_instantiation() + private async Task Test( + string source, + string expectedInternalApi, + string expectedDiagnosticSpan) { - var (diagnostics, source) = - await GetDiagnosticsAsync(@"new Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies(null, null);"); - var diagnostic = diagnostics.Single(); + var (diagnostics, fullSource) = await GetDiagnosticsAsync(source); + var diagnostic = Assert.Single(diagnostics); Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); Assert.Equal( - string.Format( - InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies"), + string.Format(InternalUsageDiagnosticAnalyzer.MessageFormat, expectedInternalApi), diagnostic.GetMessage()); var span = diagnostic.Location.SourceSpan; - Assert.Equal( - "new Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies(null, null)", source[span.Start..span.End]); + Assert.Equal(expectedDiagnosticSpan, fullSource[span.Start..span.End]); } - #endregion Attribute - protected override Task<(Diagnostic[], string)> GetDiagnosticsAsync(string source, params string[] extraUsings) => base.GetDiagnosticsAsync(source, extraUsings.Concat(new[] { "Microsoft.EntityFrameworkCore.Internal" }).ToArray()); } From 8488397d643211b3f9147f040ab3f632786761de Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 13:24:27 +0300 Subject: [PATCH 02/13] Detect subclassing via a symbol action instead of via syntax --- .../InternalUsageDiagnosticAnalyzer.cs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index bfba5cbaa61..3db2752a735 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -53,7 +53,7 @@ public override void Initialize(AnalysisContext context) OperationKind.VariableDeclaration, OperationKind.TypeOf); - context.RegisterSyntaxNodeAction(AnalyzeClassDeclaration, SyntaxKind.ClassDeclaration); + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); } private static void AnalyzeNode(OperationAnalysisContext context) @@ -186,16 +186,35 @@ private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOpera } } - private static void AnalyzeClassDeclaration(SyntaxNodeAnalysisContext context) + private static void AnalyzeSymbol(SymbolAnalysisContext context) { - var declarationSyntax = (ClassDeclarationSyntax)context.Node; + switch (context.Symbol) + { + case INamedTypeSymbol namedTypeSymbol: + AnalyzeNamedTypeSymbol(context, namedTypeSymbol); + break; + + default: + throw new ArgumentException($"Unexpected {nameof(ISymbol)}: {context.Symbol.GetType().Name}"); + } + } - if (context.SemanticModel.GetDeclaredSymbol(declarationSyntax)?.BaseType is ISymbol symbol - && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly) - && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)) - && declarationSyntax.BaseList?.Types.Count > 0) + private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol) + { + if (symbol.BaseType is ISymbol baseSymbol + && !Equals(baseSymbol.ContainingAssembly, context.Compilation.Assembly) + && (IsInInternalNamespace(baseSymbol) || HasInternalAttribute(baseSymbol))) { - context.ReportDiagnostic(Diagnostic.Create(_descriptor, declarationSyntax.BaseList.Types[0].GetLocation(), symbol)); + foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences) + { + var syntax = declaringSyntax.GetSyntax(); + if (syntax is ClassDeclarationSyntax classDeclarationSyntax + && classDeclarationSyntax.BaseList?.Types.Count > 0) + { + context.ReportDiagnostic(Diagnostic.Create(_descriptor, classDeclarationSyntax.BaseList.Types[0].GetLocation(), baseSymbol)); + } + // context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, baseSymbol)); + } } } From f44a136eb94073f405f9836579171f17c1d98b52 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 14:54:37 +0300 Subject: [PATCH 03/13] Depend on Microsoft.CodeAnalysis.CSharp.Workspaces 3.7.0 --- eng/Versions.props | 3 +++ src/EFCore.Analyzers/EFCore.Analyzers.csproj | 2 +- test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index cf04fb195b2..445fe82e749 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -28,4 +28,7 @@ 5.0.0-rc.1.20402.3 5.0.0-rc.1.20402.3 + + 3.7.0 + diff --git a/src/EFCore.Analyzers/EFCore.Analyzers.csproj b/src/EFCore.Analyzers/EFCore.Analyzers.csproj index 893e3c2475f..ba48e302c5f 100644 --- a/src/EFCore.Analyzers/EFCore.Analyzers.csproj +++ b/src/EFCore.Analyzers/EFCore.Analyzers.csproj @@ -15,7 +15,7 @@ - + diff --git a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj index 563ffcb2141..5714bd49de4 100644 --- a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj +++ b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj @@ -11,7 +11,7 @@ - + From 28d090c0814f2deb5231d5268b93292673136d20 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 14:57:33 +0300 Subject: [PATCH 04/13] Fix symbol comparison method --- src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index 3db2752a735..7df564626c5 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -96,7 +96,7 @@ private static void AnalyzeNode(OperationAnalysisContext context) private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symbol) { - if (Equals(symbol.ContainingAssembly, context.Compilation.Assembly)) + if ((object)symbol.ContainingAssembly == context.Compilation.Assembly) { // Skip all methods inside the same assembly - internal access is fine return; @@ -202,7 +202,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context) private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol) { if (symbol.BaseType is ISymbol baseSymbol - && !Equals(baseSymbol.ContainingAssembly, context.Compilation.Assembly) + && (object)baseSymbol.ContainingAssembly != context.Compilation.Assembly && (IsInInternalNamespace(baseSymbol) || HasInternalAttribute(baseSymbol))) { foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences) @@ -219,7 +219,7 @@ private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamed } private static bool IsTypeInternal(OperationAnalysisContext context, ISymbol symbol) - => !Equals(symbol.ContainingAssembly, context.Compilation.Assembly) + => (object)symbol.ContainingAssembly != context.Compilation.Assembly && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)); private static bool HasInternalAttribute(ISymbol symbol) From e8dd3f58f006107c1c417fd26816e6bd8bc1f611 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 14:41:46 +0300 Subject: [PATCH 05/13] Add analyzer release tracking As per https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md --- .../AnalyzerReleases.Shipped.md | 18 ++++++++++++++++++ .../AnalyzerReleases.Unshipped.md | 0 src/EFCore.Analyzers/EFCore.Analyzers.csproj | 5 +++++ 3 files changed, 23 insertions(+) create mode 100644 src/EFCore.Analyzers/AnalyzerReleases.Shipped.md create mode 100644 src/EFCore.Analyzers/AnalyzerReleases.Unshipped.md diff --git a/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md b/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000000..433bac626f2 --- /dev/null +++ b/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md @@ -0,0 +1,18 @@ +## Release 2.1.0 + +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +EF1000 | Usage | Warning | RawSqlStringInjectionDiagnosticAnalyzer, [Documentation](https://docs.microsoft.com/ef/core/querying/raw-sql) + +## Release 3.0.0 + +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +EF1001 | Usage | Warning | InternalUsageDiagnosticAnalyzer + +### Removed Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|-------------------- +EF1000 | Security | Disabled | RawSqlStringInjectionDiagnosticAnalyzer, [Documentation](https://docs.microsoft.com/ef/core/querying/raw-sql) \ No newline at end of file diff --git a/src/EFCore.Analyzers/AnalyzerReleases.Unshipped.md b/src/EFCore.Analyzers/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/EFCore.Analyzers/EFCore.Analyzers.csproj b/src/EFCore.Analyzers/EFCore.Analyzers.csproj index ba48e302c5f..86c654c8300 100644 --- a/src/EFCore.Analyzers/EFCore.Analyzers.csproj +++ b/src/EFCore.Analyzers/EFCore.Analyzers.csproj @@ -32,4 +32,9 @@ + + + + + From 80a452eaaac0e09d7b73cf096fb8789302bd89d0 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 15:23:26 +0300 Subject: [PATCH 06/13] Correct behavior for VB --- src/EFCore.Analyzers/EFCore.Analyzers.csproj | 1 + .../InternalUsageDiagnosticAnalyzer.cs | 64 ++++++++++++------- .../EFCore.Analyzers.Test.csproj | 1 + 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/EFCore.Analyzers/EFCore.Analyzers.csproj b/src/EFCore.Analyzers/EFCore.Analyzers.csproj index 86c654c8300..d1f0c17289d 100644 --- a/src/EFCore.Analyzers/EFCore.Analyzers.csproj +++ b/src/EFCore.Analyzers/EFCore.Analyzers.csproj @@ -16,6 +16,7 @@ + diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index 7df564626c5..f0cf37e6485 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -5,11 +5,12 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; +using CSharpSyntax = Microsoft.CodeAnalysis.CSharp.Syntax; +using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; + namespace Microsoft.EntityFrameworkCore { [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] @@ -125,16 +126,24 @@ private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symb void ReportDiagnostic(object messageArg) { - // For C# member access expressions, report a narrowed-down diagnostic, otherwise take the whole invocation. + // For certain member access expressions, report a narrowed-down diagnostic, otherwise take the whole invocation. var syntax = context.Operation.Syntax switch { - InvocationExpressionSyntax invocationSyntax - when invocationSyntax.Expression is MemberAccessExpressionSyntax memberAccessSyntax + CSharpSyntax.InvocationExpressionSyntax s + when s.Expression is CSharpSyntax.MemberAccessExpressionSyntax memberAccessSyntax => memberAccessSyntax.Name, - MemberAccessExpressionSyntax memberAccessSyntax + CSharpSyntax.MemberAccessExpressionSyntax s + => s.Name, + CSharpSyntax.ObjectCreationExpressionSyntax s + => s.Type, + + VBSyntax.InvocationExpressionSyntax s + when s.Expression is VBSyntax.MemberAccessExpressionSyntax memberAccessSyntax => memberAccessSyntax.Name, - ObjectCreationExpressionSyntax objectCreationSyntax - => objectCreationSyntax.Type, + VBSyntax.MemberAccessExpressionSyntax s + => s.Name, + VBSyntax.ObjectCreationExpressionSyntax s + => s.Type, _ => context.Operation.Syntax }; @@ -164,11 +173,13 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, { if (IsTypeInternal(context, declarator.Symbol.Type)) { - context.ReportDiagnostic( - Diagnostic.Create( - _descriptor, - ((VariableDeclarationSyntax)context.Operation.Syntax).Type.GetLocation(), - declarator.Symbol.Type)); + var syntax = context.Operation.Syntax switch + { + CSharpSyntax.VariableDeclarationSyntax s => s.Type, + // TODO: VB + _ => context.Operation.Syntax + }; + context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), declarator.Symbol.Type)); return; } } @@ -178,11 +189,14 @@ private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOpera { if (IsTypeInternal(context, typeOf.TypeOperand)) { - context.ReportDiagnostic( - Diagnostic.Create( - _descriptor, - ((TypeOfExpressionSyntax)context.Operation.Syntax).Type.GetLocation(), - typeOf.TypeOperand)); + var syntax = context.Operation.Syntax switch + { + CSharpSyntax.TypeOfExpressionSyntax s => s.Type, + VBSyntax.TypeOfExpressionSyntax s => s.Type, + _ => context.Operation.Syntax + }; + + context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), typeOf.TypeOperand)); } } @@ -207,13 +221,15 @@ private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamed { foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences) { - var syntax = declaringSyntax.GetSyntax(); - if (syntax is ClassDeclarationSyntax classDeclarationSyntax - && classDeclarationSyntax.BaseList?.Types.Count > 0) + var syntax = declaringSyntax.GetSyntax() switch { - context.ReportDiagnostic(Diagnostic.Create(_descriptor, classDeclarationSyntax.BaseList.Types[0].GetLocation(), baseSymbol)); - } - // context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, baseSymbol)); + CSharpSyntax.ClassDeclarationSyntax s when s.BaseList?.Types.Count > 0 + => s.BaseList.Types[0], + // TODO: VB + { } otherSyntax => otherSyntax + }; + + context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), baseSymbol)); } } } diff --git a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj index 5714bd49de4..20045b2a73e 100644 --- a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj +++ b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj @@ -12,6 +12,7 @@ + From b730e30ec75097cc5713d3976524068ddafc69e8 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 15:42:22 +0300 Subject: [PATCH 07/13] Implement some additional missing cases * Field, property and event declarations * Implemented interface --- .../InternalUsageDiagnosticAnalyzer.cs | 152 ++++++++++++------ .../InternalUsageDiagnosticAnalyzerTest.cs | 58 ++++++- 2 files changed, 156 insertions(+), 54 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index f0cf37e6485..d7244886dc3 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -54,7 +54,7 @@ public override void Initialize(AnalysisContext context) OperationKind.VariableDeclaration, OperationKind.TypeOf); - context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType, SymbolKind.Property, SymbolKind.Field, SymbolKind.Event); } private static void AnalyzeNode(OperationAnalysisContext context) @@ -113,42 +113,15 @@ private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symb case IEventSymbol _: if (HasInternalAttribute(symbol)) { - ReportDiagnostic(symbol.Name == ".ctor" ? (object)containingType : $"{containingType}.{symbol.Name}"); + ReportDiagnostic(context, symbol.Name == ".ctor" ? (object)containingType : $"{containingType}.{symbol.Name}"); return; } break; } - if (IsTypeInternal(context, containingType)) + if (IsInternal(context, containingType)) { - ReportDiagnostic(containingType); - } - - void ReportDiagnostic(object messageArg) - { - // For certain member access expressions, report a narrowed-down diagnostic, otherwise take the whole invocation. - var syntax = context.Operation.Syntax switch - { - CSharpSyntax.InvocationExpressionSyntax s - when s.Expression is CSharpSyntax.MemberAccessExpressionSyntax memberAccessSyntax - => memberAccessSyntax.Name, - CSharpSyntax.MemberAccessExpressionSyntax s - => s.Name, - CSharpSyntax.ObjectCreationExpressionSyntax s - => s.Type, - - VBSyntax.InvocationExpressionSyntax s - when s.Expression is VBSyntax.MemberAccessExpressionSyntax memberAccessSyntax - => memberAccessSyntax.Name, - VBSyntax.MemberAccessExpressionSyntax s - => s.Name, - VBSyntax.ObjectCreationExpressionSyntax s - => s.Type, - _ - => context.Operation.Syntax - }; - - context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), messageArg)); + ReportDiagnostic(context, containingType); } } @@ -157,7 +130,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, IInvocat // First check for any internal type parameters foreach (var a in invocation.TargetMethod.TypeArguments) { - if (IsTypeInternal(context, a)) + if (IsInternal(context, a)) { context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Operation.Syntax.GetLocation(), a)); } @@ -171,7 +144,7 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, { foreach (var declarator in variableDeclaration.Declarators) { - if (IsTypeInternal(context, declarator.Symbol.Type)) + if (IsInternal(context, declarator.Symbol.Type)) { var syntax = context.Operation.Syntax switch { @@ -187,16 +160,9 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOperation typeOf) { - if (IsTypeInternal(context, typeOf.TypeOperand)) + if (IsInternal(context, typeOf.TypeOperand)) { - var syntax = context.Operation.Syntax switch - { - CSharpSyntax.TypeOfExpressionSyntax s => s.Type, - VBSyntax.TypeOfExpressionSyntax s => s.Type, - _ => context.Operation.Syntax - }; - - context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), typeOf.TypeOperand)); + ReportDiagnostic(context, typeOf.TypeOperand); } } @@ -204,8 +170,20 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context) { switch (context.Symbol) { - case INamedTypeSymbol namedTypeSymbol: - AnalyzeNamedTypeSymbol(context, namedTypeSymbol); + case INamedTypeSymbol symbol: + AnalyzeNamedTypeSymbol(context, symbol); + break; + + case IFieldSymbol symbol: + AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type); + break; + + case IPropertySymbol symbol: + AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type); + break; + + case IEventSymbol symbol: + AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type); break; default: @@ -215,26 +193,96 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context) private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol) { - if (symbol.BaseType is ISymbol baseSymbol - && (object)baseSymbol.ContainingAssembly != context.Compilation.Assembly - && (IsInInternalNamespace(baseSymbol) || HasInternalAttribute(baseSymbol))) + if (symbol.BaseType is ITypeSymbol baseSymbol + && IsInternal(context, baseSymbol)) { foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences) { - var syntax = declaringSyntax.GetSyntax() switch + var location = declaringSyntax.GetSyntax() switch { CSharpSyntax.ClassDeclarationSyntax s when s.BaseList?.Types.Count > 0 - => s.BaseList.Types[0], + => s.BaseList.Types[0].GetLocation(), // TODO: VB - { } otherSyntax => otherSyntax + { } otherSyntax => otherSyntax.GetLocation() }; - context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), baseSymbol)); + context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, baseSymbol)); + } + } + + foreach (var iface in symbol.Interfaces.Where(i => IsInternal(context, i))) + { + foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences) + { + var location = declaringSyntax.GetSyntax() switch + { + // TODO: Report the location of the internal interface + CSharpSyntax.ClassDeclarationSyntax s => s.Identifier.GetLocation(), + // TODO: VB + { } otherSyntax => otherSyntax.GetLocation() + }; + + context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, iface)); } } } - private static bool IsTypeInternal(OperationAnalysisContext context, ISymbol symbol) + private static void AnalyzeMemberDeclarationTypeSymbol( + SymbolAnalysisContext context, + ISymbol declarationSymbol, + ITypeSymbol typeSymbol) + { + if (IsInternal(context, typeSymbol)) + { + foreach (var declaringSyntax in declarationSymbol.DeclaringSyntaxReferences) + { + ReportDiagnostic(context, declaringSyntax.GetSyntax(), typeSymbol); + } + } + } + + private static void ReportDiagnostic(OperationAnalysisContext context, object messageArg) + => context.ReportDiagnostic(Diagnostic.Create(_descriptor, NarrowDownSyntax(context.Operation.Syntax).GetLocation(), messageArg)); + + private static void ReportDiagnostic(SymbolAnalysisContext context, SyntaxNode syntax, object messageArg) + => context.ReportDiagnostic(Diagnostic.Create(_descriptor, NarrowDownSyntax(syntax).GetLocation(), messageArg)); + + /// + /// Given a syntax node, pattern matches some known types and returns a narrowed-down node for the type syntax which + /// should be reported in diagnostics. + /// + private static SyntaxNode NarrowDownSyntax(SyntaxNode syntax) + => syntax switch + { + CSharpSyntax.InvocationExpressionSyntax s + when s.Expression is CSharpSyntax.MemberAccessExpressionSyntax memberAccessSyntax + => memberAccessSyntax.Name, + CSharpSyntax.MemberAccessExpressionSyntax s => s.Name, + CSharpSyntax.ObjectCreationExpressionSyntax s => s.Type, + CSharpSyntax.PropertyDeclarationSyntax s => s.Type, + CSharpSyntax.VariableDeclaratorSyntax declarator + => declarator.Parent is CSharpSyntax.VariableDeclarationSyntax declaration + ? declaration.Type + : (SyntaxNode)declarator, + CSharpSyntax.TypeOfExpressionSyntax s => s.Type, + + VBSyntax.InvocationExpressionSyntax s + when s.Expression is VBSyntax.MemberAccessExpressionSyntax memberAccessSyntax + => memberAccessSyntax.Name, + VBSyntax.MemberAccessExpressionSyntax s => s.Name, + VBSyntax.ObjectCreationExpressionSyntax s => s.Type, + VBSyntax.TypeOfExpressionSyntax s => s.Type, + + // TODO: Complete VB syntax handling corresponding to the C# ones above + + _ => syntax + }; + + private static bool IsInternal(SymbolAnalysisContext context, ITypeSymbol symbol) + => (object)symbol.ContainingAssembly != context.Compilation.Assembly + && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)); + + private static bool IsInternal(OperationAnalysisContext context, ITypeSymbol symbol) => (object)symbol.ContainingAssembly != context.Compilation.Assembly && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)); diff --git a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs index 2a7934727eb..f8daeb7b8c0 100644 --- a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs +++ b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs @@ -29,7 +29,7 @@ public Task Instantiation_on_type_in_internal_namespace() "CoreSingletonOptions"); [ConditionalFact] - public async Task Subclassing_type_in_internal_namespace() + public async Task Base_type() { var source = @" class MyClass : Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter { @@ -67,6 +67,22 @@ class MyClass : Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalPara }); } + [ConditionalFact] + public Task Implemented_interface() + => TestFullSource( + @" +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Internal; + +class MyClass : IDbContextPool { + public IDbContextPoolable Rent() => default; + public void Return(IDbContextPoolable context) {} + public ValueTask ReturnAsync(IDbContextPoolable context, CancellationToken cancellationToken = default) => default; +}", + "Microsoft.EntityFrameworkCore.Internal.IDbContextPool", + "MyClass"); + [ConditionalFact] public Task Access_property_with_internal_attribute() => Test( @@ -82,7 +98,7 @@ public Task Instantiation_with_ctor_with_internal_attribute() "Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies"); [ConditionalFact] - public Task Variable_declaration() + public Task Local_variable_declaration() => Test( "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager state = null;", "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", @@ -105,6 +121,26 @@ public Task Typeof() "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); + [ConditionalFact] + public Task Field_declaration() + => TestFullSource( + @" +class MyClass { + private readonly Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager StateManager; +}", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); + + [ConditionalFact] + public Task Property_declaration() + => TestFullSource( + @" +class MyClass { + private Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager StateManager { get; set; } +}", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); + [ConditionalFact] public async Task No_warning_on_non_internal() => await AssertNoDiagnostics( @@ -157,6 +193,24 @@ private async Task Test( Assert.Equal(expectedDiagnosticSpan, fullSource[span.Start..span.End]); } + private async Task TestFullSource( + string fullSource, + string expectedInternalApi, + string expectedDiagnosticSpan) + { + var diagnostics = await GetDiagnosticsFullSourceAsync(fullSource); + var diagnostic = Assert.Single(diagnostics); + + Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id); + Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); + Assert.Equal( + string.Format(InternalUsageDiagnosticAnalyzer.MessageFormat, expectedInternalApi), + diagnostic.GetMessage()); + + var span = diagnostic.Location.SourceSpan; + Assert.Equal(expectedDiagnosticSpan, fullSource[span.Start..span.End]); + } + protected override Task<(Diagnostic[], string)> GetDiagnosticsAsync(string source, params string[] extraUsings) => base.GetDiagnosticsAsync(source, extraUsings.Concat(new[] { "Microsoft.EntityFrameworkCore.Internal" }).ToArray()); } From 3514b515107381159797bf1582d460c93fc1314e Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 16:41:06 +0300 Subject: [PATCH 08/13] Suppress one more new warning --- .../Migrations/Internal/MigrationsModelDiffer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index 5be1a9b9f56..caaf1b1f29a 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -139,7 +139,9 @@ public MigrationsModelDiffer( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// +#pragma warning disable EF1001 // Internal EF Core API usage. protected virtual IChangeDetector ChangeDetector { get; } +#pragma warning restore EF1001 // Internal EF Core API usage. /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to From b27e8a776a0dbe2badcfc4092dbe60cd29ff9843 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 11 Aug 2020 20:31:25 +0300 Subject: [PATCH 09/13] Align Microsoft.CodeAnalysis versions across the solution --- benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj | 2 +- test/EFCore.Design.Tests/EFCore.Design.Tests.csproj | 2 +- test/ef.Tests/ef.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj b/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj index 85cabbef025..57aeca4b90b 100644 --- a/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj +++ b/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj @@ -12,7 +12,7 @@ - + diff --git a/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj b/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj index b25cbbd3a15..78604fd4692 100644 --- a/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj +++ b/test/EFCore.Design.Tests/EFCore.Design.Tests.csproj @@ -23,7 +23,7 @@ - + diff --git a/test/ef.Tests/ef.Tests.csproj b/test/ef.Tests/ef.Tests.csproj index 7ae6986d93c..85631f36ce4 100644 --- a/test/ef.Tests/ef.Tests.csproj +++ b/test/ef.Tests/ef.Tests.csproj @@ -18,7 +18,7 @@ - + From 2fcd1691c74d1357535208b7f48f80fc48476242 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Thu, 13 Aug 2020 14:31:01 +0300 Subject: [PATCH 10/13] Address review comments --- .../InternalUsageDiagnosticAnalyzer.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index d7244886dc3..cd8f9058014 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -76,19 +76,15 @@ private static void AnalyzeNode(OperationAnalysisContext context) case OperationKind.ObjectCreation: AnalyzeMember(context, ((IObjectCreationOperation)context.Operation).Constructor); break; - case OperationKind.Invocation: AnalyzeInvocation(context, (IInvocationOperation)context.Operation); break; - case OperationKind.VariableDeclaration: AnalyzeVariableDeclaration(context, ((IVariableDeclarationOperation)context.Operation)); break; - case OperationKind.TypeOf: AnalyzeTypeof(context, ((ITypeOfOperation)context.Operation)); break; - default: throw new ArgumentException($"Unexpected {nameof(OperationKind)}: {context.Operation.Kind}"); } @@ -149,7 +145,6 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, var syntax = context.Operation.Syntax switch { CSharpSyntax.VariableDeclarationSyntax s => s.Type, - // TODO: VB _ => context.Operation.Syntax }; context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), declarator.Symbol.Type)); @@ -202,7 +197,6 @@ private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamed { CSharpSyntax.ClassDeclarationSyntax s when s.BaseList?.Types.Count > 0 => s.BaseList.Types[0].GetLocation(), - // TODO: VB { } otherSyntax => otherSyntax.GetLocation() }; @@ -216,9 +210,7 @@ CSharpSyntax.ClassDeclarationSyntax s when s.BaseList?.Types.Count > 0 { var location = declaringSyntax.GetSyntax() switch { - // TODO: Report the location of the internal interface CSharpSyntax.ClassDeclarationSyntax s => s.Identifier.GetLocation(), - // TODO: VB { } otherSyntax => otherSyntax.GetLocation() }; @@ -273,8 +265,6 @@ when s.Expression is VBSyntax.MemberAccessExpressionSyntax memberAccessSyntax VBSyntax.ObjectCreationExpressionSyntax s => s.Type, VBSyntax.TypeOfExpressionSyntax s => s.Type, - // TODO: Complete VB syntax handling corresponding to the C# ones above - _ => syntax }; From f8c7056b9427daacf57dcfb8786f6e46043f097c Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Thu, 13 Aug 2020 15:57:56 +0300 Subject: [PATCH 11/13] Analyze method declarations --- .../InternalUsageDiagnosticAnalyzer.cs | 48 ++++++++++++++++++- .../InternalUsageDiagnosticAnalyzerTest.cs | 33 ++++++++++--- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index cd8f9058014..001eeae3821 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -54,7 +54,12 @@ public override void Initialize(AnalysisContext context) OperationKind.VariableDeclaration, OperationKind.TypeOf); - context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType, SymbolKind.Property, SymbolKind.Field, SymbolKind.Event); + context.RegisterSymbolAction(AnalyzeSymbol, + SymbolKind.NamedType, + SymbolKind.Method, + SymbolKind.Property, + SymbolKind.Field, + SymbolKind.Event); } private static void AnalyzeNode(OperationAnalysisContext context) @@ -169,6 +174,10 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context) AnalyzeNamedTypeSymbol(context, symbol); break; + case IMethodSymbol symbol: + AnalyzeMethodTypeSymbol(context, symbol); + break; + case IFieldSymbol symbol: AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type); break; @@ -219,6 +228,43 @@ CSharpSyntax.ClassDeclarationSyntax s when s.BaseList?.Types.Count > 0 } } + private static void AnalyzeMethodTypeSymbol(SymbolAnalysisContext context, IMethodSymbol symbol) + { + if (symbol.MethodKind == MethodKind.PropertyGet + || symbol.MethodKind == MethodKind.PropertySet) + { + // Property getters/setters are handled via IPropertySymbol + return; + } + if (IsInternal(context, symbol.ReturnType)) + { + foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences) + { + var location = declaringSyntax.GetSyntax() switch + { + CSharpSyntax.MethodDeclarationSyntax s => s.ReturnType.GetLocation(), + { } otherSyntax => otherSyntax.GetLocation() + }; + + context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, symbol.ReturnType)); + } + } + + foreach (var paramSymbol in symbol.Parameters.Where(ps => IsInternal(context, ps.Type))) + { + foreach (var declaringSyntax in paramSymbol.DeclaringSyntaxReferences) + { + var location = declaringSyntax.GetSyntax() switch + { + CSharpSyntax.ParameterSyntax s when s.Type != null => s.Type.GetLocation(), + { } otherSyntax => otherSyntax.GetLocation() + }; + + context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, paramSymbol.Type)); + } + } + } + private static void AnalyzeMemberDeclarationTypeSymbol( SymbolAnalysisContext context, ISymbol declarationSymbol, diff --git a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs index f8daeb7b8c0..fe15fac6eac 100644 --- a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs +++ b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs @@ -71,16 +71,15 @@ class MyClass : Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalPara public Task Implemented_interface() => TestFullSource( @" -using System.Threading; -using System.Threading.Tasks; +using System; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; -class MyClass : IDbContextPool { - public IDbContextPoolable Rent() => default; - public void Return(IDbContextPoolable context) {} - public ValueTask ReturnAsync(IDbContextPoolable context, CancellationToken cancellationToken = default) => default; +class MyClass : IDbSetSource { + public object Create(DbContext context, Type type) => null; + public object Create(DbContext context, string name, Type type) => null; }", - "Microsoft.EntityFrameworkCore.Internal.IDbContextPool", + "Microsoft.EntityFrameworkCore.Internal.IDbSetSource", "MyClass"); [ConditionalFact] @@ -141,6 +140,26 @@ class MyClass { "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); + [ConditionalFact] + public Task Method_declaration_return_type() + => TestFullSource( + @" +class MyClass { + private Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager Foo() => null; +}", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); + + [ConditionalFact] + public Task Method_declaration_parameter() + => TestFullSource( + @" +class MyClass { + private void Foo(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager stateManager) {} +}", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager", + "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager"); + [ConditionalFact] public async Task No_warning_on_non_internal() => await AssertNoDiagnostics( From 68108d2dbd13589b5acef1f4e74e652040c07e82 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Thu, 13 Aug 2020 15:57:52 +0300 Subject: [PATCH 12/13] Address review comments --- .../InternalUsageDiagnosticAnalyzer.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs index 001eeae3821..d63c4e5ca0b 100644 --- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs +++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs @@ -106,18 +106,10 @@ private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symb var containingType = symbol.ContainingType; - switch (symbol) + if (HasInternalAttribute(symbol)) { - case IMethodSymbol _: - case IFieldSymbol _: - case IPropertySymbol _: - case IEventSymbol _: - if (HasInternalAttribute(symbol)) - { - ReportDiagnostic(context, symbol.Name == ".ctor" ? (object)containingType : $"{containingType}.{symbol.Name}"); - return; - } - break; + ReportDiagnostic(context, symbol.Name == ".ctor" ? (object)containingType : $"{containingType}.{symbol.Name}"); + return; } if (IsInternal(context, containingType)) From 6d185f55754e6c7bb72a814bb14ceb314e74c422 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Sat, 15 Aug 2020 19:32:42 +0300 Subject: [PATCH 13/13] Suppress more newly-discovered violations --- ...or.CosmosProjectionBindingRemovingExpressionVisitorBase.cs | 4 ++++ .../Migrations/Internal/MigrationsModelDiffer.cs | 2 ++ .../Metadata/Internal/SqlServerAnnotationProvider.cs | 2 ++ .../Internal/SqlServerMigrationsAnnotationProvider.cs | 2 ++ .../Metadata/Internal/SqliteAnnotationProvider.cs | 2 ++ 5 files changed, 12 insertions(+) diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs index 74d8484fe88..27b4c5bac0e 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs @@ -397,7 +397,9 @@ private static readonly MethodInfo _includeReferenceMethodInfo .GetDeclaredMethod(nameof(IncludeReference)); private static void IncludeReference( +#pragma warning disable EF1001 // Internal EF Core API usage. InternalEntityEntry entry, +#pragma warning restore EF1001 // Internal EF Core API usage. object entity, IEntityType entityType, TIncludedEntity relatedEntity, @@ -440,7 +442,9 @@ private static readonly MethodInfo _includeCollectionMethodInfo .GetDeclaredMethod(nameof(IncludeCollection)); private static void IncludeCollection( +#pragma warning disable EF1001 // Internal EF Core API usage. InternalEntityEntry entry, +#pragma warning restore EF1001 // Internal EF Core API usage. object entity, IEntityType entityType, IEnumerable relatedEntities, diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index caaf1b1f29a..cc3cb91c78e 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -82,7 +82,9 @@ public class MigrationsModelDiffer : IMigrationsModelDiffer public MigrationsModelDiffer( [NotNull] IRelationalTypeMappingSource typeMappingSource, [NotNull] IMigrationsAnnotationProvider migrationsAnnotations, +#pragma warning disable EF1001 // Internal EF Core API usage. [NotNull] IChangeDetector changeDetector, +#pragma warning restore EF1001 // Internal EF Core API usage. [NotNull] IUpdateAdapterFactory updateAdapterFactory, [NotNull] CommandBatchPreparerDependencies commandBatchPreparerDependencies) { diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs index c13fbcbb96b..0f7d4f6ddde 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs @@ -31,7 +31,9 @@ public class SqlServerAnnotationProvider : RelationalAnnotationProvider /// Initializes a new instance of this class. /// /// Parameter object containing dependencies for this service. +#pragma warning disable EF1001 // Internal EF Core API usage. public SqlServerAnnotationProvider([NotNull] RelationalAnnotationProviderDependencies dependencies) +#pragma warning restore EF1001 // Internal EF Core API usage. : base(dependencies) { } diff --git a/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationsAnnotationProvider.cs b/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationsAnnotationProvider.cs index a6b5cc80599..76a0b5888a1 100644 --- a/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationsAnnotationProvider.cs +++ b/src/EFCore.SqlServer/Migrations/Internal/SqlServerMigrationsAnnotationProvider.cs @@ -31,7 +31,9 @@ public class SqlServerMigrationsAnnotationProvider : MigrationsAnnotationProvide /// Initializes a new instance of this class. /// /// Parameter object containing dependencies for this service. +#pragma warning disable EF1001 // Internal EF Core API usage. public SqlServerMigrationsAnnotationProvider([NotNull] MigrationsAnnotationProviderDependencies dependencies) +#pragma warning restore EF1001 // Internal EF Core API usage. : base(dependencies) { } diff --git a/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs b/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs index 6050b86eb9a..959090f53e0 100644 --- a/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs +++ b/src/EFCore.Sqlite.Core/Metadata/Internal/SqliteAnnotationProvider.cs @@ -33,7 +33,9 @@ public class SqliteAnnotationProvider : RelationalAnnotationProvider /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// +#pragma warning disable EF1001 // Internal EF Core API usage. public SqliteAnnotationProvider([NotNull] RelationalAnnotationProviderDependencies dependencies) +#pragma warning restore EF1001 // Internal EF Core API usage. : base(dependencies) { }