如何在 .NET 代码分析器中获取解决方案路径
How to get solution path in .NET code analyzer
如何访问 Roslyn 代码分析器中正在编译的 project/solution 的文件路径?我需要根据相对于代码存储的一些规范文件来验证代码。不起作用的东西:
SyntaxTreeAnalysisContext.Tree.FilePath
Assembly.GetExecutingAssembly().Location
AppDomain.CurrentDomain.BaseDirectory
Environment.CurrentDirectory
Path.GetFullPath(relativePath)
分析器存在于工作区级别之下(它们 运行 直接由编译器提供),因此解决方案可能不存在。
由于复杂的原因,它们不是由 MEF 创建的,因此即使它确实存在,也没有简单的方法来获取它。
从 VS 中,您可以找到全局服务提供者(例如,ServiceProvider.GlobalProvider
),然后获取 SComponentModel
(VS 自己的 MEF 图的根)并获取 Roslyn 的 VisualStudioWorkspace
从那个。请注意,这是一种有点脆弱的方法,在 VS 之外根本不起作用。
即使在 VS 中,这也会以奇怪的方式中断预览窗格、杂项文件和其他不属于全局解决方案的上下文中的分析。
这是另一种无需 VS 即可工作的方法;也很脆,但出于不同的原因。 :)
通过从当前语法树的源文件路径开始向上搜索文件夹层次结构,在文件级别查找 csproj。
当然在某些情况下它不会工作(如果源文件在 csproj 文件夹的子树之外,例如链接文件;或者周围有其他陈旧的 csproj 文件等)唯一我能想到的安全网是检查找到的 csproj 是否真的与当前 SemanticModel.Compilation.AssemblyName 引用的程序集名称相同,这样我们就不会得到其他随机项目的 csproj。
这是代码,请参阅名为 FindProjectFile 的方法:https://nsdepcop.codeplex.com/SourceControl/changeset/view/75896#VS2015/source/NsDepCop.VisualStudioIntegration/ProjectAnalyzerRepository.cs
不经过反射不可能从分析器或修复器得到解决方案。
使用additional files存储设置。
在项目中:
<ItemGroup>
<AdditionalFiles Include="MyConfig.config" />
</ItemGroup>
在分析器中:
private const string ConfigFileName = "MyConfig.config";
private static string LoadConfig(ImmutableArray<AdditionalText> additionalFiles, CancellationToken cancellationToken)
{
var file = additionalFiles.SingleOrDefault(f => string.Compare(Path.GetFileName(f.Path), ConfigFileName, StringComparison.OrdinalIgnoreCase) == 0);
if (file == null)
{
return null;
}
var fileText = file.GetText(cancellationToken);
using (var stream = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{
fileText.Write(writer, cancellationToken);
}
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
private static void HandleCompilationStart(CompilationStartAnalysisContext context)
{
var config = LoadConfig(context.Options.AdditionalFiles, context.CancellationToken);
}
我想出了一个通过反射来做到这一点的方法,我只在 windows 环境中测试过这个。
public static class RoslynExtensions
{
public static Solution GetSolution(this SyntaxNodeAnalysisContext context)
{
var workspace = context.Options.GetPrivatePropertyValue<object>("Workspace");
return workspace.GetPrivatePropertyValue<Solution>("CurrentSolution");
}
public static T GetPrivatePropertyValue<T>(this object obj, string propName)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
var pi = obj.GetType().GetRuntimeProperty(propName);
if (pi == null)
{
throw new ArgumentOutOfRangeException(nameof(propName), $"Property {propName} was not found in Type {obj.GetType().FullName}");
}
return (T)pi.GetValue(obj, null);
}
}
像这样从分析器调用:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeConstDeclaration, SyntaxKind.FieldDeclaration);
}
public static void AnalyzeConstDeclaration(SyntaxNodeAnalysisContext context)
{
var solution = context.GetSolution();
}
如何访问 Roslyn 代码分析器中正在编译的 project/solution 的文件路径?我需要根据相对于代码存储的一些规范文件来验证代码。不起作用的东西:
SyntaxTreeAnalysisContext.Tree.FilePath
Assembly.GetExecutingAssembly().Location
AppDomain.CurrentDomain.BaseDirectory
Environment.CurrentDirectory
Path.GetFullPath(relativePath)
分析器存在于工作区级别之下(它们 运行 直接由编译器提供),因此解决方案可能不存在。
由于复杂的原因,它们不是由 MEF 创建的,因此即使它确实存在,也没有简单的方法来获取它。
从 VS 中,您可以找到全局服务提供者(例如,ServiceProvider.GlobalProvider
),然后获取 SComponentModel
(VS 自己的 MEF 图的根)并获取 Roslyn 的 VisualStudioWorkspace
从那个。请注意,这是一种有点脆弱的方法,在 VS 之外根本不起作用。
即使在 VS 中,这也会以奇怪的方式中断预览窗格、杂项文件和其他不属于全局解决方案的上下文中的分析。
这是另一种无需 VS 即可工作的方法;也很脆,但出于不同的原因。 :)
通过从当前语法树的源文件路径开始向上搜索文件夹层次结构,在文件级别查找 csproj。
当然在某些情况下它不会工作(如果源文件在 csproj 文件夹的子树之外,例如链接文件;或者周围有其他陈旧的 csproj 文件等)唯一我能想到的安全网是检查找到的 csproj 是否真的与当前 SemanticModel.Compilation.AssemblyName 引用的程序集名称相同,这样我们就不会得到其他随机项目的 csproj。
这是代码,请参阅名为 FindProjectFile 的方法:https://nsdepcop.codeplex.com/SourceControl/changeset/view/75896#VS2015/source/NsDepCop.VisualStudioIntegration/ProjectAnalyzerRepository.cs
不经过反射不可能从分析器或修复器得到解决方案。
使用additional files存储设置。
在项目中:
<ItemGroup>
<AdditionalFiles Include="MyConfig.config" />
</ItemGroup>
在分析器中:
private const string ConfigFileName = "MyConfig.config";
private static string LoadConfig(ImmutableArray<AdditionalText> additionalFiles, CancellationToken cancellationToken)
{
var file = additionalFiles.SingleOrDefault(f => string.Compare(Path.GetFileName(f.Path), ConfigFileName, StringComparison.OrdinalIgnoreCase) == 0);
if (file == null)
{
return null;
}
var fileText = file.GetText(cancellationToken);
using (var stream = new MemoryStream())
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{
fileText.Write(writer, cancellationToken);
}
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
private static void HandleCompilationStart(CompilationStartAnalysisContext context)
{
var config = LoadConfig(context.Options.AdditionalFiles, context.CancellationToken);
}
我想出了一个通过反射来做到这一点的方法,我只在 windows 环境中测试过这个。
public static class RoslynExtensions
{
public static Solution GetSolution(this SyntaxNodeAnalysisContext context)
{
var workspace = context.Options.GetPrivatePropertyValue<object>("Workspace");
return workspace.GetPrivatePropertyValue<Solution>("CurrentSolution");
}
public static T GetPrivatePropertyValue<T>(this object obj, string propName)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
var pi = obj.GetType().GetRuntimeProperty(propName);
if (pi == null)
{
throw new ArgumentOutOfRangeException(nameof(propName), $"Property {propName} was not found in Type {obj.GetType().FullName}");
}
return (T)pi.GetValue(obj, null);
}
}
像这样从分析器调用:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeConstDeclaration, SyntaxKind.FieldDeclaration);
}
public static void AnalyzeConstDeclaration(SyntaxNodeAnalysisContext context)
{
var solution = context.GetSolution();
}