Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule S2077: add support for Entity Framework Core new overloads #5296

Merged
merged 3 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ protected override Location SecondaryLocationForExpression(ExpressionSyntax node
return Location.None;
}

if (node.Parent is EqualsValueClauseSyntax equalsValueClause
&& equalsValueClause.Parent is VariableDeclaratorSyntax declarationSyntax)
if (node.Parent is EqualsValueClauseSyntax {Parent: VariableDeclaratorSyntax declarationSyntax})
{
return declarationSyntax.Identifier.GetLocation();
}
Expand All @@ -95,7 +94,7 @@ private static bool AllConstants(IEnumerable<ArgumentSyntax> arguments, Semantic
private static bool IsConcatenationOfConstants(BinaryExpressionSyntax binaryExpression, SemanticModel semanticModel)
{
System.Diagnostics.Debug.Assert(binaryExpression.IsKind(SyntaxKind.AddExpression), "Binary expression should be of syntax kind add expression.");
if ((semanticModel.GetTypeInfo(binaryExpression).Type is ITypeSymbol) && binaryExpression.Right.HasConstantValue(semanticModel))
if ((semanticModel.GetTypeInfo(binaryExpression).Type != null) && binaryExpression.Right.HasConstantValue(semanticModel))
{
var nestedLeft = binaryExpression.Left;
var nestedBinary = nestedLeft as BinaryExpressionSyntax;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public abstract class ExecutingSqlQueriesBase<TSyntaxKind, TExpressionSyntax, TI
where TExpressionSyntax : SyntaxNode
where TIdentifierNameSyntax : SyntaxNode
{
protected const string DiagnosticId = "S2077";
private const string DiagnosticId = "S2077";
private const string AssignmentWithFormattingMessage = "SQL Query is dynamically formatted and assigned to {0}.";
private const string AssignmentMessage = "SQL query is assigned to {0}.";
private const string MessageFormat = "Make sure using a dynamically formatted SQL query is safe here.";
Expand All @@ -38,11 +38,12 @@ public abstract class ExecutingSqlQueriesBase<TSyntaxKind, TExpressionSyntax, TI

private readonly KnownType[] constructorsForFirstArgument =
{
KnownType.Microsoft_EntityFrameworkCore_RawSqlString,
KnownType.System_Data_SqlClient_SqlCommand,
KnownType.System_Data_SqlClient_SqlDataAdapter,
KnownType.System_Data_Odbc_OdbcCommand,
KnownType.System_Data_Odbc_OdbcDataAdapter,
KnownType.System_Data_SqlClient_SqlCommand,
KnownType.System_Data_SqlClient_SqlDataAdapter,
KnownType.System_Data_Sqlite_SqliteCommand,
KnownType.System_Data_Sqlite_SQLiteDataAdapter,
KnownType.System_Data_SqlServerCe_SqlCeCommand,
KnownType.System_Data_SqlServerCe_SqlCeDataAdapter,
KnownType.System_Data_OracleClient_OracleCommand,
Expand All @@ -51,52 +52,58 @@ public abstract class ExecutingSqlQueriesBase<TSyntaxKind, TExpressionSyntax, TI
KnownType.MySql_Data_MySqlClient_MySqlDataAdapter,
KnownType.MySql_Data_MySqlClient_MySqlScript,
KnownType.Microsoft_Data_Sqlite_SqliteCommand,
KnownType.System_Data_Sqlite_SqliteCommand,
KnownType.System_Data_Sqlite_SQLiteDataAdapter,
KnownType.Microsoft_EntityFrameworkCore_RawSqlString
};

private readonly KnownType[] constructorsForSecondArgument =
{
KnownType.MySql_Data_MySqlClient_MySqlScript,
KnownType.MySql_Data_MySqlClient_MySqlScript
};

private readonly MemberDescriptor[] invocationsForFirstTwoArguments =
{
new MemberDescriptor(KnownType.Microsoft_EntityFrameworkCore_RelationalDatabaseFacadeExtensions, "ExecuteSqlCommandAsync"),
new MemberDescriptor(KnownType.Microsoft_EntityFrameworkCore_RelationalDatabaseFacadeExtensions, "ExecuteSqlCommand"),
new MemberDescriptor(KnownType.Microsoft_EntityFrameworkCore_RelationalQueryableExtensions, "FromSql"),
new(KnownType.Microsoft_EntityFrameworkCore_RelationalDatabaseFacadeExtensions, "ExecuteSqlCommandAsync"),
new(KnownType.Microsoft_EntityFrameworkCore_RelationalDatabaseFacadeExtensions, "ExecuteSqlCommand"),
new(KnownType.Microsoft_EntityFrameworkCore_RelationalQueryableExtensions, "FromSql")
};

private readonly MemberDescriptor[] invocationsForFirstTwoArgumentsAfterV2 =
{
new(KnownType.Microsoft_EntityFrameworkCore_RelationalDatabaseFacadeExtensions, "ExecuteSqlRaw"),
new(KnownType.Microsoft_EntityFrameworkCore_RelationalDatabaseFacadeExtensions, "ExecuteSqlRawAsync"),
new(KnownType.Microsoft_EntityFrameworkCore_RelationalQueryableExtensions, "FromSqlRaw")
};

private readonly MemberDescriptor[] invocationsForFirstArgument =
{
new MemberDescriptor(KnownType.System_Data_Sqlite_SqliteCommand, "Execute"),
new(KnownType.System_Data_Sqlite_SqliteCommand, "Execute")
};

private readonly MemberDescriptor[] invocationsForSecondArgument =
{
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDataRow"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDataRowAsync"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDataset"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDatasetAsync"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteNonQuery"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteNonQueryAsync"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteReader"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteReaderAsync"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteScalar"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteScalarAsync"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "UpdateDataSet"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlHelper, "UpdateDataSetAsync"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDataRow"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDataRowAsync"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDataset"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteDatasetAsync"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteNonQuery"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteNonQueryAsync"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteReader"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteReaderAsync"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteScalar"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "ExecuteScalarAsync"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "UpdateDataSet"),
new(KnownType.MySql_Data_MySqlClient_MySqlHelper, "UpdateDataSetAsync")
};

private readonly MemberDescriptor[] properties =
{
new MemberDescriptor(KnownType.System_Data_Odbc_OdbcCommand, "CommandText"),
new MemberDescriptor(KnownType.System_Data_OracleClient_OracleCommand, "CommandText"),
new MemberDescriptor(KnownType.System_Data_SqlClient_SqlCommand, "CommandText"),
new MemberDescriptor(KnownType.System_Data_SqlServerCe_SqlCeCommand, "CommandText"),
new MemberDescriptor(KnownType.MySql_Data_MySqlClient_MySqlCommand, "CommandText"),
new MemberDescriptor(KnownType.Microsoft_Data_Sqlite_SqliteCommand, "CommandText"),
new MemberDescriptor(KnownType.System_Data_Sqlite_SqliteCommand, "CommandText"),
new(KnownType.System_Data_Odbc_OdbcCommand, "CommandText"),
new(KnownType.System_Data_OracleClient_OracleCommand, "CommandText"),
new(KnownType.System_Data_SqlClient_SqlCommand, "CommandText"),
new(KnownType.System_Data_Sqlite_SqliteCommand, "CommandText"),
new(KnownType.System_Data_SqlServerCe_SqlCeCommand, "CommandText"),
new(KnownType.Microsoft_Data_Sqlite_SqliteCommand, "CommandText"),
new(KnownType.MySql_Data_MySqlClient_MySqlCommand, "CommandText")
};

protected abstract TExpressionSyntax GetArgumentAtIndex(InvocationContext context, int index);
Expand All @@ -113,10 +120,12 @@ protected override void Initialize(TrackerInput input)
var inv = Language.Tracker.Invocation;
inv.Track(input,
inv.MatchMethod(invocationsForFirstTwoArguments),
inv.And(
MethodHasRawSqlQueryParameter(),
inv.Or(ArgumentAtIndexIsTracked(0), ArgumentAtIndexIsTracked(1))
),
inv.And(MethodHasRawSqlQueryParameter(), inv.Or(ArgumentAtIndexIsTracked(0), ArgumentAtIndexIsTracked(1))),
inv.ExceptWhen(inv.ArgumentAtIndexIsConstant(0)));

inv.Track(input,
inv.MatchMethod(invocationsForFirstTwoArgumentsAfterV2),
inv.Or(ArgumentAtIndexIsTracked(0), ArgumentAtIndexIsTracked(1)),
inv.ExceptWhen(inv.ArgumentAtIndexIsConstant(0)));

TrackInvocations(input, invocationsForFirstArgument, FirstArgumentIndex);
Expand Down Expand Up @@ -161,7 +170,7 @@ private void TrackObjectCreation(TrackerInput input, KnownType[] objectCreationT
t.Track(input,
t.MatchConstructor(objectCreationTypes),
t.ArgumentAtIndexIs(argumentIndex, KnownType.System_String),
c => IsTracked(GetArgumentAtIndex(c, argumentIndex), c),
c => IsTracked(GetArgumentAtIndex(c, argumentIndex), c),
t.ExceptWhen(t.ArgumentAtIndexIsConst(argumentIndex)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace SonarAnalyzer.Helpers.Trackers
public class VisualBasicInvocationTracker : InvocationTracker<SyntaxKind>
{
protected override ILanguageFacade<SyntaxKind> Language => VisualBasicFacade.Instance;
protected override SyntaxKind[] TrackedSyntaxKinds { get; } = new[] { SyntaxKind.InvocationExpression };
protected override SyntaxKind[] TrackedSyntaxKinds { get; } = { SyntaxKind.InvocationExpression };

public override Condition ArgumentAtIndexIsConstant(int index) =>
context => ((InvocationExpressionSyntax)context.Node).ArgumentList is { } argumentList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private static string GetTestCaseFileName(string analyzerName) =>
"UsingCookies" => "UsingCookies_Net46",
"LooseFilePermissions" => "LooseFilePermissions.Windows",
#else
"ExecutingSqlQueries" => "ExecutingSqlQueries_NetCore",
"ExecutingSqlQueries" => "ExecutingSqlQueries_EntityFrameworkCoreLatest",
"UsingCookies" => "UsingCookies_NetCore",
"LooseFilePermissions" => "LooseFilePermissions.Unix",
"PermissiveCors" => "PermissiveCors.Net",
Expand Down Expand Up @@ -120,7 +120,7 @@ private static IEnumerable<MetadataReference> GetAdditionalReferences(string ana
nameof(UsingRegularExpressions) => MetadataReferenceFacade.RegularExpressions,
#if NET
nameof(DisablingCsrfProtection) => DisablingCsrfProtectionTest.AdditionalReferences(),
nameof(ExecutingSqlQueries) => ExecutingSqlQueriesTest.GetReferencesNetCore(Constants.DotNetCore220Version),
nameof(ExecutingSqlQueries) => ExecutingSqlQueriesTest.GetReferencesEntityFrameworkNetCore(Constants.NuGetLatestVersion),
nameof(LooseFilePermissions) => NuGetMetadataReference.MonoPosixNetStandard(),
nameof(PermissiveCors) => PermissiveCorsTest.AdditionalReferences,
nameof(UsingCookies) => UsingCookies.GetAdditionalReferencesForNetCore(Constants.DotNetCore220Version),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,25 @@ namespace SonarAnalyzer.UnitTest.Rules
[TestClass]
public class ExecutingSqlQueriesTest
{
private readonly VerifierBuilder builderCS = new VerifierBuilder().AddAnalyzer(() => new CS.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled)).WithBasePath("Hotspots");
private readonly VerifierBuilder builderVB = new VerifierBuilder().AddAnalyzer(() => new VB.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled)).WithBasePath("Hotspots");

#if NETFRAMEWORK // System.Data.OracleClient.dll is not available on .Net Core

[TestMethod]
public void ExecutingSqlQueries_CS_Net46() =>
OldVerifier.VerifyAnalyzer(
@"TestCases\Hotspots\ExecutingSqlQueries_Net46.cs",
new CS.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled),
GetReferencesNet46(Constants.NuGetLatestVersion));
builderCS
.AddPaths(@"ExecutingSqlQueries_Net46.cs")
.AddReferences(GetReferencesNet46(Constants.NuGetLatestVersion))
.Verify();

[TestMethod]
public void ExecutingSqlQueries_VB_Net46() =>
OldVerifier.VerifyAnalyzer(
@"TestCases\Hotspots\ExecutingSqlQueries_Net46.vb",
new VB.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled),
ParseOptionsHelper.FromVisualBasic15,
GetReferencesNet46(Constants.NuGetLatestVersion));
builderVB
.AddPaths(@"ExecutingSqlQueries_Net46.vb")
.WithOptions(ParseOptionsHelper.FromVisualBasic15)
.AddReferences(GetReferencesNet46(Constants.NuGetLatestVersion))
.Verify();

internal static IEnumerable<MetadataReference> GetReferencesNet46(string sqlServerCeVersion) =>
NetStandardMetadataReference.Netstandard
Expand All @@ -62,36 +65,55 @@ internal static IEnumerable<MetadataReference> GetReferencesNet46(string sqlServ
#else

[TestMethod]
public void ExecutingSqlQueries_CS_NetCore() =>
OldVerifier.VerifyAnalyzer(
@"TestCases\Hotspots\ExecutingSqlQueries_NetCore.cs",
new CS.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled),
ParseOptionsHelper.FromCSharp8,
GetReferencesNetCore(Constants.DotNetCore220Version));
public void ExecutingSqlQueries_CS_EntityFrameworkCore2() =>
builderCS
.AddPaths(@"ExecutingSqlQueries_EntityFrameworkCore2.cs")
.WithOptions(ParseOptionsHelper.FromCSharp8)
.AddReferences(GetReferencesEntityFrameworkNetCore("2.2.6"))
.Verify();

[TestMethod]
public void ExecutingSqlQueries_CS_EntityFrameworkCoreLatest() =>
builderCS
.AddPaths(@"ExecutingSqlQueries_EntityFrameworkCoreLatest.cs")
.WithOptions(ParseOptionsHelper.FromCSharp8)
.AddReferences(GetReferencesEntityFrameworkNetCore(Constants.NuGetLatestVersion))
.Verify();

[TestMethod]
public void ExecutingSqlQueries_CSharp9() =>
OldVerifier.VerifyAnalyzerFromCSharp9Console(
@"TestCases\Hotspots\ExecutingSqlQueries.CSharp9.cs",
new CS.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled),
GetReferencesNetCore(Constants.DotNetCore220Version).Concat(NuGetMetadataReference.MicrosoftDataSqliteCore()));
builderCS
.AddPaths(@"ExecutingSqlQueries.CSharp9.cs")
.WithTopLevelStatements()
.AddReferences(GetReferencesEntityFrameworkNetCore(Constants.DotNetCore220Version).Concat(NuGetMetadataReference.MicrosoftDataSqliteCore()))
.Verify();

[TestMethod]
public void ExecutingSqlQueries_CSharp10() =>
OldVerifier.VerifyAnalyzerFromCSharp10Console(
@"TestCases\Hotspots\ExecutingSqlQueries.CSharp10.cs",
new CS.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled),
GetReferencesNetCore(Constants.DotNetCore220Version).Concat(NuGetMetadataReference.MicrosoftDataSqliteCore()));
builderCS
.AddPaths(@"ExecutingSqlQueries.CSharp10.cs")
.WithOptions(ParseOptionsHelper.FromCSharp10)
.WithTopLevelStatements()
.AddReferences(GetReferencesEntityFrameworkNetCore(Constants.DotNetCore220Version).Concat(NuGetMetadataReference.MicrosoftDataSqliteCore()))
.Verify();

[TestMethod]
public void ExecutingSqlQueries_VB_EntityFrameworkCore2() =>
builderVB
.AddPaths(@"ExecutingSqlQueries_EntityFrameworkCore2.vb")
.WithOptions(ParseOptionsHelper.FromVisualBasic15)
.AddReferences(GetReferencesEntityFrameworkNetCore(Constants.DotNetCore220Version))
.Verify();

[TestMethod]
public void ExecutingSqlQueries_VB_NetCore() =>
OldVerifier.VerifyAnalyzer(
@"TestCases\Hotspots\ExecutingSqlQueries_NetCore.vb",
new VB.ExecutingSqlQueries(AnalyzerConfiguration.AlwaysEnabled),
ParseOptionsHelper.FromVisualBasic15,
GetReferencesNetCore(Constants.DotNetCore220Version));
public void ExecutingSqlQueries_VB_EntityFrameworkCoreLatest() =>
builderVB
.AddPaths(@"ExecutingSqlQueries_EntityFrameworkCoreLatest.vb")
.WithOptions(ParseOptionsHelper.FromVisualBasic15)
.AddReferences(GetReferencesEntityFrameworkNetCore(Constants.NuGetLatestVersion))
.Verify();

internal static IEnumerable<MetadataReference> GetReferencesNetCore(string entityFrameworkVersion) =>
internal static IEnumerable<MetadataReference> GetReferencesEntityFrameworkNetCore(string entityFrameworkVersion) =>
Enumerable.Empty<MetadataReference>()
.Concat(NuGetMetadataReference.MicrosoftEntityFrameworkCore(entityFrameworkVersion))
.Concat(NuGetMetadataReference.MicrosoftEntityFrameworkCoreRelational(entityFrameworkVersion));
Expand Down
Loading