Skip to content

Commit 3ee0a7d

Browse files
committed
Add support for F# analyzers in project evaluation
- Extend ProjectInfo types to include Analyzers, AllProperties, and AllItems. - Implement logic to collect all properties and items during project evaluation. - Add a test case for loading a project with NuGet analyzers. - Create sample project files for testing F# analyzers.
1 parent b5079bf commit 3ee0a7d

File tree

6 files changed

+194
-2
lines changed

6 files changed

+194
-2
lines changed

src/Ionide.ProjInfo/Library.fs

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,48 @@ module ProjectLoader =
873873
IsPublishable = msbuildPropBool "IsPublishable"
874874
}
875875

876-
let mapToProject (path: string) (compilerArgs: string seq) (p2p: ProjectReference seq) (compile: CompileItem seq) (nugetRefs: PackageReference seq) (sdkInfo: ProjectSdkInfo) (props: Property seq) (customProps: Property seq) =
876+
#if DEBUG
877+
let private printProperties (props: Map<string, Set<string>>) =
878+
let file = Path.Combine(__SOURCE_DIRECTORY__, "props.txt")
879+
880+
props
881+
|> Map.iter (fun key value ->
882+
IO.File.AppendAllLines(file, [ $"Property: {key}" ])
883+
884+
value
885+
|> Set.iter (fun v -> IO.File.AppendAllLines(file, [ $" Value: {v}" ]))
886+
)
887+
888+
let private printItems (items: Map<string, Set<string * Map<string, string>>>) =
889+
let file = Path.Combine(__SOURCE_DIRECTORY__, "items.txt")
890+
891+
items
892+
|> Map.iter (fun key value ->
893+
IO.File.AppendAllLines(file, [ $"ItemType: {key}" ])
894+
895+
value
896+
|> Set.iter (fun (include_, metadata) ->
897+
IO.File.AppendAllLines(file, [ $" Value: {include_}" ])
898+
899+
metadata
900+
|> Map.iter (fun mk mv -> IO.File.AppendAllLines(file, [ $" {mk}: {mv}" ]))
901+
)
902+
)
903+
#endif
904+
905+
let mapToProject
906+
(path: string)
907+
(compilerArgs: string seq)
908+
(p2p: ProjectReference seq)
909+
(compile: CompileItem seq)
910+
(nugetRefs: PackageReference seq)
911+
(sdkInfo: ProjectSdkInfo)
912+
(props: Property seq)
913+
(customProps: Property seq)
914+
(analyzers: Map<string, Set<string>>)
915+
(allProps: Map<string, Set<string>>)
916+
(allItems: Map<string, Set<string * Map<string, string>>>)
917+
=
877918
let projDir = Path.GetDirectoryName path
878919

879920
let outputType, sourceFiles, otherOptions =
@@ -944,6 +985,9 @@ module ProjectLoader =
944985
Items = compileItems
945986
Properties = List.ofSeq props
946987
CustomProperties = List.ofSeq customProps
988+
Analyzers = analyzers
989+
AllProperties = allProps
990+
AllItems = allItems
947991
}
948992

949993

@@ -1005,10 +1049,64 @@ module ProjectLoader =
10051049
let sdkInfo = getSdkInfo props
10061050
let customProps = getProperties p customProperties
10071051

1052+
let allProperties =
1053+
(Map.empty, p.Properties)
1054+
||> Seq.fold (fun acc p ->
1055+
Map.change
1056+
p.Name
1057+
(function
1058+
| None ->
1059+
Set.singleton p.EvaluatedValue
1060+
|> Some
1061+
| Some v ->
1062+
v
1063+
|> Set.add p.EvaluatedValue
1064+
|> Some)
1065+
acc
1066+
)
1067+
1068+
let allItems =
1069+
(Map.empty, p.Items)
1070+
||> Seq.fold (fun acc item ->
1071+
let value = item.EvaluatedInclude
1072+
1073+
let metadata =
1074+
item.Metadata
1075+
|> Seq.map (fun m -> m.Name, m.EvaluatedValue)
1076+
|> Map.ofSeq
1077+
1078+
Map.change
1079+
item.ItemType
1080+
(function
1081+
| None ->
1082+
Set.singleton (value, metadata)
1083+
|> Some
1084+
| Some items ->
1085+
items
1086+
|> Set.add (value, metadata)
1087+
|> Some)
1088+
acc
1089+
1090+
)
1091+
1092+
1093+
let analyzers =
1094+
let analyzerLikePath = Path.Combine("analyzers", "dotnet", "fs")
1095+
1096+
allProperties
1097+
|> Map.filter (fun k v ->
1098+
k.StartsWith("Pkg")
1099+
&& v
1100+
|> Set.exists (fun s -> Path.Exists(Path.Combine(s, analyzerLikePath)))
1101+
)
1102+
1103+
10081104
if not sdkInfo.RestoreSuccess then
10091105
Error "not restored"
10101106
else
1011-
let proj = mapToProject path commandLineArgs p2pRefs compileItems nuGetRefs sdkInfo props customProps
1107+
let proj =
1108+
mapToProject path commandLineArgs p2pRefs compileItems nuGetRefs sdkInfo props customProps analyzers allProperties allItems
1109+
10121110
Ok(LoadedProjectInfo.StandardProjectInfo proj)
10131111
| LoadedProject.Other p -> Ok(LoadedProjectInfo.OtherProjectInfo p)
10141112

