In Roslyn, a SyntaxTree represents the content of a source file. To determine which source file created a given SyntaxTree, use the FilePath property, which returns the path on disk. However, the disk path may not always be what you need.
- On CI, you should use reproducible builds to get deterministic paths (
ContinuousIntegrationBuild=true and <SourceRoot>). In this case, the file paths used for the [CallerFilePathAttribute] and PDB are deterministic paths that do not correspond to the disk path. Instead, it should be something starting with /_/. - You can use the
#line preprocessor directive to change the file path of the next line. This directive is often used by code generators. For instance, when a XAML file generates a C# file, the C# files contain #line directives to map the original XAML node.
C#
SyntaxTree syntaxTree = ...;
// Get the actual file path on disk
syntaxTree.FilePath;
// Get the mapped file path
_ = syntaxTree.GetLocation().GetMappedLineSpan().Path;
Another useful type is SourceReferenceResolver, accessible from a Compilation instance via compilation.Options.SourceReferenceResolver. Note that it may be null.
C#
static string GetDisplayPath(this SyntaxTree tree, TextSpan span, SourceReferenceResolver? resolver)
{
var mappedSpan = tree.GetMappedLineSpan(span);
if (resolver == null || mappedSpan.Path.Length == 0)
return mappedSpan.Path;
return resolver.NormalizePath(mappedSpan.Path, baseFilePath: mappedSpan.HasMappedPath ? tree.FilePath : null) ?? mappedSpan.Path;
}
If you have an IOperation, you can get the path through the Syntax property.
C#
IOperation operation = ...;
_ = operation.Syntax.SyntaxTree.FilePath;
If you have an ISymbol, you can get the paths through the Locations property.
C#
ISymbol operation = ...;
foreach (var location in symbol.Locations)
{
_ = location.SourceTree?.FilePath;
}
Do you have a question or a suggestion about this post? Contact me!