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/eng/Versions.props b/eng/Versions.props
index df73e37d4ac..3ce1995e5f7 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -28,4 +28,7 @@
5.0.0-rc.1.20409.3
5.0.0-rc.1.20409.3
+
+ 3.7.0
+
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 893e3c2475f..d1f0c17289d 100644
--- a/src/EFCore.Analyzers/EFCore.Analyzers.csproj
+++ b/src/EFCore.Analyzers/EFCore.Analyzers.csproj
@@ -15,7 +15,8 @@
-
+
+
@@ -32,4 +33,9 @@
+
+
+
+
+
diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
index eb4dd635156..d63c4e5ca0b 100644
--- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
+++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
@@ -5,21 +5,23 @@
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)]
+ [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 +42,282 @@ 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.RegisterSymbolAction(AnalyzeSymbol,
+ SymbolKind.NamedType,
+ SymbolKind.Method,
+ SymbolKind.Property,
+ SymbolKind.Field,
+ SymbolKind.Event);
+ }
+
+ private static void AnalyzeNode(OperationAnalysisContext context)
+ {
+ switch (context.Operation.Kind)
+ {
+ 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 ((object)symbol.ContainingAssembly == context.Compilation.Assembly)
+ {
+ // Skip all methods inside the same assembly - internal access is fine
+ return;
+ }
+
+ var containingType = symbol.ContainingType;
+
+ if (HasInternalAttribute(symbol))
+ {
+ ReportDiagnostic(context, symbol.Name == ".ctor" ? (object)containingType : $"{containingType}.{symbol.Name}");
+ return;
+ }
+
+ if (IsInternal(context, containingType))
+ {
+ ReportDiagnostic(context, containingType);
+ }
}
- private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ private static void AnalyzeInvocation(OperationAnalysisContext context, IInvocationOperation invocation)
{
- switch (context.Node)
+ // First check for any internal type parameters
+ foreach (var a in invocation.TargetMethod.TypeArguments)
{
- case MemberAccessExpressionSyntax memberAccessSyntax:
+ if (IsInternal(context, a))
{
- 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, memberAccessSyntax.Name.GetLocation(), $"{containingType}.{symbol.Name}"));
- return;
- }
-
- if (IsInInternalNamespace(containingType)
- || HasInternalAttribute(containingType))
- {
- context.ReportDiagnostic(Diagnostic.Create(_descriptor, memberAccessSyntax.Name.GetLocation(), containingType));
- return;
- }
- }
+ context.ReportDiagnostic(Diagnostic.Create(_descriptor, context.Operation.Syntax.GetLocation(), a));
+ }
+ }
+
+ // 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 (IsInternal(context, declarator.Symbol.Type))
+ {
+ var syntax = context.Operation.Syntax switch
+ {
+ CSharpSyntax.VariableDeclarationSyntax s => s.Type,
+ _ => context.Operation.Syntax
+ };
+ context.ReportDiagnostic(Diagnostic.Create(_descriptor, syntax.GetLocation(), declarator.Symbol.Type));
return;
}
+ }
+ }
+
+ private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOperation typeOf)
+ {
+ if (IsInternal(context, typeOf.TypeOperand))
+ {
+ ReportDiagnostic(context, typeOf.TypeOperand);
+ }
+ }
+
+ private static void AnalyzeSymbol(SymbolAnalysisContext context)
+ {
+ switch (context.Symbol)
+ {
+ case INamedTypeSymbol symbol:
+ AnalyzeNamedTypeSymbol(context, symbol);
+ break;
+
+ case IMethodSymbol symbol:
+ AnalyzeMethodTypeSymbol(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:
+ throw new ArgumentException($"Unexpected {nameof(ISymbol)}: {context.Symbol.GetType().Name}");
+ }
+ }
+
+ private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol)
+ {
+ if (symbol.BaseType is ITypeSymbol baseSymbol
+ && IsInternal(context, baseSymbol))
+ {
+ foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences)
+ {
+ var location = declaringSyntax.GetSyntax() switch
+ {
+ CSharpSyntax.ClassDeclarationSyntax s when s.BaseList?.Types.Count > 0
+ => s.BaseList.Types[0].GetLocation(),
+ { } otherSyntax => otherSyntax.GetLocation()
+ };
+
+ context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, baseSymbol));
+ }
+ }
- case ObjectCreationExpressionSyntax creationSyntax:
+ foreach (var iface in symbol.Interfaces.Where(i => IsInternal(context, i)))
+ {
+ foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences)
{
- if (context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol is ISymbol symbol
- && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly))
+ var location = declaringSyntax.GetSyntax() switch
{
- 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;
- }
- }
+ CSharpSyntax.ClassDeclarationSyntax s => s.Identifier.GetLocation(),
+ { } otherSyntax => otherSyntax.GetLocation()
+ };
- return;
+ context.ReportDiagnostic(Diagnostic.Create(_descriptor, location, iface));
}
+ }
+ }
- case ClassDeclarationSyntax declarationSyntax:
+ 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)
{
- if (context.SemanticModel.GetDeclaredSymbol(declarationSyntax)?.BaseType is ISymbol symbol
- && !Equals(symbol.ContainingAssembly, context.Compilation.Assembly)
- && (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol))
- && declarationSyntax.BaseList?.Types.Count > 0)
+ var location = declaringSyntax.GetSyntax() switch
{
- context.ReportDiagnostic(Diagnostic.Create(_descriptor, declarationSyntax.BaseList.Types[0].GetLocation(), symbol));
- }
+ CSharpSyntax.MethodDeclarationSyntax s => s.ReturnType.GetLocation(),
+ { } otherSyntax => otherSyntax.GetLocation()
+ };
- return;
+ 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,
+ 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,
+
+ _ => 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));
+
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 c0d36d32986..f430be2d4c0 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(
@@ -394,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,
@@ -437,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.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs
index 0327bd298f2..1ec832b0e78 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs
@@ -259,10 +259,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)
{
@@ -270,17 +270,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 e0e1fea226b..d3bf4cc1526 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
@@ -82,13 +82,17 @@ 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)
{
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));
@@ -137,7 +141,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
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)
{
}
diff --git a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj
index 563ffcb2141..20045b2a73e 100644
--- a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj
+++ b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Test.csproj
@@ -11,7 +11,8 @@
-
+
+
diff --git a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs
index f7b4acc1950..fe15fac6eac 100644
--- a/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs
+++ b/test/EFCore.Analyzers.Tests/InternalUsageDiagnosticAnalyzerTest.cs
@@ -15,74 +15,161 @@ 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 Base_type()
{
- 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 Implemented_interface()
+ => TestFullSource(
+ @"
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Internal;
- Assert.Equal(InternalUsageDiagnosticAnalyzer.Id, diagnostic.Id);
- Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity);
- Assert.Equal(
- string.Format(InternalUsageDiagnosticAnalyzer.MessageFormat, "Microsoft.EntityFrameworkCore.Internal.CoreSingletonOptions"),
- diagnostic.GetMessage());
+class MyClass : IDbSetSource {
+ public object Create(DbContext context, Type type) => null;
+ public object Create(DbContext context, string name, Type type) => null;
+}",
+ "Microsoft.EntityFrameworkCore.Internal.IDbSetSource",
+ "MyClass");
- var span = diagnostic.Location.SourceSpan;
- Assert.Equal("CoreSingletonOptions", source[span.Start..span.End]);
- }
+ [ConditionalFact]
+ public Task Access_property_with_internal_attribute()
+ => Test(
+ "var x = Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices.Count;",
+ "Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices",
+ "RelationalServices");
[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 Instantiation_with_ctor_with_internal_attribute()
+ => Test(
+ "new Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies(null, null);",
+ "Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies",
+ "Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies");
- var diagnostics = await GetDiagnosticsFullSourceAsync(source);
- var diagnostic = diagnostics.Single();
+ [ConditionalFact]
+ public Task Local_variable_declaration()
+ => Test(
+ "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager state = null;",
+ "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager",
+ "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager");
- 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 Generic_type_parameter_in_method_call()
+ => Test(
+ @"
+void SomeGenericMethod() {}
- var span = diagnostic.Location.SourceSpan;
- Assert.Equal(
- "Microsoft.EntityFrameworkCore.Storage.Internal.RawRelationalParameter",
- source[span.Start..span.End]);
- }
+SomeGenericMethod();",
+ "Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager",
+ "SomeGenericMethod()");
+
+ [ConditionalFact]
+ public Task Typeof()
+ => Test(
+ "var t = typeof(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IStateManager);",
+ "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 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 async Task No_warning_on_ef_internal_namespace_in_same_assembly()
+ 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(
+ @"
+var a = new Microsoft.EntityFrameworkCore.Infrastructure.Annotatable();
+var x = a.GetAnnotations();
+");
+
+ [ConditionalFact]
+ public async Task No_warning_in_same_assembly()
{
var diagnostics = await GetDiagnosticsFullSourceAsync(
@"
@@ -107,51 +194,42 @@ public void Main(string[] args) {
Assert.Empty(diagnostics);
}
- #endregion Namespace
-
- #region Attribute
-
- [ConditionalFact]
- public async Task Warning_on_ef_internal_attribute_property_access()
+ private async Task Test(
+ string source,
+ string expectedInternalApi,
+ string expectedDiagnosticSpan)
{
- 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();
+ 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.Infrastructure.EntityFrameworkRelationalServicesBuilder.RelationalServices"),
+ string.Format(InternalUsageDiagnosticAnalyzer.MessageFormat, expectedInternalApi),
diagnostic.GetMessage());
var span = diagnostic.Location.SourceSpan;
- Assert.Equal("RelationalServices", source[span.Start..span.End]);
+ Assert.Equal(expectedDiagnosticSpan, fullSource[span.Start..span.End]);
}
- [ConditionalFact]
- public async Task Warning_on_ef_internal_name_instantiation()
+ private async Task TestFullSource(
+ string fullSource,
+ string expectedInternalApi,
+ string expectedDiagnosticSpan)
{
- var (diagnostics, source) =
- await GetDiagnosticsAsync(@"new Microsoft.EntityFrameworkCore.Update.UpdateSqlGeneratorDependencies(null, null);");
- var diagnostic = diagnostics.Single();
+ 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, "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());
}
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 @@
-
+