src/Ionide.ProjInfo/Types.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ module Types =
6868
Items: ProjectItem list
6969
Properties: Property list
7070
CustomProperties: Property list
71+
/// This contains all Project Properties seen during evaluation
72+
AllProperties: Map<string, Set<string>>
73+
/// This contains all Project Items seen during evaluation with their values and associated metadata
74+
AllItems: Map<string, Set<string * Map<string, string>>>
75+
/// Will have Key Value pairs like "PkgIonide_Analyzers" -> "C:\Users\username\.nuget\packages\ionide.analyzers\0.14.7"
76+
/// The "analyzers/dotnet/fs" subfolder is not included here, just the package root
77+
Analyzers: Map<string, Set<string>>
7178
} with
7279

7380
/// ResolvedTargetPath is the path to the primary reference assembly for this project.

test/Ionide.ProjInfo.Tests/TestAssets.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,13 @@ let ``sample 14 slnx solution`` = {
366366
TargetFrameworks = Map.empty
367367
ProjectReferences = []
368368
}
369+
370+
371+
/// A test with f# analyzers
372+
let ``sample 15 nuget analyzers`` = {
373+
ProjDir = "sample15-nuget-analyzers"
374+
AssemblyName = ""
375+
ProjectFile = "sample15-nuget-analyzers.fsproj"
376+
TargetFrameworks = Map.empty
377+
ProjectReferences = []
378+
}

test/Ionide.ProjInfo.Tests/Tests.fs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,9 @@ let testFCSmapManyProjCheckCaching =
15061506
Items = []
15071507
Properties = []
15081508
CustomProperties = []
1509+
Analyzers = Map.empty
1510+
AllProperties = Map.empty
1511+
AllItems = Map.empty
15091512
}
15101513

15111514
let makeReference (options: ProjectOptions) = {
@@ -2353,6 +2356,48 @@ let sample14SlnxFileTest toolsPath loaderType workspaceFactory =
23532356
| _ -> failtestf "Expected a project, but got %A" expectedProject.Kind
23542357
)
23552358

2359+
let sample15NugetAnalyzers toolsPath loaderType workspaceFactory =
2360+
testCase
2361+
|> withLog
2362+
$"Can load sample15 with nuget analyzers - {loaderType}"
2363+
(fun log fs ->
2364+
2365+
let projPath = pathForProject ``sample 15 nuget analyzers``
2366+
let projDir = Path.GetDirectoryName projPath
2367+
2368+
// need to build the projects first so that there's something to latch on to
2369+
dotnet fs [
2370+
"restore"
2371+
projPath
2372+
]
2373+
|> checkExitCodeZero
2374+
2375+
let loader: IWorkspaceLoader = workspaceFactory toolsPath
2376+
2377+
let parsed =
2378+
loader.LoadProjects [ projPath ]
2379+
|> Seq.toList
2380+
2381+
Expect.hasLength parsed 1 "Should have loaded the F# lib"
2382+
2383+
let fsharpProject = parsed[0]
2384+
2385+
let analyzerDlls = fsharpProject.Analyzers
2386+
2387+
Expect.isSome
2388+
(analyzerDlls
2389+
|> Map.tryFind "PkgG-Research_FSharp_Analyzers")
2390+
"Should have found the PkgG-Research_FSharp_Analyzers key in the analyzers map"
2391+
2392+
Expect.isSome
2393+
(analyzerDlls
2394+
|> Map.tryFind "PkgIonide_Analyzers")
2395+
"Should have found the PkgIonide_Analyzers key in the analyzers map"
2396+
2397+
2398+
)
2399+
2400+
23562401
let tests toolsPath =
23572402
let testSample3WorkspaceLoaderExpected = [
23582403
ExpectNotification.loading "c1.fsproj"
@@ -2491,4 +2536,7 @@ let tests toolsPath =
24912536

24922537
sample14SlnxFileTest toolsPath (nameof (WorkspaceLoader)) WorkspaceLoader.Create
24932538
sample14SlnxFileTest toolsPath (nameof (WorkspaceLoaderViaProjectGraph)) WorkspaceLoaderViaProjectGraph.Create
2539+
2540+
sample15NugetAnalyzers toolsPath (nameof (WorkspaceLoader)) WorkspaceLoader.Create
2541+
sample15NugetAnalyzers toolsPath (nameof (WorkspaceLoaderViaProjectGraph)) WorkspaceLoaderViaProjectGraph.Create
24942542
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace sample14_nuget_analyzers
2+
3+
module Say =
4+
let hello name =
5+
printfn "Hello %s" name
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<RootNamespace>sample14_nuget_analyzers</RootNamespace>
6+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<Compile Include="Library.fs" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="G-Research.FSharp.Analyzers">
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
<PrivateAssets>all</PrivateAssets>
17+
</PackageReference>
18+
<PackageReference Include="Ionide.Analyzers">
19+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20+
<PrivateAssets>all</PrivateAssets>
21+
</PackageReference>
22+
</ItemGroup>
23+
24+
</Project>

0 commit comments

Comments
 (0)