Last active
June 1, 2018 22:42
-
-
Save aggieben/576fc049e6d17567ee905fed8858efab to your computer and use it in GitHub Desktop.
Tool to find catch blocks for a specific exception type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ExceptionHandlers | |
open System | |
open System.Collections.Generic | |
open System.Diagnostics | |
open Microsoft.Build.Locator | |
open Microsoft.CodeAnalysis | |
open Microsoft.CodeAnalysis.MSBuild | |
open Microsoft.CodeAnalysis.CSharp | |
open Microsoft.CodeAnalysis.Text | |
open Microsoft.CodeAnalysis.Workspaces | |
open System.Threading | |
open Microsoft.CodeAnalysis.CSharp.Syntax | |
let hasExceptionDeclaration exceptionType (ccSyntax:CatchClauseSyntax) = | |
if isNull ccSyntax.Declaration then false | |
else ccSyntax.Declaration.Type.ToString() = exceptionType | |
let hasExceptionFilter exceptionType (ccSyntax:CatchClauseSyntax) = | |
if isNull ccSyntax.Filter then false | |
else ccSyntax.Filter.DescendantNodesAndSelf() | |
|> Seq.exists (fun node -> node :? TypeSyntax && (node :?> TypeSyntax).ToString() = exceptionType) | |
let usesExceptionInBlock exceptionType (ccSyntax:CatchClauseSyntax) = | |
let isTypeSyntaxOfType (t:string) (node:SyntaxNode) = node :? TypeSyntax && (node :?> TypeSyntax).ToString() = t | |
let isInNewObjectExpression (node:TypeSyntax) = node.Ancestors() |> Seq.exists (fun sn -> sn :? ObjectCreationExpressionSyntax) | |
let allNodes = ccSyntax.Block.DescendantNodesAndSelf() | |
let typeNodes = Seq.filter (isTypeSyntaxOfType exceptionType) allNodes | |
|> Seq.map (fun node -> node :?> TypeSyntax) | |
Seq.exists (fun node -> isTypeSyntaxOfType exceptionType node | |
&& (not << isInNewObjectExpression) node) typeNodes | |
let isExceptionCatchClause exceptionType (ccSyntax:CatchClauseSyntax) = | |
hasExceptionDeclaration exceptionType ccSyntax | |
|| hasExceptionFilter exceptionType ccSyntax | |
|| usesExceptionInBlock exceptionType ccSyntax | |
let analyzeHandlers exceptionType (project:Project) = async { | |
let! compilation = project.GetCompilationAsync() |> Async.AwaitTask | |
let catchClauses = compilation.SyntaxTrees | |
|> Seq.collect (fun stree -> stree.GetRoot(CancellationToken.None).DescendantNodesAndSelf() | |
|> Seq.filter (fun node -> node :? CatchClauseSyntax)) | |
|> Seq.map (fun node -> node :?> CatchClauseSyntax) | |
|> Seq.filter (isExceptionCatchClause exceptionType) | |
for cc in catchClauses do | |
let flps = cc.GetLocation().GetLineSpan() | |
// let line = (cc.GetText()).Lines.[0] | |
printfn "found catch clause: %s:%d" flps.Path flps.StartLinePosition.Line | |
printfn "%s" (cc.ToString()) | |
return Seq.length catchClauses | |
} | |
[<EntryPoint>] | |
let main argv = | |
MSBuildLocator.RegisterDefaults() |> ignore | |
use workspace = MSBuildWorkspace.Create() | |
let solution = workspace.OpenSolutionAsync(argv.[0]) |> Async.AwaitTask |> Async.RunSynchronously | |
printfn "Opened %d projects." (Seq.length solution.Projects) | |
let nonTestProjects = solution.Projects | |
|> Seq.filter (fun p -> p.Name.Contains("Test") |> not) | |
printfn "Found %d non-test projects." (Seq.length nonTestProjects) | |
let appProjects = nonTestProjects |> Project.getAppProjects | |
printfn "Found %d app projects" (Seq.length appProjects) | |
let numCatchClauses = appProjects | |
|> Seq.map (analyzeHandlers argv.[1]) | |
|> Async.Parallel | |
|> Async.RunSynchronously | |
|> Array.sum | |
printfn "Found %d catch clauses" numCatchClauses | |
0 // return an integer exit code |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Project | |
open System | |
open Microsoft.CodeAnalysis | |
type MSBuildProjectProperty = Microsoft.Build.Evaluation.ProjectProperty | |
type MSBuildProject = Microsoft.Build.Evaluation.Project | |
let getReferencedProjectIds (proj:Project) = | |
match List.ofSeq proj.ProjectReferences with | |
| [] -> [] | |
| prl -> List.map (fun (pr:ProjectReference) -> pr.ProjectId.Id) prl | |
let appProjectTypes = set [Guid.Parse("{349c5851-65df-11da-9384-00065b846f21}"); // web application | |
Guid.Parse("{E3E379DF-F4C6-4180-9B81-6769533ABE47}"); // mvc 4 | |
] | |
let hasProjectType (guids:Set<Guid>) (msbproj:MSBuildProject) = | |
match Seq.tryFind (fun (prop:MSBuildProjectProperty) -> prop.Name = "ProjectTypeGuids") msbproj.Properties with | |
| None -> false | |
| Some prop -> prop.EvaluatedValue.Split([|';'|], StringSplitOptions.RemoveEmptyEntries) | |
|> Array.map (fun s -> Guid.Parse(s)) |> set | |
|> Set.intersect guids | |
|> Set.isEmpty |> not | |
let hasExeOutputType (msbproj:MSBuildProject) = | |
match Seq.tryFind (fun (prop:MSBuildProjectProperty) -> prop.Name = "OutputType") msbproj.Properties with | |
| None -> false | |
| Some prop -> | |
let value = prop.EvaluatedValue.ToLower() | |
value = "exe" || value = "winexe" | |
let getAppProjects (projects:Project seq) = | |
let projectMap = projects |> Seq.fold (fun map p -> Map.add p.Id.Id (MSBuildProject(p.FilePath)) map) Map.empty | |
let projectIds = projects | |
|> Seq.filter (fun p -> (p.Name.ToLower()).Contains("test") |> not) | |
|> Seq.map (fun p -> projectMap.[p.Id.Id]) | |
|> Seq.filter (fun mp -> (hasExeOutputType mp) || (hasProjectType appProjectTypes mp)) | |
|> Seq.map (fun mp -> Map.pick (fun key msproj -> if msproj = mp then Some key else None) projectMap) | |
projects |> Seq.filter (fun p -> Seq.contains p.Id.Id projectIds) | |
let getRootProjects (projects:Project seq) = | |
let referencedProjects = projects | |
|> Seq.map getReferencedProjectIds | |
|> Seq.fold (fun state pidl -> Set.union (Set.ofSeq pidl) state) Set.empty | |
projects |> Seq.filter (fun p -> Set.contains p.Id.Id referencedProjects |> not) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment