Description
Background and Motivation
Implementing feature https://github.com/dotnet/csharplang/blob/main/proposals/ignored-directives.md.
Proposed API
namespace Microsoft.CodeAnalysis.CSharp
{
public enum SyntaxKind
{
+ IgnoredDirectiveTrivia = 9079,
}
public static class SyntaxFactory
{
+ public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive);
+ public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(bool isActive);
}
public class CSharpSyntaxVisitor<TResult>
{
+ public virtual TResult? VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node);
}
public class CSharpSyntaxVisitor
{
+ public virtual void VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node);
}
public class CSharpSyntaxRewriter : CSharpSyntaxVisitor<SyntaxNode>
{
+ public override SyntaxNode? VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node);
}
}
namespace Microsoft.CodeAnalysis.CSharp.Syntax
{
+ public sealed class IgnoredDirectiveTriviaSyntax : DirectiveTriviaSyntax
+ {
+ public override SyntaxToken HashToken { get; }
+ public SyntaxToken ColonToken { get; }
+ public override SyntaxToken EndOfDirectiveToken { get; }
+ public override bool IsActive { get; }
+ public override void Accept(CSharpSyntaxVisitor visitor);
+ public override TResult? Accept<TResult>(CSharpSyntaxVisitor<TResult> visitor) where TResult : default;
+ public IgnoredDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive);
+ public new IgnoredDirectiveTriviaSyntax WithHashToken(SyntaxToken hashToken);
+ public IgnoredDirectiveTriviaSyntax WithColonToken(SyntaxToken colonToken);
+ public new IgnoredDirectiveTriviaSyntax WithEndOfDirectiveToken(SyntaxToken endOfDirectiveToken);
+ public IgnoredDirectiveTriviaSyntax WithIsActive(bool isActive);
+ }
}
Note that these APIs are generated by Roslyn from
<Node Name="IgnoredDirectiveTriviaSyntax" Base="DirectiveTriviaSyntax">
<Kind Name="IgnoredDirectiveTrivia"/>
<Field Name="HashToken" Type="SyntaxToken" Override="true">
<Kind Name="HashToken"/>
</Field>
<Field Name="ColonToken" Type="SyntaxToken">
<Kind Name="ColonToken"/>
</Field>
<Field Name="EndOfDirectiveToken" Type="SyntaxToken" Override="true">
<Kind Name="EndOfDirectiveToken"/>
</Field>
<Field Name="IsActive" Type="bool" Override="true"/>
</Node>
Usage Examples
// This is how 'dotnet run file' will find the directives:
public void FindDirectives(SourceText text)
{
SyntaxTokenParser tokenizer = SyntaxFactory.CreateTokenParser(text);
var result = tokenizer.ParseLeadingTrivia();
foreach (var trivia in result.Token.LeadingTrivia)
{
if (trivia.IsKind(SyntaxKind.ShebangDirectiveTrivia))
{
// process #!
}
else if (trivia.IsKind(SyntaxKind.IgnoredDirectiveTrivia))
{
var message = trivia.GetStructure() is IgnoredDirectiveTriviaSyntax { EndOfDirectiveToken.LeadingTrivia: [{ RawKind: (int)SyntaxKind.PreprocessingMessageTrivia } messageTrivia] }
? messageTrivia.ToString().AsSpan().Trim()
: "";
// process #:
}
}
}
Alternative Designs
IgnoredDirective is very similar to the existing ShebangDirective:
<Node Name="ShebangDirectiveTriviaSyntax" Base="DirectiveTriviaSyntax">
<Kind Name="ShebangDirectiveTrivia"/>
<Field Name="HashToken" Type="SyntaxToken" Override="true">
<Kind Name="HashToken"/>
</Field>
<Field Name="ExclamationToken" Type="SyntaxToken">
<Kind Name="ExclamationToken"/>
</Field>
<Field Name="EndOfDirectiveToken" Type="SyntaxToken" Override="true">
<Kind Name="EndOfDirectiveToken"/>
</Field>
<Field Name="IsActive" Type="bool" Override="true"/>
</Node>
Note that it means the "text" of the directive is appended as leading trivia of kind PreprocessingMessageTrivia on the ignored trivia's EndOfDirectiveToken. We could change that so the text is part of the directive syntax instead. The usage would be simpler but the shape would not be consistent with shebang directive.
Risks
None.