From b3e173952e4bbf07856576ac5a71eeed5fb90b0b Mon Sep 17 00:00:00 2001 From: Zaid Date: Mon, 17 Aug 2020 12:14:08 +0200 Subject: [PATCH] Proper file type checking for extracting symbols --- NpgsqlFSharpAnalyzer.sln | 4 +- src/AnalyzerVs/ContentTypeNames.cs | 2 +- src/AnalyzerVs/FsLintVsPackage.cs | 4 +- src/AnalyzerVs/LintChecker.cs | 81 +++++++++++-------- src/AnalyzerVs/LintCheckerProvider.cs | 4 +- src/AnalyzerVs/LintError.cs | 7 +- src/AnalyzerVs/LintProjectInfo.cs | 2 +- src/AnalyzerVs/LintTagger.cs | 2 +- ...arpLintVs.csproj => NpgsqlFSharpVs.csproj} | 0 src/AnalyzerVs/Options/FsLintOptionsPage.cs | 2 +- src/AnalyzerVs/SubscriptionManager.cs | 2 +- src/AnalyzerVs/SuggestionPreview.xaml | 4 +- src/AnalyzerVs/SuggestionPreview.xaml.cs | 2 +- .../Suggestions/LintActionsSource.cs | 2 +- src/AnalyzerVs/Suggestions/LintFixAction.cs | 2 +- .../Suggestions/LintSuggestionProvider.cs | 2 +- .../Suggestions/LintSuppressAction.cs | 2 +- src/AnalyzerVs/Suggestions/LintSuppressBy.cs | 2 +- .../Table/LintTableSnapshotFactory.cs | 2 +- src/AnalyzerVs/Table/LintingErrorsSnapshot.cs | 2 +- src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs | 44 +++------- src/NpgsqlFSharpAnalyzer.Core/SqlAnalyzer.fs | 10 +++ .../SyntacticAnalysis.fs | 4 +- src/NpgsqlFSharpAnalyzer.Core/Types.fs | 6 +- src/NpgsqlFSharpAnalyzer/SqlAnalyzer.fs | 4 +- tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs | 7 +- 26 files changed, 101 insertions(+), 104 deletions(-) rename src/AnalyzerVs/{FSharpLintVs.csproj => NpgsqlFSharpVs.csproj} (100%) diff --git a/NpgsqlFSharpAnalyzer.sln b/NpgsqlFSharpAnalyzer.sln index b2622b0..b0a01c0 100644 --- a/NpgsqlFSharpAnalyzer.sln +++ b/NpgsqlFSharpAnalyzer.sln @@ -29,9 +29,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution RELEASE_NOTES.md = RELEASE_NOTES.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharpLintVs", "src\AnalyzerVs\FSharpLintVs.csproj", "{37577282-1289-40DB-AD3D-24499BD09DAE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NpgsqlFSharpVs", "src\AnalyzerVs\NpgsqlFSharpVs.csproj", "{37577282-1289-40DB-AD3D-24499BD09DAE}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "NpgsqlFSharpAnalyzer.Core", "src\NpgsqlFSharpAnalyzer.Core\NpgsqlFSharpAnalyzer.Core.fsproj", "{5964BB56-97B8-4FAE-9933-8113DB11438D}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "NpgsqlFSharpAnalyzer.Core", "src\NpgsqlFSharpAnalyzer.Core\NpgsqlFSharpAnalyzer.Core.fsproj", "{5964BB56-97B8-4FAE-9933-8113DB11438D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/AnalyzerVs/ContentTypeNames.cs b/src/AnalyzerVs/ContentTypeNames.cs index e60ad0c..736f09b 100644 --- a/src/AnalyzerVs/ContentTypeNames.cs +++ b/src/AnalyzerVs/ContentTypeNames.cs @@ -1,4 +1,4 @@ -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public static class ContentTypeNames { diff --git a/src/AnalyzerVs/FsLintVsPackage.cs b/src/AnalyzerVs/FsLintVsPackage.cs index 1c37c87..849653e 100644 --- a/src/AnalyzerVs/FsLintVsPackage.cs +++ b/src/AnalyzerVs/FsLintVsPackage.cs @@ -9,11 +9,11 @@ using Microsoft.VisualStudio.Shell.Interop; using Task = System.Threading.Tasks.Task; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { // DO NOT REMOVE THIS MAGICAL INCANTATION NO MATTER HOW MUCH VS WARNS YOU OF DEPRECATION // -------------------------------------------------------------------------------------- - [InstalledProductRegistration("F# Lint", "", "0.1", IconResourceID = 400)] + [InstalledProductRegistration("F# Npgsql Analyzer", "Static SQL analyzer with Npgsql.FSharp", "0.1", IconResourceID = 400)] // -------------------------------------------------------------------------------------- // Package registration attributes diff --git a/src/AnalyzerVs/LintChecker.cs b/src/AnalyzerVs/LintChecker.cs index d8b2667..6e4e9fa 100644 --- a/src/AnalyzerVs/LintChecker.cs +++ b/src/AnalyzerVs/LintChecker.cs @@ -1,28 +1,20 @@ -using Dotnet.ProjInfo.Workspace; -using FSharp.Compiler; -using FSharp.Compiler.SourceCodeServices; +using FSharp.Compiler; using FSharp.Compiler.Text; using Npgsql.FSharp.Analyzers.Core; using Microsoft.FSharp.Control; using Microsoft.VisualStudio; -using Microsoft.VisualStudio.Language.StandardClassification; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Threading; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Windows.Controls; -using System.Windows.Threading; using Microsoft.FSharp.Core; -using Microsoft.FSharp.Collections; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { /// /// Finds the linting errors in comments for a specific buffer. @@ -171,12 +163,13 @@ private void RunLinter() public async Task DoUpdateAsync() { if (IsDisposed) + { return; + } var buffer = _currentSnapshot; var path = _document.FilePath; - // replace with user token var token = _cts.Token; var instance = await FsLintVsPackage.Instance.WithCancellation(token); @@ -210,53 +203,73 @@ public async Task DoUpdateAsync() return; } - var defaults = FSharpParsingOptions.Default; - var parseOpts = new FSharpParsingOptions( - sourceFiles: new string[] { path }, - conditionalCompilationDefines: defaults.ConditionalCompilationDefines, - errorSeverityOptions: defaults.ErrorSeverityOptions, - isInteractive: defaults.IsInteractive, - lightSyntax: defaults.LightSyntax, - compilingFsLib: defaults.CompilingFsLib, - isExe: defaults.IsExe - ); + var loadedSchema = SqlAnalysis.databaseSchema(connectionString); + + if (loadedSchema.IsError) + { + return; + } var source = _currentSnapshot.GetText(); var sourceText = SourceText.ofString(source); - var parseAsync = _provider.CheckerInstance.ParseFile(path, sourceText, parseOpts, null); - var parseResults = await FSharpAsync.StartAsTask(parseAsync, null, token); - if (parseResults.ParseHadErrors) + + var getProjectOptions = _provider.CheckerInstance.GetProjectOptionsFromScript( + filename: path, + sourceText: sourceText, + assumeDotNetFramework: false, + useSdkRefs: true, + useFsiAuxLib: true, + previewEnabled: true, + otherFlags: new string[] { "--targetprofile:netstandard" }, + loadedTimeStamp: FSharpOption.None, + extraProjectInfo: FSharpOption.None, + optionsStamp: FSharpOption.None, + userOpName: FSharpOption.None + ); + + var (options, errors) = await FSharpAsync.StartAsTask(getProjectOptions, null, token); + + if (errors.Any()) { return; } + var performParseAndCheck = _provider.CheckerInstance.ParseAndCheckFileInProject( + filename: path, + fileversion: 1, + sourceText: sourceText, + options: options, + textSnapshotInfo: FSharpOption.None, + userOpName: FSharpOption.None + ); - var loadedSchema = SqlAnalysis.databaseSchema(connectionString); + var (parseResults, checkAnswer) = await FSharpAsync.StartAsTask(performParseAndCheck, null, token); - if (loadedSchema.IsError) + if (parseResults.ParseHadErrors || checkAnswer.IsAborted) { return; } - var context = new SpecializedContext( + var checkResults = SqlAnalyzer.checkAnswerResult(checkAnswer).Value; + + var context = new SqlAnalyzerContext( fileName: path, content: source.Split('\n'), parseTree: parseResults.ParseTree.Value, - symbols: FSharpList.Empty + symbols: SqlAnalyzer.getSymbols(checkResults) ); - var operations = SyntacticAnalysis.findSqlOperations(context).ToList(); var databaseSchema = loadedSchema.ResultValue; - var foundErrors = - from operation in operations - from message in SqlAnalysis.analyzeOperation(operation, connectionString, databaseSchema) - select message; + var errorMessages = + from operation in SyntacticAnalysis.findSqlOperations(context) + from analysisOutput in SqlAnalysis.analyzeOperation(operation, connectionString, databaseSchema) + select analysisOutput; var oldLintingErrors = this.Factory.CurrentSnapshot; var newLintErrors = new LintingErrorsSnapshot(_document, oldLintingErrors.VersionNumber + 1); - foreach (var error in foundErrors) + foreach (var error in errorMessages) { var span = RangeToSpan(error.Range, buffer); newLintErrors.Errors.Add(new LintError(span, error, Project)); diff --git a/src/AnalyzerVs/LintCheckerProvider.cs b/src/AnalyzerVs/LintCheckerProvider.cs index e7f8a0e..2f92f47 100644 --- a/src/AnalyzerVs/LintCheckerProvider.cs +++ b/src/AnalyzerVs/LintCheckerProvider.cs @@ -1,5 +1,5 @@ using FSharp.Compiler.SourceCodeServices; -using FSharpLintVs; +using NpgsqlFSharpVs; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; @@ -14,7 +14,7 @@ using System.ComponentModel.Composition; using System.Diagnostics; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { /// /// Factory for the and . diff --git a/src/AnalyzerVs/LintError.cs b/src/AnalyzerVs/LintError.cs index 71bdd7b..990c2ee 100644 --- a/src/AnalyzerVs/LintError.cs +++ b/src/AnalyzerVs/LintError.cs @@ -1,12 +1,9 @@ using Npgsql.FSharp.Analyzers.Core; -using Microsoft.FSharp.Core; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Utilities; using System.Collections.Generic; -using System.Text; using System.Linq; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintError { @@ -16,7 +13,7 @@ public class LintError public int NextIndex = -1; - public string Tooltip => $"{LintWarning.Code}: {LintWarning.Message}"; + public string Tooltip => LintWarning.Message; public string Identifier => LintWarning.Type; diff --git a/src/AnalyzerVs/LintProjectInfo.cs b/src/AnalyzerVs/LintProjectInfo.cs index 9e9c2ab..c918218 100644 --- a/src/AnalyzerVs/LintProjectInfo.cs +++ b/src/AnalyzerVs/LintProjectInfo.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.Shell.Interop; using System; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintProjectInfo { diff --git a/src/AnalyzerVs/LintTagger.cs b/src/AnalyzerVs/LintTagger.cs index 0fcd797..309baa6 100644 --- a/src/AnalyzerVs/LintTagger.cs +++ b/src/AnalyzerVs/LintTagger.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintTagger : ITagger, IDisposable { diff --git a/src/AnalyzerVs/FSharpLintVs.csproj b/src/AnalyzerVs/NpgsqlFSharpVs.csproj similarity index 100% rename from src/AnalyzerVs/FSharpLintVs.csproj rename to src/AnalyzerVs/NpgsqlFSharpVs.csproj diff --git a/src/AnalyzerVs/Options/FsLintOptionsPage.cs b/src/AnalyzerVs/Options/FsLintOptionsPage.cs index 714efeb..38efe7b 100644 --- a/src/AnalyzerVs/Options/FsLintOptionsPage.cs +++ b/src/AnalyzerVs/Options/FsLintOptionsPage.cs @@ -3,7 +3,7 @@ using Microsoft.VisualStudio.Shell; using System.Runtime.InteropServices; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { [Guid(GuidString)] public class FsLintOptionsPage : DialogPage diff --git a/src/AnalyzerVs/SubscriptionManager.cs b/src/AnalyzerVs/SubscriptionManager.cs index ec15fb8..3f413a4 100644 --- a/src/AnalyzerVs/SubscriptionManager.cs +++ b/src/AnalyzerVs/SubscriptionManager.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.Shell.TableManager; using System; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { /// /// Every consumer of data from an provides an to record the changes. We give the consumer diff --git a/src/AnalyzerVs/SuggestionPreview.xaml b/src/AnalyzerVs/SuggestionPreview.xaml index 100cd96..fb818a4 100644 --- a/src/AnalyzerVs/SuggestionPreview.xaml +++ b/src/AnalyzerVs/SuggestionPreview.xaml @@ -1,10 +1,10 @@ - diff --git a/src/AnalyzerVs/SuggestionPreview.xaml.cs b/src/AnalyzerVs/SuggestionPreview.xaml.cs index 570c38f..f15c1ae 100644 --- a/src/AnalyzerVs/SuggestionPreview.xaml.cs +++ b/src/AnalyzerVs/SuggestionPreview.xaml.cs @@ -13,7 +13,7 @@ using System.Windows.Navigation; using System.Windows.Shapes; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { /// /// Interaction logic for SuggestionPreview.xaml diff --git a/src/AnalyzerVs/Suggestions/LintActionsSource.cs b/src/AnalyzerVs/Suggestions/LintActionsSource.cs index efb633d..b7d3e59 100644 --- a/src/AnalyzerVs/Suggestions/LintActionsSource.cs +++ b/src/AnalyzerVs/Suggestions/LintActionsSource.cs @@ -10,7 +10,7 @@ using System.Threading; using System.Threading.Tasks; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintActionsSource : ISuggestedActionsSource { diff --git a/src/AnalyzerVs/Suggestions/LintFixAction.cs b/src/AnalyzerVs/Suggestions/LintFixAction.cs index b21eae8..fc14c8f 100644 --- a/src/AnalyzerVs/Suggestions/LintFixAction.cs +++ b/src/AnalyzerVs/Suggestions/LintFixAction.cs @@ -9,7 +9,7 @@ using Microsoft.VisualStudio.Text; using Npgsql.FSharp.Analyzers.Core; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintFixAction : ISuggestedAction { diff --git a/src/AnalyzerVs/Suggestions/LintSuggestionProvider.cs b/src/AnalyzerVs/Suggestions/LintSuggestionProvider.cs index 0dcb90d..29c6541 100644 --- a/src/AnalyzerVs/Suggestions/LintSuggestionProvider.cs +++ b/src/AnalyzerVs/Suggestions/LintSuggestionProvider.cs @@ -4,7 +4,7 @@ using Microsoft.VisualStudio.Utilities; using System.ComponentModel.Composition; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { [Export(typeof(ISuggestedActionsSourceProvider))] [ContentType(ContentTypeNames.FSharpContentType)] diff --git a/src/AnalyzerVs/Suggestions/LintSuppressAction.cs b/src/AnalyzerVs/Suggestions/LintSuppressAction.cs index 78a807c..71e65e0 100644 --- a/src/AnalyzerVs/Suggestions/LintSuppressAction.cs +++ b/src/AnalyzerVs/Suggestions/LintSuppressAction.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using System.Windows; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintSuppressAction : ISuggestedAction { diff --git a/src/AnalyzerVs/Suggestions/LintSuppressBy.cs b/src/AnalyzerVs/Suggestions/LintSuppressBy.cs index 95adc10..9640e50 100644 --- a/src/AnalyzerVs/Suggestions/LintSuppressBy.cs +++ b/src/AnalyzerVs/Suggestions/LintSuppressBy.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using System.Windows; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintSuppressBy : ISuggestedAction { diff --git a/src/AnalyzerVs/Table/LintTableSnapshotFactory.cs b/src/AnalyzerVs/Table/LintTableSnapshotFactory.cs index 74f2891..94b4689 100644 --- a/src/AnalyzerVs/Table/LintTableSnapshotFactory.cs +++ b/src/AnalyzerVs/Table/LintTableSnapshotFactory.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.Shell.TableManager; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintTableSnapshotFactory : TableEntriesSnapshotFactoryBase { diff --git a/src/AnalyzerVs/Table/LintingErrorsSnapshot.cs b/src/AnalyzerVs/Table/LintingErrorsSnapshot.cs index e0f02ed..ed385d5 100644 --- a/src/AnalyzerVs/Table/LintingErrorsSnapshot.cs +++ b/src/AnalyzerVs/Table/LintingErrorsSnapshot.cs @@ -9,7 +9,7 @@ using System.Windows; using System.Windows.Documents; -namespace FSharpLintVs +namespace NpgsqlFSharpVs { public class LintingErrorsSnapshot : WpfTableEntriesSnapshotBase { diff --git a/src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs b/src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs index b1a2728..923f2f2 100644 --- a/src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs +++ b/src/NpgsqlFSharpAnalyzer.Core/SqlAnalysis.fs @@ -1,5 +1,6 @@ namespace Npgsql.FSharp.Analyzers.Core +open System open FSharp.Compiler.Range open F23.StringSimilarity @@ -205,7 +206,12 @@ module SqlAnalysis = |> List.minBy (fun column -> levenshtein.Distance(attempt.columnName, column.Name)) |> fun column -> column.Name - let warningMsg = sprintf "Attempting to read column named '%s' that was not returned by the result set. Did you mean to read '%s'?\nAvailable columns from the result set are:\n%s" attempt.columnName closestAlternative (formatColumns availableColumns) + let warningMsg = + if String.IsNullOrWhiteSpace attempt.columnName then + sprintf "Attempting to read a column without specifying a name. Available columns returned from the result set are:\n%s" (formatColumns availableColumns) + else + sprintf "Attempting to read column named '%s' that was not returned by the result set. Did you mean to read '%s'?\nAvailable columns from the result set are:\n%s" attempt.columnName closestAlternative (formatColumns availableColumns) + let warning = createWarning warningMsg attempt.columnNameRange let codeFixes = availableColumns @@ -281,8 +287,6 @@ module SqlAnalysis = ("bit"|"bool"|"boolean") -> if column.Nullable && notUsing "boolOrNone" then yield typeMismatch [ replace "boolOrNone" ] - // else if not column.Nullable && using "boolOrNone" - // then yield typeMismatch [ replace "bool" ] else if notUsing "boolOrNone" && notUsing "bool" then if column.Nullable @@ -293,14 +297,6 @@ module SqlAnalysis = | ("int8" | "tinyint") -> if column.Nullable && List.forall notUsing [ "int8OrNone"; "int16OrNone"; "intOrNone"; "int64OrNone" ] then yield typeMismatch [ replace "int8OrNone"; replace "int16OrNone"; replace "intOrNone"; replace "int64OrNone" ] - // else if not column.Nullable && using "int8OrNone" - // then yield typeMismatch [ replace "int8"; replace "int16"; replace "int"; replace "int64" ] - // else if not column.Nullable && using "int16OrNone" - // then yield typeMismatch [ replace "int8"; replace "int16"; replace "int"; replace "int64" ] - // else if not column.Nullable && using "intdOrNone" - // then yield typeMismatch [ replace "int8"; replace "int16"; replace "int"; replace "int64" ] - // else if not column.Nullable && using "int64OrNone" - // then yield typeMismatch [ replace "int8"; replace "int16"; replace "int"; replace "int64" ] else if List.forall notUsing [ "int8OrNone"; "int16OrNone"; "intOrNone"; "int64OrNone"; "int8"; "int16"; "int"; "int64" ] then if column.Nullable @@ -310,25 +306,15 @@ module SqlAnalysis = | ("int16"| "smallint") -> if column.Nullable && List.forall notUsing [ "int16OrNone"; "intOrNone"; "int64OrNone" ] then yield typeMismatch [ replace "int16OrNone"; replace "intOrNone"; replace "int64OrNone" ] - //else if not column.Nullable && using "int16OrNone" - //then yield typeMismatch [ replace "int16"; replace "int"; replace "int64" ] - //else if not column.Nullable && using "intOrNone" - //then yield typeMismatch [ replace "int16"; replace "int"; replace "int64" ] - //else if not column.Nullable && using "int64OrNone" - //then yield typeMismatch [ replace "int16"; replace "int"; replace "int64" ] else if List.forall notUsing [ "int16OrNone"; "intOrNone"; "int64OrNone"; "int16"; "int"; "int64" ] then if column.Nullable then yield typeMismatch [ replace "int16OrNone"; replace "intOrNone"; replace "int64OrNone" ] else yield typeMismatch [ replace "int16"; replace "int"; replace "int64" ] - | ("int"|"integer"|"int32"|"serial"|"int4") -> + | ("int"|"integer"|"int32"|"serial"|"int4"|"int2") -> if column.Nullable && List.forall notUsing [ "intOrNone"; "int64OrNone" ] then yield typeMismatch [ replace "intOrNone"; replace "int64OrNone" ] - // else if not column.Nullable && using "intOrNone" - // then yield typeMismatch [ replace "int"; replace "int64" ] - // else if not column.Nullable && using "int64OrNone" - // then yield typeMismatch [ replace "int"; replace "int64" ] else if List.forall notUsing [ "intOrNone"; "int64OrNone"; "int"; "int64" ] then if column.Nullable @@ -338,8 +324,6 @@ module SqlAnalysis = | ("int64"|"bigint"|"bigserial") -> if column.Nullable && notUsing "int64OrNone" then yield typeMismatch [ replace "int64OrNone" ] - // else if not column.Nullable && using "int64OrNone" - // then yield typeMismatch [ replace "int64" ] else if notUsing "int64OrNone" && notUsing "int64" then if column.Nullable @@ -350,8 +334,6 @@ module SqlAnalysis = | ("numeric"|"decimal"|"money") -> if column.Nullable && notUsing "decimalOrNone" then yield typeMismatch [ replace "decimalOrNone" ] - // else if not column.Nullable && using "decimalOrNone" - // then yield typeMismatch [ replace "decimal" ] else if notUsing "decimalOrNone" && notUsing "decimal" then if column.Nullable @@ -362,8 +344,6 @@ module SqlAnalysis = | "double precision" -> if column.Nullable && notUsing "doubleOrNone" then yield typeMismatch [ replace "doubleOrNone" ] - // else if not column.Nullable && using "doubleOrNone" - // then yield typeMismatch [ replace "double" ] else if notUsing "doubleOrNone" && notUsing "double" then if column.Nullable @@ -371,11 +351,9 @@ module SqlAnalysis = else yield typeMismatch [ replace "double" ] else () - | ("text"|"json"|"xml"|"jsonb") -> + | ("text"|"json"|"xml"|"jsonb"|"varchar") -> if column.Nullable && notUsing "textOrNone" && notUsing "stringOrNone" then yield typeMismatch [ replace "textOrNone"; replace "stringOrNone" ] - //else if not column.Nullable && (using "textOrNone" || using "stringOrNone") - //then yield typeMismatch [ replace "text"; replace "string" ] else if notUsing "textOrNone" && notUsing "text" && notUsing "string" && notUsing "stringOrNone" then if column.Nullable @@ -386,8 +364,6 @@ module SqlAnalysis = | "date" -> if column.Nullable && notUsing "dateOrNone" then yield typeMismatch [ replace "dateOrNone" ] - //else if not column.Nullable && using "dateOrNone" - //then yield typeMismatch [ replace "date" ] else if notUsing "dateOrNone" && notUsing "date" then if column.Nullable @@ -398,8 +374,6 @@ module SqlAnalysis = | ("timestamp"|"timestamp without time zone") -> if column.Nullable && notUsing "timestampOrNone" && notUsing "dateTimeOrNone" then yield typeMismatch [ replace "dateTimeOrNone"; replace "timestampOrNone" ] - //else if not column.Nullable && (using "timestampOrNone" || using "dateTimeOrNone") - //then yield typeMismatch [ replace "dateTime"; replace "timestamp" ] else if notUsing "timestampOrNone" && notUsing "timestamp" && notUsing "dateTimeOrNone" && notUsing "dateTime" then if column.Nullable diff --git a/src/NpgsqlFSharpAnalyzer.Core/SqlAnalyzer.fs b/src/NpgsqlFSharpAnalyzer.Core/SqlAnalyzer.fs index e47dc8c..ae9cded 100644 --- a/src/NpgsqlFSharpAnalyzer.Core/SqlAnalyzer.fs +++ b/src/NpgsqlFSharpAnalyzer.Core/SqlAnalyzer.fs @@ -3,6 +3,7 @@ namespace Npgsql.FSharp.Analyzers.Core open System open System.IO open System.Linq +open FSharp.Compiler.SourceCodeServices module SqlAnalyzer = @@ -14,6 +15,15 @@ module SqlAnalyzer = then path else findParent (DirectoryInfo(path).Parent.FullName) fileToFind + let getSymbols (checkResults: FSharpCheckFileResults) = + checkResults.PartialAssemblySignature.Entities + |> Seq.toList + + let checkAnswerResult checkFileAnswer = + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> None + | FSharpCheckFileAnswer.Succeeded results -> Some results + let tryFindConfig (file: string) = try let parent = (Directory.GetParent file).FullName diff --git a/src/NpgsqlFSharpAnalyzer.Core/SyntacticAnalysis.fs b/src/NpgsqlFSharpAnalyzer.Core/SyntacticAnalysis.fs index 2d80722..21d7849 100644 --- a/src/NpgsqlFSharpAnalyzer.Core/SyntacticAnalysis.fs +++ b/src/NpgsqlFSharpAnalyzer.Core/SyntacticAnalysis.fs @@ -403,7 +403,7 @@ module SyntacticAnalysis = | SynBinding.Binding (access, kind, mustInline, isMutable, attrs, xmlDecl, valData, headPat, returnInfo, expr, range, seqPoint) -> visitSyntacticExpression expr range - let findLiterals (ctx: SpecializedContext) = + let findLiterals (ctx: SqlAnalyzerContext) = let values = new ResizeArray() for symbol in ctx.Symbols |> Seq.collect (fun s -> s.TryGetMembersFunctionsAndValues) do match symbol.LiteralValue with @@ -427,7 +427,7 @@ module SyntacticAnalysis = { operation with blocks = modifiedBlocks } - let findSqlOperations (ctx: SpecializedContext) = + let findSqlOperations (ctx: SqlAnalyzerContext) = let operations = ResizeArray() match ctx.ParseTree with | ParsedInput.ImplFile input -> diff --git a/src/NpgsqlFSharpAnalyzer.Core/Types.fs b/src/NpgsqlFSharpAnalyzer.Core/Types.fs index 76d4055..4903ace 100644 --- a/src/NpgsqlFSharpAnalyzer.Core/Types.fs +++ b/src/NpgsqlFSharpAnalyzer.Core/Types.fs @@ -7,7 +7,7 @@ open FSharp.Compiler open FSharp.Compiler.SyntaxTree open FSharp.Compiler.SourceCodeServices -type SpecializedContext = +type SqlAnalyzerContext = { FileName: string Content: string[] ParseTree: ParsedInput @@ -31,6 +31,10 @@ type Message = Range: range Fixes: Fix list } + with + member self.IsWarning() = self.Severity = Warning + member self.IsInfo() = self.Severity = Info + member self.IsError() = self.Severity = Error type ColumnReadAttempt = { funcName: string; diff --git a/src/NpgsqlFSharpAnalyzer/SqlAnalyzer.fs b/src/NpgsqlFSharpAnalyzer/SqlAnalyzer.fs index 5efdc54..5bcf1ae 100644 --- a/src/NpgsqlFSharpAnalyzer/SqlAnalyzer.fs +++ b/src/NpgsqlFSharpAnalyzer/SqlAnalyzer.fs @@ -29,7 +29,7 @@ module SqlAnalyzer = Type = message.Type } - let specializedContext (ctx: Context) : Core.SpecializedContext = { + let sqlAnalyzerContext (ctx: Context) : Core.SqlAnalyzerContext = { Content = ctx.Content FileName = ctx.FileName Symbols = ctx.Symbols @@ -39,7 +39,7 @@ module SqlAnalyzer = [] let queryAnalyzer : Analyzer = fun (ctx: Context) -> - let syntacticBlocks = Core.SyntacticAnalysis.findSqlOperations (specializedContext ctx) + let syntacticBlocks = Core.SyntacticAnalysis.findSqlOperations (sqlAnalyzerContext ctx) if List.isEmpty syntacticBlocks then [ ] else diff --git a/tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs b/tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs index 020740a..f3fdc8e 100644 --- a/tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs +++ b/tests/NpgsqlFSharpAnalyzer.Tests/Tests.fs @@ -6,7 +6,6 @@ open Npgsql.FSharp.Analyzers open Npgsql.FSharp.Analyzers.Core open Npgsql.FSharp open ThrowawayDb.Postgres -open FSharp.Analyzers.SDK let analyzers = [ SqlAnalyzer.queryAnalyzer @@ -111,7 +110,7 @@ let tests = let messages = SqlAnalysis.analyzeOperation block db.ConnectionString schema match messages with | [ message ] -> - Expect.equal Core.Severity.Warning message.Severity "The message is an warning" + Expect.isTrue (message.IsWarning()) "The message is an warning" Expect.stringContains message.Message "Sql.int64" "Message should contain the missing column name" | _ -> failwith "Expected only one error message" @@ -244,7 +243,7 @@ let tests = let messages = SqlAnalysis.analyzeOperation block db.ConnectionString schema match messages with | [ message ] -> - Expect.equal Core.Severity.Warning message.Severity "The message is an warning" + Expect.isTrue (message.IsWarning()) "The message is an warning" Expect.stringContains message.Message "non_existent" "Message should contain the missing column name" | _ -> failwith "Expected only one error message" @@ -269,7 +268,7 @@ let tests = let messages = SqlAnalysis.analyzeOperation block db.ConnectionString schema match messages with | [ message ] -> - Expect.equal Core.Severity.Warning message.Severity "The message is a warning" + Expect.isTrue (message.IsWarning()) "The message is a warning" Expect.stringContains message.Message "Missing parameter 'active'" "Error should say which parameter is not provided" | _ -> failwith "Expected only one error message"