diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props
index 45d3a2045367d..29cb0772f5142 100644
--- a/eng/targets/Settings.props
+++ b/eng/targets/Settings.props
@@ -17,6 +17,8 @@
$(NoWarn);NU1507
+ $(NoWarn);RSEXPERIMENTAL005
+
CommonExtensions
Microsoft\VBCSharp\LanguageServices
diff --git a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs
index 8ae7033ac2fd0..650f5b27cf0ad 100644
--- a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs
+++ b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs
@@ -230,6 +230,14 @@ static void addSingleNamespaceParts(ArrayBuilder> namespa
}
}
+ ///
+ /// Used for parsing .cs file-based programs.
+ ///
+ ///
+ /// In this mode, ignored directives #: are allowed.
+ ///
+ internal bool FileBasedProgram => Features.ContainsKey("FileBasedProgram");
+
internal override void ValidateOptions(ArrayBuilder builder)
{
ValidateOptions(builder, MessageProvider.Instance);
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index d482f2ad9b4f2..dd2f750ea57fa 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -8122,4 +8122,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
An expression tree may not contain an extension property access
+
+ '#:' directives cannot be after first token in file
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
+ '#:' directives cannot be after '#if' directive
+
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 9d85d3a14cf13..3ee0557087076 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -2386,13 +2386,19 @@ internal enum ErrorCode
ERR_UnderspecifiedExtension = 9295,
ERR_ExpressionTreeContainsExtensionPropertyAccess = 9296,
+ ERR_PPIgnoredFollowsToken = 9297,
+ ERR_PPIgnoredNeedsFileBasedProgram = 9298,
+ ERR_PPIgnoredFollowsIf = 9299,
+
// Note: you will need to do the following after adding errors:
// 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)
+ // 2) Add message to CSharpResources.resx
// Note: you will need to do the following after adding warnings:
// 1) Re-generate compiler code (eng\generate-compiler-code.cmd).
// 2) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)
// 3) Update ErrorFacts.GetWarningLevel (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)
// 4) Update DiagnosticTest.WarningLevel_2 (src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs)
+ // 5) Add message and '_Title' to CSharpResources.resx
}
}
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
index 69e4f74bf2725..9941defbe5926 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
@@ -2503,6 +2503,9 @@ or ErrorCode.ERR_InvalidExtensionParameterReference
or ErrorCode.ERR_ValueParameterSameNameAsExtensionTypeParameter
or ErrorCode.ERR_UnderspecifiedExtension
or ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess
+ or ErrorCode.ERR_PPIgnoredFollowsToken
+ or ErrorCode.ERR_PPIgnoredNeedsFileBasedProgram
+ or ErrorCode.ERR_PPIgnoredFollowsIf
=> false,
};
#pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value.
diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4
index 3c53475dbf132..02c4b6ce75cbe 100644
--- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4
+++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4
@@ -1279,6 +1279,7 @@ directive_trivia
| end_if_directive_trivia
| end_region_directive_trivia
| error_directive_trivia
+ | ignored_directive_trivia
| line_or_span_directive_trivia
| load_directive_trivia
| nullable_directive_trivia
@@ -1333,6 +1334,10 @@ error_directive_trivia
: '#' 'error'
;
+ignored_directive_trivia
+ : '#' ':'
+ ;
+
line_or_span_directive_trivia
: line_directive_trivia
| line_span_directive_trivia
diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs
index f268ad36627da..5a024081d1d9b 100644
--- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs
@@ -26604,6 +26604,96 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations)
=> new ShebangDirectiveTriviaSyntax(this.Kind, this.hashToken, this.exclamationToken, this.endOfDirectiveToken, this.isActive, GetDiagnostics(), annotations);
}
+internal sealed partial class IgnoredDirectiveTriviaSyntax : DirectiveTriviaSyntax
+{
+ internal readonly SyntaxToken hashToken;
+ internal readonly SyntaxToken colonToken;
+ internal readonly SyntaxToken endOfDirectiveToken;
+ internal readonly bool isActive;
+
+ internal IgnoredDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations)
+ : base(kind, diagnostics, annotations)
+ {
+ this.SlotCount = 3;
+ this.AdjustFlagsAndWidth(hashToken);
+ this.hashToken = hashToken;
+ this.AdjustFlagsAndWidth(colonToken);
+ this.colonToken = colonToken;
+ this.AdjustFlagsAndWidth(endOfDirectiveToken);
+ this.endOfDirectiveToken = endOfDirectiveToken;
+ this.isActive = isActive;
+ }
+
+ internal IgnoredDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive, SyntaxFactoryContext context)
+ : base(kind)
+ {
+ this.SetFactoryContext(context);
+ this.SlotCount = 3;
+ this.AdjustFlagsAndWidth(hashToken);
+ this.hashToken = hashToken;
+ this.AdjustFlagsAndWidth(colonToken);
+ this.colonToken = colonToken;
+ this.AdjustFlagsAndWidth(endOfDirectiveToken);
+ this.endOfDirectiveToken = endOfDirectiveToken;
+ this.isActive = isActive;
+ }
+
+ internal IgnoredDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive)
+ : base(kind)
+ {
+ this.SlotCount = 3;
+ this.AdjustFlagsAndWidth(hashToken);
+ this.hashToken = hashToken;
+ this.AdjustFlagsAndWidth(colonToken);
+ this.colonToken = colonToken;
+ this.AdjustFlagsAndWidth(endOfDirectiveToken);
+ this.endOfDirectiveToken = endOfDirectiveToken;
+ this.isActive = isActive;
+ }
+
+ public override SyntaxToken HashToken => this.hashToken;
+ public SyntaxToken ColonToken => this.colonToken;
+ public override SyntaxToken EndOfDirectiveToken => this.endOfDirectiveToken;
+ public override bool IsActive => this.isActive;
+
+ internal override GreenNode? GetSlot(int index)
+ => index switch
+ {
+ 0 => this.hashToken,
+ 1 => this.colonToken,
+ 2 => this.endOfDirectiveToken,
+ _ => null,
+ };
+
+ internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.IgnoredDirectiveTriviaSyntax(this, parent, position);
+
+ public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitIgnoredDirectiveTrivia(this);
+ public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitIgnoredDirectiveTrivia(this);
+
+ public IgnoredDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive)
+ {
+ if (hashToken != this.HashToken || colonToken != this.ColonToken || endOfDirectiveToken != this.EndOfDirectiveToken)
+ {
+ var newNode = SyntaxFactory.IgnoredDirectiveTrivia(hashToken, colonToken, endOfDirectiveToken, isActive);
+ var diags = GetDiagnostics();
+ if (diags?.Length > 0)
+ newNode = newNode.WithDiagnosticsGreen(diags);
+ var annotations = GetAnnotations();
+ if (annotations?.Length > 0)
+ newNode = newNode.WithAnnotationsGreen(annotations);
+ return newNode;
+ }
+
+ return this;
+ }
+
+ internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics)
+ => new IgnoredDirectiveTriviaSyntax(this.Kind, this.hashToken, this.colonToken, this.endOfDirectiveToken, this.isActive, diagnostics, GetAnnotations());
+
+ internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations)
+ => new IgnoredDirectiveTriviaSyntax(this.Kind, this.hashToken, this.colonToken, this.endOfDirectiveToken, this.isActive, GetDiagnostics(), annotations);
+}
+
internal sealed partial class NullableDirectiveTriviaSyntax : DirectiveTriviaSyntax
{
internal readonly SyntaxToken hashToken;
@@ -26967,6 +27057,7 @@ internal partial class CSharpSyntaxVisitor
public virtual TResult VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node);
+ public virtual TResult VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node);
public virtual TResult VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node);
}
@@ -27216,6 +27307,7 @@ internal partial class CSharpSyntaxVisitor
public virtual void VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node) => this.DefaultVisit(node);
public virtual void VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyntax node) => this.DefaultVisit(node);
public virtual void VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node);
+ public virtual void VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node);
public virtual void VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node);
}
@@ -27953,6 +28045,9 @@ public override CSharpSyntaxNode VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyn
public override CSharpSyntaxNode VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node)
=> node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.ExclamationToken), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive);
+ public override CSharpSyntaxNode VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node)
+ => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.ColonToken), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive);
+
public override CSharpSyntaxNode VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node)
=> node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.NullableKeyword), (SyntaxToken)Visit(node.SettingToken), (SyntaxToken)Visit(node.TargetToken), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive);
}
@@ -33237,6 +33332,20 @@ public ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(SyntaxToken hashToken
return new ShebangDirectiveTriviaSyntax(SyntaxKind.ShebangDirectiveTrivia, hashToken, exclamationToken, endOfDirectiveToken, isActive, this.context);
}
+ public IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive)
+ {
+#if DEBUG
+ if (hashToken == null) throw new ArgumentNullException(nameof(hashToken));
+ if (hashToken.Kind != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken));
+ if (colonToken == null) throw new ArgumentNullException(nameof(colonToken));
+ if (colonToken.Kind != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken));
+ if (endOfDirectiveToken == null) throw new ArgumentNullException(nameof(endOfDirectiveToken));
+ if (endOfDirectiveToken.Kind != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken));
+#endif
+
+ return new IgnoredDirectiveTriviaSyntax(SyntaxKind.IgnoredDirectiveTrivia, hashToken, colonToken, endOfDirectiveToken, isActive, this.context);
+ }
+
public NullableDirectiveTriviaSyntax NullableDirectiveTrivia(SyntaxToken hashToken, SyntaxToken nullableKeyword, SyntaxToken settingToken, SyntaxToken? targetToken, SyntaxToken endOfDirectiveToken, bool isActive)
{
#if DEBUG
@@ -38545,6 +38654,20 @@ public static ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(SyntaxToken ha
return new ShebangDirectiveTriviaSyntax(SyntaxKind.ShebangDirectiveTrivia, hashToken, exclamationToken, endOfDirectiveToken, isActive);
}
+ public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive)
+ {
+#if DEBUG
+ if (hashToken == null) throw new ArgumentNullException(nameof(hashToken));
+ if (hashToken.Kind != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken));
+ if (colonToken == null) throw new ArgumentNullException(nameof(colonToken));
+ if (colonToken.Kind != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken));
+ if (endOfDirectiveToken == null) throw new ArgumentNullException(nameof(endOfDirectiveToken));
+ if (endOfDirectiveToken.Kind != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken));
+#endif
+
+ return new IgnoredDirectiveTriviaSyntax(SyntaxKind.IgnoredDirectiveTrivia, hashToken, colonToken, endOfDirectiveToken, isActive);
+ }
+
public static NullableDirectiveTriviaSyntax NullableDirectiveTrivia(SyntaxToken hashToken, SyntaxToken nullableKeyword, SyntaxToken settingToken, SyntaxToken? targetToken, SyntaxToken endOfDirectiveToken, bool isActive)
{
#if DEBUG
diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs
index 66df0106254e3..6998b9cf5448b 100644
--- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs
@@ -747,6 +747,9 @@ public partial class CSharpSyntaxVisitor
/// Called when the visitor visits a ShebangDirectiveTriviaSyntax node.
public virtual TResult? VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node);
+ /// Called when the visitor visits a IgnoredDirectiveTriviaSyntax node.
+ public virtual TResult? VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node);
+
/// Called when the visitor visits a NullableDirectiveTriviaSyntax node.
public virtual TResult? VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node);
}
@@ -1485,6 +1488,9 @@ public partial class CSharpSyntaxVisitor
/// Called when the visitor visits a ShebangDirectiveTriviaSyntax node.
public virtual void VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node);
+ /// Called when the visitor visits a IgnoredDirectiveTriviaSyntax node.
+ public virtual void VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node);
+
/// Called when the visitor visits a NullableDirectiveTriviaSyntax node.
public virtual void VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node);
}
@@ -2223,6 +2229,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor
public override SyntaxNode? VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node)
=> node.Update(VisitToken(node.HashToken), VisitToken(node.ExclamationToken), VisitToken(node.EndOfDirectiveToken), node.IsActive);
+ public override SyntaxNode? VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node)
+ => node.Update(VisitToken(node.HashToken), VisitToken(node.ColonToken), VisitToken(node.EndOfDirectiveToken), node.IsActive);
+
public override SyntaxNode? VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node)
=> node.Update(VisitToken(node.HashToken), VisitToken(node.NullableKeyword), VisitToken(node.SettingToken), VisitToken(node.TargetToken), VisitToken(node.EndOfDirectiveToken), node.IsActive);
}
@@ -6511,6 +6520,19 @@ public static ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(SyntaxToken ha
public static ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(bool isActive)
=> SyntaxFactory.ShebangDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ExclamationToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive);
+ /// Creates a new IgnoredDirectiveTriviaSyntax instance.
+ public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive)
+ {
+ if (hashToken.Kind() != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken));
+ if (colonToken.Kind() != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken));
+ if (endOfDirectiveToken.Kind() != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken));
+ return (IgnoredDirectiveTriviaSyntax)Syntax.InternalSyntax.SyntaxFactory.IgnoredDirectiveTrivia((Syntax.InternalSyntax.SyntaxToken)hashToken.Node!, (Syntax.InternalSyntax.SyntaxToken)colonToken.Node!, (Syntax.InternalSyntax.SyntaxToken)endOfDirectiveToken.Node!, isActive).CreateRed();
+ }
+
+ /// Creates a new IgnoredDirectiveTriviaSyntax instance.
+ public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(bool isActive)
+ => SyntaxFactory.IgnoredDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ColonToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive);
+
/// Creates a new NullableDirectiveTriviaSyntax instance.
public static NullableDirectiveTriviaSyntax NullableDirectiveTrivia(SyntaxToken hashToken, SyntaxToken nullableKeyword, SyntaxToken settingToken, SyntaxToken targetToken, SyntaxToken endOfDirectiveToken, bool isActive)
{
diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs
index aa66360fb08d8..4623b79d7ea54 100644
--- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs
@@ -16572,6 +16572,55 @@ public ShebangDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken ex
public ShebangDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.ExclamationToken, this.EndOfDirectiveToken, isActive);
}
+///
+/// This node is associated with the following syntax kinds:
+///
+///
+///
+///
+public sealed partial class IgnoredDirectiveTriviaSyntax : DirectiveTriviaSyntax
+{
+
+ internal IgnoredDirectiveTriviaSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position)
+ : base(green, parent, position)
+ {
+ }
+
+ public override SyntaxToken HashToken => new SyntaxToken(this, ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).hashToken, Position, 0);
+
+ public SyntaxToken ColonToken => new SyntaxToken(this, ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).colonToken, GetChildPosition(1), GetChildIndex(1));
+
+ public override SyntaxToken EndOfDirectiveToken => new SyntaxToken(this, ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).endOfDirectiveToken, GetChildPosition(2), GetChildIndex(2));
+
+ public override bool IsActive => ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).IsActive;
+
+ internal override SyntaxNode? GetNodeSlot(int index) => null;
+
+ internal override SyntaxNode? GetCachedSlot(int index) => null;
+
+ public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitIgnoredDirectiveTrivia(this);
+ public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitIgnoredDirectiveTrivia(this);
+
+ public IgnoredDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive)
+ {
+ if (hashToken != this.HashToken || colonToken != this.ColonToken || endOfDirectiveToken != this.EndOfDirectiveToken)
+ {
+ var newNode = SyntaxFactory.IgnoredDirectiveTrivia(hashToken, colonToken, endOfDirectiveToken, isActive);
+ var annotations = GetAnnotations();
+ return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode;
+ }
+
+ return this;
+ }
+
+ internal override DirectiveTriviaSyntax WithHashTokenCore(SyntaxToken hashToken) => WithHashToken(hashToken);
+ public new IgnoredDirectiveTriviaSyntax WithHashToken(SyntaxToken hashToken) => Update(hashToken, this.ColonToken, this.EndOfDirectiveToken, this.IsActive);
+ public IgnoredDirectiveTriviaSyntax WithColonToken(SyntaxToken colonToken) => Update(this.HashToken, colonToken, this.EndOfDirectiveToken, this.IsActive);
+ internal override DirectiveTriviaSyntax WithEndOfDirectiveTokenCore(SyntaxToken endOfDirectiveToken) => WithEndOfDirectiveToken(endOfDirectiveToken);
+ public new IgnoredDirectiveTriviaSyntax WithEndOfDirectiveToken(SyntaxToken endOfDirectiveToken) => Update(this.HashToken, this.ColonToken, endOfDirectiveToken, this.IsActive);
+ public IgnoredDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.ColonToken, this.EndOfDirectiveToken, isActive);
+}
+
///
/// This node is associated with the following syntax kinds:
///
diff --git a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs
index 7c6641d1fd3a1..8ab5f8c772445 100644
--- a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs
+++ b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs
@@ -108,10 +108,14 @@ public CSharpSyntaxNode ParseDirective(
break;
default:
- if (lexer.Options.Kind == SourceCodeKind.Script && contextualKind == SyntaxKind.ExclamationToken && hashPosition == 0 && !hash.HasTrailingTrivia)
+ if (contextualKind == SyntaxKind.ExclamationToken && hashPosition == 0 && !hash.HasTrailingTrivia)
{
result = this.ParseShebangDirective(hash, this.EatToken(SyntaxKind.ExclamationToken), isActive);
}
+ else if (contextualKind == SyntaxKind.ColonToken && !hash.HasTrailingTrivia)
+ {
+ result = this.ParseIgnoredDirective(hash, this.EatToken(SyntaxKind.ColonToken), isActive, isFollowingToken);
+ }
else
{
var id = this.EatToken(SyntaxKind.IdentifierToken, false);
@@ -681,6 +685,26 @@ private DirectiveTriviaSyntax ParseShebangDirective(SyntaxToken hash, SyntaxToke
return SyntaxFactory.ShebangDirectiveTrivia(hash, exclamation, this.ParseEndOfDirectiveWithOptionalPreprocessingMessage(), isActive);
}
+ private DirectiveTriviaSyntax ParseIgnoredDirective(SyntaxToken hash, SyntaxToken colon, bool isActive, bool isFollowingToken)
+ {
+ if (!lexer.Options.FileBasedProgram)
+ {
+ colon = this.AddError(colon, ErrorCode.ERR_PPIgnoredNeedsFileBasedProgram);
+ }
+
+ if (isFollowingToken)
+ {
+ colon = this.AddError(colon, ErrorCode.ERR_PPIgnoredFollowsToken);
+ }
+
+ if (_context.SeenAnyIfDirectives)
+ {
+ colon = this.AddError(colon, ErrorCode.ERR_PPIgnoredFollowsIf);
+ }
+
+ return SyntaxFactory.IgnoredDirectiveTrivia(hash, colon, this.ParseEndOfDirectiveWithOptionalPreprocessingMessage(), isActive);
+ }
+
private SyntaxToken ParseEndOfDirectiveWithOptionalPreprocessingMessage()
=> this.lexer.LexEndOfDirectiveWithOptionalPreprocessingMessage();
diff --git a/src/Compilers/CSharp/Portable/Parser/Directives.cs b/src/Compilers/CSharp/Portable/Parser/Directives.cs
index 30d0149a20605..101314b0b7cb0 100644
--- a/src/Compilers/CSharp/Portable/Parser/Directives.cs
+++ b/src/Compilers/CSharp/Portable/Parser/Directives.cs
@@ -117,13 +117,15 @@ internal enum DefineState
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal readonly struct DirectiveStack
{
- public static readonly DirectiveStack Empty = new DirectiveStack(ConsList.Empty);
+ public static readonly DirectiveStack Empty = new DirectiveStack(ConsList.Empty, seenAnyIfDirectives: false);
private readonly ConsList? _directives;
+ private readonly bool _seenAnyIfDirectives;
- private DirectiveStack(ConsList? directives)
+ private DirectiveStack(ConsList? directives, bool seenAnyIfDirectives)
{
_directives = directives;
+ _seenAnyIfDirectives = seenAnyIfDirectives;
}
public static void InterlockedInitialize(ref DirectiveStack location, DirectiveStack value)
@@ -145,6 +147,8 @@ public bool IsEmpty
}
}
+ public bool SeenAnyIfDirectives => _seenAnyIfDirectives;
+
public DefineState IsDefined(string id)
{
for (var current = _directives; current != null && current.Any(); current = current.Tail)
@@ -235,7 +239,7 @@ public DirectiveStack Add(Directive directive)
}
RoslynDebug.AssertNotNull(_directives); // If 'prevIf' isn't null, then '_directives' wasn't null.
- return new DirectiveStack(CompleteIf(_directives, out _));
+ return new DirectiveStack(CompleteIf(_directives, out _), seenAnyIfDirectives: _seenAnyIfDirectives);
case SyntaxKind.EndRegionDirectiveTrivia:
var prevRegion = GetPreviousRegion(_directives);
if (prevRegion == null || !prevRegion.Any())
@@ -244,9 +248,10 @@ public DirectiveStack Add(Directive directive)
}
RoslynDebug.AssertNotNull(_directives); // If 'prevRegion' isn't null, then '_directives' wasn't null.
- return new DirectiveStack(CompleteRegion(_directives)); // remove region directives from stack but leave everything else
+ return new DirectiveStack(CompleteRegion(_directives), seenAnyIfDirectives: _seenAnyIfDirectives); // remove region directives from stack but leave everything else
default:
- return new DirectiveStack(new ConsList(directive, _directives ?? ConsList.Empty));
+ return new DirectiveStack(new ConsList(directive, _directives ?? ConsList.Empty),
+ seenAnyIfDirectives: _seenAnyIfDirectives || directive.Kind is SyntaxKind.IfDirectiveTrivia);
}
}
diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs
index 3ef0208c74f8a..719961f828e30 100644
--- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs
+++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs
@@ -2600,6 +2600,11 @@ private bool ScanDirectiveToken(ref TokenInfo info)
info.Kind = SyntaxKind.MinusToken;
break;
+ case ':':
+ TextWindow.AdvanceChar();
+ info.Kind = SyntaxKind.ColonToken;
+ break;
+
case '!':
TextWindow.AdvanceChar();
if (TextWindow.PeekChar() == '=')
diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
index 57a3f640e82e5..fd4173df29920 100644
--- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
+++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
@@ -1,5 +1,4 @@
abstract Microsoft.CodeAnalysis.CSharp.InterceptableLocation.Equals(Microsoft.CodeAnalysis.CSharp.InterceptableLocation? other) -> bool
-
Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddAttributeLists(params Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddConstraintClauses(params Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax!
@@ -41,3 +40,21 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration(Microsof
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! node) -> TResult?
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken colonToken, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithEndOfDirectiveToken(Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithHashToken(Microsoft.CodeAnalysis.SyntaxToken hashToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithIsActive(bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+Microsoft.CodeAnalysis.CSharp.SyntaxKind.IgnoredDirectiveTrivia = 9080 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
+override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode?
+[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void
+[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> TResult?
+[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.EndOfDirectiveToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.HashToken.get -> Microsoft.CodeAnalysis.SyntaxToken
+[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.IsActive.get -> bool
+static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken colonToken, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax!
+virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> void
+virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> TResult?
diff --git a/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs
index 704298132f7a4..17880ee6cdf7b 100644
--- a/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs
@@ -56,6 +56,8 @@ public SyntaxToken DirectiveNameToken
return ((LoadDirectiveTriviaSyntax)this).LoadKeyword;
case SyntaxKind.ShebangDirectiveTrivia:
return ((ShebangDirectiveTriviaSyntax)this).ExclamationToken;
+ case SyntaxKind.IgnoredDirectiveTrivia:
+ return ((IgnoredDirectiveTriviaSyntax)this).ColonToken;
case SyntaxKind.NullableDirectiveTrivia:
return ((NullableDirectiveTriviaSyntax)this).NullableKeyword;
default:
diff --git a/src/Compilers/CSharp/Portable/Syntax/IgnoredDirectiveTriviaSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/IgnoredDirectiveTriviaSyntax.cs
new file mode 100644
index 0000000000000..584eb1aef7c14
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Syntax/IgnoredDirectiveTriviaSyntax.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+
+// NOTE: This whole file should be removed once the experimental attribute is not needed
+// (the source generator should be used to emit this code instead).
+
+namespace Microsoft.CodeAnalysis.CSharp.Syntax;
+
+[Experimental(RoslynExperiments.IgnoredDirectives, UrlFormat = RoslynExperiments.IgnoredDirectives_Url)]
+partial class IgnoredDirectiveTriviaSyntax;
diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
index 4c34bbf696e2c..369768857720f 100644
--- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
+++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
@@ -5100,6 +5100,7 @@
+
@@ -5113,6 +5114,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
index d110fee28917e..6ba23a9cfc285 100644
--- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
@@ -929,5 +929,7 @@ public enum SyntaxKind : ushort
SpreadElement = 9078,
ExtensionDeclaration = 9079,
+
+ IgnoredDirectiveTrivia = 9080,
}
}
diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
index b2b8b824b17ac..9cd9333a54557 100644
--- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
@@ -291,6 +291,7 @@ public static bool IsPreprocessorDirective(SyntaxKind kind)
case SyntaxKind.LoadDirectiveTrivia:
case SyntaxKind.BadDirectiveTrivia:
case SyntaxKind.ShebangDirectiveTrivia:
+ case SyntaxKind.IgnoredDirectiveTrivia:
case SyntaxKind.NullableDirectiveTrivia:
return true;
default:
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index 068f107e816e6..3a04390a29050 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -1682,6 +1682,21 @@
Metoda {0} určuje omezení struct pro parametr typu {1}, ale odpovídající parametr typu {2} přepsané nebo explicitně implementované metody {3} není typ, který nemůže mít hodnotu null.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Konstruktor {0} ponechá požadovaný člen {1} neinicializovaný.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index dace7bde958f8..641e3a0a91ad0 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -1682,6 +1682,21 @@
Die Methode "{0}" gibt eine struct-Einschränkung für den Typparameter "{1}" an, aber der zugehörige Typparameter "{2}" der außer Kraft gesetzten oder explizit implementierten Methode "{3}" ist kein Non-Nullable-Werttyp.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Der Konstruktor "{0}" lässt den erforderlichen Member "{1}" deinitialisiert.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index 2eb9d48228ab9..e8053fcf06405 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -1682,6 +1682,21 @@
El método "{0}" especifica una restricción "struct" para el parámetro de tipo "{1}", pero el parámetro de tipo correspondiente "{2}" de los métodos invalidados o implementados explícitamente "{3}" no es un tipo de valor que acepta valores NULL.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
El constructor "{0}" deja el miembro requerido "{1}" sin inicializar.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index df38a03fcc1ca..e5bca98561f73 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -1682,6 +1682,21 @@
La méthode '{0}' spécifie une contrainte 'struct' pour le paramètre de type '{1}', mais le paramètre de type '{2}' correspondant de la méthode substituée ou explicitement implémentée '{3}' n'est pas un type valeur non-nullable.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Le constructeur « {0} » laisse le membre « {1} » requis non initialisé.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 5610170431e2e..157330cb2bad4 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -1682,6 +1682,21 @@
Il metodo '{0}' specifica un vincolo 'struct' per il parametro di tipo '{1}', ma il parametro di tipo corrispondente '{2}' del metodo '{3}' sottoposto a override o implementato in modo esplicito non è un tipo valore che non ammette valori Null.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Il costruttore '{0}' lascia non inizializzato il membro richiesto '{1}'.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index a15745b5f8e45..2127c58399bab 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -1682,6 +1682,21 @@
メソッド '{0}' は、型パラメーター '{1}' に対して 'struct' 制約を指定していますが、オーバーライドされた、または明示的に実装されたメソッド '{3}' の対応する型パラメーター '{2}' は NULL 非許容の値型ではありません。
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
コンストラクター '{0}' は、必要なメンバー '{1}' を初期化しないままにします。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index 24ba7f9d10c13..83dcf11bca751 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -1682,6 +1682,21 @@
'{0}' 메서드는 형식 매개 변수 '{1}'의 'struct' 제약 조건을 지정하지만 재정의되었거나 명시적으로 구현된 '{3}' 메서드의 해당 형식 매개 변수 '{2}'이(가) null을 허용하지 않는 값 형식이 아닙니다.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
생성자 '{0}'은(는) 필수 멤버 '{1}'을(를) 초기화되지 않은 상태로 둡니다.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index d493a435e49f4..fd9c20663a0ba 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -1682,6 +1682,21 @@
Metoda „{0}” określa ograniczenie „struct” dla parametru typu „{1}”, lecz odpowiadający parametr typu „{2}” przesłoniętej lub jawnie zaimplementowanej metody „{3}” nie jest nienullowalnym typem wartości.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Konstruktor „{0}” pozostawia wymaganą składową "{1}".
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index d1314a1f45e98..3969ca7bf83d0 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -1682,6 +1682,21 @@
O método '{0}' especifica uma restrição 'struct' para o parâmetro de tipo '{1}', mas o parâmetro de tipo correspondente '{2}' do método substituído ou implementado explicitamente '{3}' não é um tipo de valor não anulável.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
O construtor '{0}' deixa o membro obrigatório '{1}' não inicializado.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index 46a7390dcb912..c2b43cecfcf0a 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -1682,6 +1682,21 @@
Метод "{0}" задает ограничение struct для параметра типа "{1}", но соответствующий параметр типа "{2}" переопределенного или явно реализованного метода "{3}" не является типом значения, не допускающим значение NULL.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Конструктор "{0}" оставляет необходимый элемент "{1}" не инициализированным.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index 6161bd4a1569a..7a33cc92ed372 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -1682,6 +1682,21 @@
'{0}' yöntemi, '{1}' tür parametresi için bir 'struct' kısıtlaması belirtiyor, ancak geçersiz kılınan veya açıkça uygulanan '{3}' yönteminin karşılık gelen '{2}' tür parametresi boş değer atanamaz bir tip değil.
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
Oluşturucu '{0}', gerekli '{1}' üyesinin başlatmasını geri alıyor.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 37e1cdbe2ff0f..57d79284de704 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -1682,6 +1682,21 @@
方法 "{0}" 为类型参数 "{1}" 指定了 "struct" 约束,但重写的或显式实现的方法 "{3}" 的相应类型参数 "{2}" 不是不可为 null 的值类型。
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
构造函数“{0}”的必需成员“{1}”未初始化。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index 50a644618727a..20a4511f62f68 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -1682,6 +1682,21 @@
方法 '{0}' 會為型別參數 '{1}' 指定 'struct' 條件約束,但覆寫或明確實作的方法 '{3}' 對應型別參數 '{2}' 是不可為 Null 實值型別。
+
+ '#:' directives cannot be after '#if' directive
+ '#:' directives cannot be after '#if' directive
+
+
+
+ '#:' directives cannot be after first token in file
+ '#:' directives cannot be after first token in file
+
+
+
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+
+
Constructor '{0}' leaves required member '{1}' uninitialized.
建構函式 '{0}' 未初始化必要的成員 '{1}'。
diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs
index 604aa3566ef2d..10a3e4a9a239e 100644
--- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs
@@ -742,6 +742,9 @@ private static Syntax.InternalSyntax.LoadDirectiveTriviaSyntax GenerateLoadDirec
private static Syntax.InternalSyntax.ShebangDirectiveTriviaSyntax GenerateShebangDirectiveTrivia()
=> InternalSyntaxFactory.ShebangDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.ExclamationToken), InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool());
+ private static Syntax.InternalSyntax.IgnoredDirectiveTriviaSyntax GenerateIgnoredDirectiveTrivia()
+ => InternalSyntaxFactory.IgnoredDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.ColonToken), InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool());
+
private static Syntax.InternalSyntax.NullableDirectiveTriviaSyntax GenerateNullableDirectiveTrivia()
=> InternalSyntaxFactory.NullableDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.NullableKeyword), InternalSyntaxFactory.Token(SyntaxKind.EnableKeyword), null, InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool());
#endregion Green Generators
@@ -3882,6 +3885,19 @@ public void TestShebangDirectiveTriviaFactoryAndProperties()
AttachAndCheckDiagnostics(node);
}
+ [Fact]
+ public void TestIgnoredDirectiveTriviaFactoryAndProperties()
+ {
+ var node = GenerateIgnoredDirectiveTrivia();
+
+ Assert.Equal(SyntaxKind.HashToken, node.HashToken.Kind);
+ Assert.Equal(SyntaxKind.ColonToken, node.ColonToken.Kind);
+ Assert.Equal(SyntaxKind.EndOfDirectiveToken, node.EndOfDirectiveToken.Kind);
+ Assert.Equal(new bool(), node.IsActive);
+
+ AttachAndCheckDiagnostics(node);
+ }
+
[Fact]
public void TestNullableDirectiveTriviaFactoryAndProperties()
{
@@ -10243,6 +10259,32 @@ public void TestShebangDirectiveTriviaIdentityRewriter()
Assert.Same(oldNode, newNode);
}
+ [Fact]
+ public void TestIgnoredDirectiveTriviaTokenDeleteRewriter()
+ {
+ var oldNode = GenerateIgnoredDirectiveTrivia();
+ var rewriter = new TokenDeleteRewriter();
+ var newNode = rewriter.Visit(oldNode);
+
+ if(!oldNode.IsMissing)
+ {
+ Assert.NotEqual(oldNode, newNode);
+ }
+
+ Assert.NotNull(newNode);
+ Assert.True(newNode.IsMissing, "No tokens => missing");
+ }
+
+ [Fact]
+ public void TestIgnoredDirectiveTriviaIdentityRewriter()
+ {
+ var oldNode = GenerateIgnoredDirectiveTrivia();
+ var rewriter = new IdentityRewriter();
+ var newNode = rewriter.Visit(oldNode);
+
+ Assert.Same(oldNode, newNode);
+ }
+
[Fact]
public void TestNullableDirectiveTriviaTokenDeleteRewriter()
{
@@ -11006,6 +11048,9 @@ private static LoadDirectiveTriviaSyntax GenerateLoadDirectiveTrivia()
private static ShebangDirectiveTriviaSyntax GenerateShebangDirectiveTrivia()
=> SyntaxFactory.ShebangDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ExclamationToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool());
+ private static IgnoredDirectiveTriviaSyntax GenerateIgnoredDirectiveTrivia()
+ => SyntaxFactory.IgnoredDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ColonToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool());
+
private static NullableDirectiveTriviaSyntax GenerateNullableDirectiveTrivia()
=> SyntaxFactory.NullableDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.NullableKeyword), SyntaxFactory.Token(SyntaxKind.EnableKeyword), default(SyntaxToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool());
#endregion Red Generators
@@ -14146,6 +14191,19 @@ public void TestShebangDirectiveTriviaFactoryAndProperties()
Assert.Equal(node, newNode);
}
+ [Fact]
+ public void TestIgnoredDirectiveTriviaFactoryAndProperties()
+ {
+ var node = GenerateIgnoredDirectiveTrivia();
+
+ Assert.Equal(SyntaxKind.HashToken, node.HashToken.Kind());
+ Assert.Equal(SyntaxKind.ColonToken, node.ColonToken.Kind());
+ Assert.Equal(SyntaxKind.EndOfDirectiveToken, node.EndOfDirectiveToken.Kind());
+ Assert.Equal(new bool(), node.IsActive);
+ var newNode = node.WithHashToken(node.HashToken).WithColonToken(node.ColonToken).WithEndOfDirectiveToken(node.EndOfDirectiveToken).WithIsActive(node.IsActive);
+ Assert.Equal(node, newNode);
+ }
+
[Fact]
public void TestNullableDirectiveTriviaFactoryAndProperties()
{
@@ -20507,6 +20565,32 @@ public void TestShebangDirectiveTriviaIdentityRewriter()
Assert.Same(oldNode, newNode);
}
+ [Fact]
+ public void TestIgnoredDirectiveTriviaTokenDeleteRewriter()
+ {
+ var oldNode = GenerateIgnoredDirectiveTrivia();
+ var rewriter = new TokenDeleteRewriter();
+ var newNode = rewriter.Visit(oldNode);
+
+ if(!oldNode.IsMissing)
+ {
+ Assert.NotEqual(oldNode, newNode);
+ }
+
+ Assert.NotNull(newNode);
+ Assert.True(newNode.IsMissing, "No tokens => missing");
+ }
+
+ [Fact]
+ public void TestIgnoredDirectiveTriviaIdentityRewriter()
+ {
+ var oldNode = GenerateIgnoredDirectiveTrivia();
+ var rewriter = new IdentityRewriter();
+ var newNode = rewriter.Visit(oldNode);
+
+ Assert.Same(oldNode, newNode);
+ }
+
[Fact]
public void TestNullableDirectiveTriviaTokenDeleteRewriter()
{
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs
index 57d82e0f09cfd..95acc81fd07c9 100644
--- a/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs
@@ -63,6 +63,7 @@ public void TestFieldsForEqualsAndGetHashCode()
ReflectionAssert.AssertPublicAndInternalFieldsAndProperties(
typeof(CSharpParseOptions),
"Features",
+ "FileBasedProgram",
"Language",
"LanguageVersion",
"InterceptorsNamespaces",
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/IgnoredDirectiveParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/IgnoredDirectiveParsingTests.cs
new file mode 100644
index 0000000000000..5acd0beb53958
--- /dev/null
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/IgnoredDirectiveParsingTests.cs
@@ -0,0 +1,557 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.CodeAnalysis.CSharp.UnitTests;
+
+public sealed class IgnoredDirectiveParsingTests(ITestOutputHelper output) : ParsingTests(output)
+{
+ private const string FeatureName = "FileBasedProgram";
+
+ [Theory, CombinatorialData]
+ public void FeatureFlag(bool script)
+ {
+ var options = script ? TestOptions.Script : TestOptions.Regular;
+
+ var source = """
+ #!xyz
+ #:name value
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, options,
+ // (2,2): error CS9282: '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram')
+ // #:name value
+ Diagnostic(ErrorCode.ERR_PPIgnoredNeedsFileBasedProgram, ":").WithLocation(2, 2));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.ShebangDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ExclamationToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "xyz");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "name value");
+ }
+ }
+ }
+ }
+ EOF();
+
+ UsingTree(source, options.WithFeature(FeatureName));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.ShebangDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ExclamationToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "xyz");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "name value");
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void Semantics()
+ {
+ var source = """
+ #!xyz
+ #:name value
+ System.Console.WriteLine(123);
+ """;
+ CompileAndVerify(source,
+ parseOptions: TestOptions.Regular.WithFeature(FeatureName),
+ expectedOutput: "123").VerifyDiagnostics();
+ }
+
+ [Fact]
+ public void Api()
+ {
+ var source = """
+ #:abc
+ """;
+ var root = SyntaxFactory.ParseCompilationUnit(source, options: TestOptions.Regular.WithFeature(FeatureName));
+ var trivia = root.EndOfFileToken.GetLeadingTrivia().Single();
+ Assert.Equal(SyntaxKind.IgnoredDirectiveTrivia, trivia.Kind());
+ Assert.True(SyntaxFacts.IsPreprocessorDirective(trivia.Kind()));
+ Assert.True(SyntaxFacts.IsTrivia(trivia.Kind()));
+ var structure = (IgnoredDirectiveTriviaSyntax)trivia.GetStructure()!;
+ Assert.Equal(":", structure.DirectiveNameToken.ToFullString());
+ var messageTrivia = structure.EndOfDirectiveToken.GetLeadingTrivia().Single();
+ Assert.Equal(SyntaxKind.PreprocessingMessageTrivia, messageTrivia.Kind());
+ Assert.Equal("abc", messageTrivia.ToString());
+ trivia.GetDiagnostics().Verify();
+ }
+
+ [Fact]
+ public void Api_Diagnostics()
+ {
+ var source = """
+ #if X
+ #endif
+ #:abc
+ """;
+ var root = SyntaxFactory.ParseCompilationUnit(source, options: TestOptions.Regular.WithFeature(FeatureName));
+ var trivia = root.EndOfFileToken.GetLeadingTrivia().Last();
+ Assert.Equal(SyntaxKind.IgnoredDirectiveTrivia, trivia.Kind());
+ Assert.True(SyntaxFacts.IsPreprocessorDirective(trivia.Kind()));
+ Assert.True(SyntaxFacts.IsTrivia(trivia.Kind()));
+ var structure = (IgnoredDirectiveTriviaSyntax)trivia.GetStructure()!;
+ Assert.Equal(":", structure.DirectiveNameToken.ToFullString());
+ var messageTrivia = structure.EndOfDirectiveToken.GetLeadingTrivia().Single();
+ Assert.Equal(SyntaxKind.PreprocessingMessageTrivia, messageTrivia.Kind());
+ Assert.Equal("abc", messageTrivia.ToString());
+ trivia.GetDiagnostics().Verify(
+ // (3,2): error CS9283: '#:' directives cannot be after '#if' directive
+ // #:abc
+ Diagnostic(ErrorCode.ERR_PPIgnoredFollowsIf, ":").WithLocation(3, 2));
+ }
+
+ [Theory, CombinatorialData]
+ public void ShebangNotFirst(bool script, bool featureFlag)
+ {
+ var options = script ? TestOptions.Script : TestOptions.Regular;
+
+ if (featureFlag)
+ {
+ options = options.WithFeature(FeatureName);
+ }
+
+ var source = """
+ #!xyz
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, options,
+ // (1,2): error CS1024: Preprocessor directive expected
+ // #!xyz
+ Diagnostic(ErrorCode.ERR_PPDirectiveExpected, "#").WithLocation(1, 2));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.WhitespaceTrivia, " ");
+ L(SyntaxKind.BadDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ M(SyntaxKind.IdentifierToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.SkippedTokensTrivia);
+ {
+ N(SyntaxKind.ExclamationToken);
+ N(SyntaxKind.IdentifierToken, "xyz");
+ }
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void AfterToken()
+ {
+ var source = """
+ #:x
+ M();
+ #:y
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName),
+ // (3,2): error CS9281: '#:' directives cannot be after first token in file
+ // #:y
+ Diagnostic(ErrorCode.ERR_PPIgnoredFollowsToken, ":").WithLocation(3, 2));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.GlobalStatement);
+ {
+ N(SyntaxKind.ExpressionStatement);
+ {
+ N(SyntaxKind.InvocationExpression);
+ {
+ N(SyntaxKind.IdentifierName);
+ {
+ N(SyntaxKind.IdentifierToken, "M");
+ {
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "x");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ }
+ }
+ N(SyntaxKind.ArgumentList);
+ {
+ N(SyntaxKind.OpenParenToken);
+ N(SyntaxKind.CloseParenToken);
+ }
+ }
+ N(SyntaxKind.SemicolonToken);
+ {
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ }
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "y");
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void AfterIf()
+ {
+ var source = """
+ #:x
+ #if X
+ #:y
+ #endif
+ #:z
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName),
+ // (3,2): error CS9283: '#:' directives cannot be after '#if' directive
+ // #:y
+ Diagnostic(ErrorCode.ERR_PPIgnoredFollowsIf, ":").WithLocation(3, 2),
+ // (5,2): error CS9283: '#:' directives cannot be after '#if' directive
+ // #:z
+ Diagnostic(ErrorCode.ERR_PPIgnoredFollowsIf, ":").WithLocation(5, 2));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "x");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.IfDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.IfKeyword);
+ {
+ T(SyntaxKind.WhitespaceTrivia, " ");
+ }
+ N(SyntaxKind.IdentifierName);
+ {
+ N(SyntaxKind.IdentifierToken, "X");
+ }
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "y");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.EndIfDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.EndIfKeyword);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "z");
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void AfterComment()
+ {
+ var source = """
+ #:x
+ // comment
+ #:y
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "x");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.SingleLineCommentTrivia);
+ L(SyntaxKind.EndOfLineTrivia, "\n");
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "y");
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void AfterDefine()
+ {
+ var source = """
+ #:x
+ #define y
+ #:y
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "x");
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.DefineDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.DefineKeyword);
+ {
+ T(SyntaxKind.WhitespaceTrivia, " ");
+ }
+ N(SyntaxKind.IdentifierToken, "y");
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ T(SyntaxKind.EndOfLineTrivia, "\n");
+ }
+ }
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "y");
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void SpaceBeforeHash()
+ {
+ var source = """
+ #:x
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.WhitespaceTrivia, " ");
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.PreprocessingMessageTrivia, "x");
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void SpacesAfterHash()
+ {
+ var source = """
+ # : x
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName),
+ // (1,2): error CS1024: Preprocessor directive expected
+ // # : x
+ Diagnostic(ErrorCode.ERR_PPDirectiveExpected, "#").WithLocation(1, 2));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.WhitespaceTrivia, " ");
+ L(SyntaxKind.BadDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ {
+ T(SyntaxKind.WhitespaceTrivia, " ");
+ }
+ M(SyntaxKind.IdentifierToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ {
+ L(SyntaxKind.SkippedTokensTrivia);
+ {
+ N(SyntaxKind.ColonToken);
+ {
+ T(SyntaxKind.WhitespaceTrivia, " ");
+ }
+ N(SyntaxKind.IdentifierToken, "x");
+ }
+ }
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void NoMessage()
+ {
+ var source = """
+ #:
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.IgnoredDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ N(SyntaxKind.ColonToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ }
+ }
+ }
+ EOF();
+ }
+
+ [Fact]
+ public void NoColon()
+ {
+ var source = """
+ #
+ """;
+
+ VerifyTrivia();
+ UsingTree(source, TestOptions.Regular.WithFeature(FeatureName),
+ // (1,1): error CS1024: Preprocessor directive expected
+ // #
+ Diagnostic(ErrorCode.ERR_PPDirectiveExpected, "#").WithLocation(1, 1));
+
+ N(SyntaxKind.CompilationUnit);
+ {
+ N(SyntaxKind.EndOfFileToken);
+ {
+ L(SyntaxKind.BadDirectiveTrivia);
+ {
+ N(SyntaxKind.HashToken);
+ M(SyntaxKind.IdentifierToken);
+ N(SyntaxKind.EndOfDirectiveToken);
+ }
+ }
+ }
+ EOF();
+ }
+}
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs
index 930ced6a0a2bc..f69df41310e44 100644
--- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs
@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Test.Utilities;
+using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
@@ -20,7 +21,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
public abstract class ParsingTests : CSharpTestBase
{
private CSharpSyntaxNode? _node;
- private IEnumerator? _treeEnumerator;
+ private IEnumerator? _treeEnumerator;
+ private bool _verifyTrivia;
private readonly ITestOutputHelper _output;
public ParsingTests(ITestOutputHelper output)
@@ -192,6 +194,12 @@ protected void UsingNode(CSharpSyntaxNode root)
_treeEnumerator = nodes.GetEnumerator();
}
+ protected void VerifyTrivia(bool enabled = true)
+ {
+ VerifyEnumeratorConsumed();
+ _verifyTrivia = enabled;
+ }
+
///
/// Moves the enumerator and asserts that the current node is of the given kind.
///
@@ -204,6 +212,7 @@ protected SyntaxNodeOrToken N(SyntaxKind kind, string? value = null)
var current = _treeEnumerator.Current;
Assert.Equal(kind, current.Kind());
+ Assert.False(current.IsTrivia);
Assert.False(current.IsMissing);
if (value != null)
@@ -211,7 +220,7 @@ protected SyntaxNodeOrToken N(SyntaxKind kind, string? value = null)
Assert.Equal(current.ToString(), value);
}
- return current;
+ return current.NodeOrToken;
}
catch when (DumpAndCleanup())
{
@@ -229,10 +238,53 @@ protected SyntaxNodeOrToken M(SyntaxKind kind)
try
{
Assert.True(_treeEnumerator!.MoveNext());
- SyntaxNodeOrToken current = _treeEnumerator.Current;
+ var current = _treeEnumerator.Current;
Assert.Equal(kind, current.Kind());
Assert.True(current.IsMissing);
- return current;
+ return current.NodeOrToken;
+ }
+ catch when (DumpAndCleanup())
+ {
+ throw;
+ }
+ }
+
+ ///
+ /// Asserts leading trivia.
+ ///
+ [DebuggerHidden]
+ protected SyntaxTrivia L(SyntaxKind kind, string? value = null)
+ {
+ return Trivia(kind, value, trailing: false);
+ }
+
+ ///
+ /// Asserts trailing trivia.
+ ///
+ [DebuggerHidden]
+ protected SyntaxTrivia T(SyntaxKind kind, string? value = null)
+ {
+ return Trivia(kind, value, trailing: true);
+ }
+
+ [DebuggerHidden]
+ private SyntaxTrivia Trivia(SyntaxKind kind, string? value, bool trailing)
+ {
+ try
+ {
+ Assert.True(_treeEnumerator!.MoveNext());
+ var current = _treeEnumerator.Current;
+
+ Assert.Equal(kind, current.Kind());
+ Assert.True(current.IsTrivia);
+ Assert.Equal(trailing, current.IsTrailing);
+
+ if (value != null)
+ {
+ Assert.Equal(current.ToString().ReplaceLineEndings("\n"), value);
+ }
+
+ return current.Trivia;
}
catch when (DumpAndCleanup())
{
@@ -254,35 +306,80 @@ protected void EOF()
}
}
- private IEnumerable EnumerateNodes(CSharpSyntaxNode node, bool dump)
+ private IEnumerable EnumerateNodes(CSharpSyntaxNode node, bool dump)
{
Print(node, dump);
yield return node;
- var stack = new Stack(24);
- stack.Push(node.ChildNodesAndTokens().GetEnumerator());
+ var stack = new Stack<(SyntaxTriviaList.Enumerator, ChildSyntaxList.Enumerator, SyntaxTriviaList.Enumerator)>(24);
+ stack.Push((default, node.ChildNodesAndTokens().GetEnumerator(), default));
Open(dump);
while (stack.Count > 0)
{
- var en = stack.Pop();
- if (!en.MoveNext())
+ var (en1, en2, en3) = stack.Pop();
+
+ byte en;
+ SyntaxTrivia currentTrivia = default;
+ SyntaxNodeOrToken currentChild = default;
+
+ if (en1.MoveNext())
+ {
+ currentTrivia = en1.Current;
+ en = 1;
+ }
+ else if (en2.MoveNext())
+ {
+ currentChild = en2.Current;
+ en = 2;
+ }
+ else if (en3.MoveNext())
+ {
+ currentTrivia = en3.Current;
+ en = 3;
+ }
+ else
{
// no more down this branch
Close(dump);
continue;
}
- var current = en.Current;
- stack.Push(en); // put it back on stack (struct enumerator)
+ stack.Push((en1, en2, en3)); // put it back on stack (struct enumerator)
+
+ if (en != 2) // we are on a trivia
+ {
+ Print(currentTrivia, trailing: en == 3, dump);
+ yield return new SyntaxNodeOrTokenOrTrivia(currentTrivia, trailing: en == 3);
- Print(current, dump);
- yield return current;
+ if (currentTrivia.TryGetStructure(out var triviaStructure))
+ {
+ // trivia has a structure, so consider its children
+ stack.Push((default, triviaStructure.ChildNodesAndTokens().GetEnumerator(), default));
+ Open(dump);
+ }
- if (current.IsNode)
+ continue;
+ }
+
+ Print(currentChild, dump);
+ yield return currentChild;
+
+ if (currentChild.AsNode(out var currentChildNode))
{
// not token, so consider children
- stack.Push(current.ChildNodesAndTokens().GetEnumerator());
+ stack.Push((default, currentChildNode.ChildNodesAndTokens().GetEnumerator(), default));
+ Open(dump);
+ continue;
+ }
+
+ if (_verifyTrivia && currentChild.AsToken(out var currentChildToken) &&
+ (currentChildToken.HasLeadingTrivia || currentChildToken.HasTrailingTrivia))
+ {
+ // token with trivia
+ stack.Push((currentChildToken.GetLeadingTrivia().GetEnumerator(),
+ default,
+ currentChildToken.GetTrailingTrivia().GetEnumerator()));
Open(dump);
continue;
}
@@ -295,30 +392,53 @@ private void Print(SyntaxNodeOrToken node, bool dump)
{
if (dump)
{
- switch (node.Kind())
+ if (!node.IsMissing && ShouldIncludeText(node.Kind()))
{
- case SyntaxKind.IdentifierToken:
- case SyntaxKind.NumericLiteralToken:
- case SyntaxKind.StringLiteralToken:
- case SyntaxKind.Utf8StringLiteralToken:
- case SyntaxKind.SingleLineRawStringLiteralToken:
- case SyntaxKind.Utf8SingleLineRawStringLiteralToken:
- case SyntaxKind.MultiLineRawStringLiteralToken:
- case SyntaxKind.Utf8MultiLineRawStringLiteralToken:
- if (node.IsMissing)
- {
- goto default;
- }
- var value = node.ToString().Replace("\"", "\\\"");
- _output.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), value);
- break;
- default:
- _output.WriteLine("{0}(SyntaxKind.{1});", node.IsMissing ? "M" : "N", node.Kind());
- break;
+ var value = node.ToString().Replace("\"", "\\\"");
+ _output.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), value);
}
+ else
+ {
+ _output.WriteLine("{0}(SyntaxKind.{1});", node.IsMissing ? "M" : "N", node.Kind());
+ }
+ }
+ }
+
+ private void Print(SyntaxTrivia trivia, bool trailing, bool dump)
+ {
+ if (dump)
+ {
+ string? value = ShouldIncludeText(trivia.Kind())
+ ? $"""
+ , "{trivia.ToString().Replace("\"", "\\\"").ReplaceLineEndings("\\n")}"
+ """
+ : null;
+ _output.WriteLine($"""
+ {(trailing ? "T" : "L")}(SyntaxKind.{trivia.Kind()}{value});
+ """);
}
}
+ private static bool ShouldIncludeText(SyntaxKind kind)
+ {
+ // This can be changed without failing existing tests,
+ // it only affects the baseline output printed when a test fails.
+ return kind
+ is SyntaxKind.IdentifierToken
+ or SyntaxKind.NumericLiteralToken
+ or SyntaxKind.StringLiteralToken
+ or SyntaxKind.Utf8StringLiteralToken
+ or SyntaxKind.SingleLineRawStringLiteralToken
+ or SyntaxKind.Utf8SingleLineRawStringLiteralToken
+ or SyntaxKind.MultiLineRawStringLiteralToken
+ or SyntaxKind.Utf8MultiLineRawStringLiteralToken
+ or SyntaxKind.BadToken
+ or SyntaxKind.WhitespaceTrivia
+ or SyntaxKind.EndOfLineTrivia
+ or SyntaxKind.PreprocessingMessageTrivia
+ ;
+ }
+
private void Open(bool dump)
{
if (dump)
@@ -379,5 +499,45 @@ protected static void ParseIncompleteSyntax(string text)
return tokensBuilder.ToImmutableAndFree();
}
}
+
+ private readonly struct SyntaxNodeOrTokenOrTrivia
+ {
+ public bool IsTrivia { get; }
+ public bool IsTrailing { get; }
+ public SyntaxNodeOrToken NodeOrToken { get; }
+ public SyntaxTrivia Trivia { get; }
+
+ public SyntaxNodeOrTokenOrTrivia(SyntaxNodeOrToken nodeOrToken)
+ {
+ NodeOrToken = nodeOrToken;
+ Trivia = default;
+ IsTrivia = false;
+ IsTrailing = false;
+ }
+
+ public SyntaxNodeOrTokenOrTrivia(SyntaxTrivia trivia, bool trailing)
+ {
+ NodeOrToken = default;
+ Trivia = trivia;
+ IsTrivia = true;
+ IsTrailing = trailing;
+ }
+
+ public bool IsMissing => !IsTrivia && NodeOrToken.IsMissing;
+
+ public SyntaxKind Kind() => IsTrivia ? Trivia.Kind() : NodeOrToken.Kind();
+
+ public override string ToString() => IsTrivia ? Trivia.ToString() : NodeOrToken.ToString();
+
+ public static implicit operator SyntaxNodeOrTokenOrTrivia(SyntaxNode node)
+ {
+ return new SyntaxNodeOrTokenOrTrivia(node);
+ }
+
+ public static implicit operator SyntaxNodeOrTokenOrTrivia(SyntaxNodeOrToken nodeOrToken)
+ {
+ return new SyntaxNodeOrTokenOrTrivia(nodeOrToken);
+ }
+ }
}
}
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs
index 18a55b0dd7804..1ed38eebcfb49 100644
--- a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs
@@ -9716,8 +9716,7 @@ public void ShebangInComment()
[Fact]
public void ShebangNotInScript()
{
- ParseAndValidate("#!/usr/bin/env csi", TestOptions.Regular,
- new ErrorDescription { Code = (int)ErrorCode.ERR_PPDirectiveExpected, Line = 1, Column = 1 });
+ ParseAndValidate("#!/usr/bin/env csi", TestOptions.Regular);
}
private void TestShebang(SyntaxTrivia trivia, string expectedSkippedText)
diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs
index 645ee0ad29ad3..913605abf6fbc 100644
--- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs
@@ -383,10 +383,11 @@ public void TestContainsDirective()
testContainsHelper1("#undef x", SyntaxKind.UndefDirectiveTrivia);
testContainsHelper1("#warning", SyntaxKind.WarningDirectiveTrivia);
- // #! is special and is only recognized at start of a script file and nowhere else.
+ // #! is special and is only recognized at start of a file and nowhere else.
testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Script));
testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit(" #!command", options: TestOptions.Script));
- testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular));
+ testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular));
+ testContainsHelper2([SyntaxKind.IgnoredDirectiveTrivia], SyntaxFactory.ParseCompilationUnit("#:x"));
return;
@@ -470,7 +471,7 @@ static void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSynt
{
Assert.True(compilationUnit.ContainsDirectives);
foreach (var directiveKind in directiveKinds)
- Assert.True(compilationUnit.ContainsDirective(directiveKind));
+ Assert.True(compilationUnit.ContainsDirective(directiveKind), directiveKind.ToString());
for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++)
{
diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs
index 4abda44062fae..856a0faa50743 100644
--- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs
@@ -3705,6 +3705,27 @@ void Goo(A x)
""");
}
+ [Fact]
+ public void IgnoredDirectives()
+ {
+ TestNormalizeDeclaration("""
+ #:a
+ #: b c
+ {
+ #:d
+ }
+ #:e
+ """, """
+ #:a
+ #:b c
+ {
+ #:d
+ }
+ #:e
+
+ """);
+ }
+
[Fact, WorkItem(542887, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542887")]
public void TestFormattingForBlockSyntax()
{
diff --git a/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs
index 9394f13b21c30..3919a71673d49 100644
--- a/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs
+++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs
@@ -17,4 +17,7 @@ internal static class RoslynExperiments
internal const string GeneratorHostOutputs = "RSEXPERIMENTAL004";
internal const string GeneratorHostOutputs_Url = "https://github.com/dotnet/roslyn/issues/74753";
+
+ internal const string IgnoredDirectives = "RSEXPERIMENTAL005";
+ internal const string IgnoredDirectives_Url = "https://github.com/dotnet/roslyn/issues/77697";
}
diff --git a/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs
index f8f1d60ede2ea..ca95cc1bf5288 100644
--- a/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs
+++ b/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs
@@ -55,7 +55,7 @@ protected override CSharpCompilation CreateCompilation(SyntaxTree[] syntaxTrees,
public void VerifyUpToDate()
{
verifyCount(11);
- verifyCount(12);
+ verifyCount(13);
verifyCount(63);
verifyCount(9);
diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs
index 28358c6810405..a84ea085613c6 100644
--- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs
@@ -1504,8 +1504,7 @@ public async Task ShebangAsFirstCommentInNonScript(TestHost testHost)
var expected = new[]
{
- PPKeyword("#"),
- PPText("!/usr/bin/env scriptcs"),
+ Comment("#!/usr/bin/env scriptcs"),
Identifier("System"),
Operators.Dot,
Identifier("Console"),
diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt
index b37e71720462c..31577ead6d1b7 100644
--- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt
+++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt
@@ -431,6 +431,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitGroupClause(Microsoft.Co
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIfDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IfDirectiveTriviaSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIfStatement(Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax)
+Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitImplicitElementAccess(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitElementAccessSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax)
@@ -726,6 +727,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitGroupClause(Microsoft.Cod
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIfDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IfDirectiveTriviaSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIfStatement(Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax)
+Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitElementAccess(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitElementAccessSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax)
@@ -975,6 +977,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitGroupClause(Microsoft.C
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIfDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IfDirectiveTriviaSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIfStatement(Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax)
+Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitImplicitElementAccess(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitElementAccessSyntax)
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax)
@@ -2826,6 +2829,18 @@ Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_Else
Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_IfKeyword
Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_OpenParenToken
Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_Statement
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor)
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0})
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,System.Boolean)
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken)
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithEndOfDirectiveToken(Microsoft.CodeAnalysis.SyntaxToken)
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithHashToken(Microsoft.CodeAnalysis.SyntaxToken)
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithIsActive(System.Boolean)
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_ColonToken
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_EndOfDirectiveToken
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_HashToken
+Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_IsActive
Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor)
Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0})
@@ -5060,6 +5075,8 @@ Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.C
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ElseClauseSyntax)
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ElseClauseSyntax)
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ElseClauseSyntax)
+Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,System.Boolean)
+Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(System.Boolean)
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax)
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitArrayCreationExpression(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax)
Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitArrayCreationExpression(Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax)
@@ -5915,6 +5932,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.IdentifierToken
Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfDirectiveTrivia
Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfKeyword
Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfStatement
+Microsoft.CodeAnalysis.CSharp.SyntaxKind.IgnoredDirectiveTrivia
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitArrayCreationExpression
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitElementAccess
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitKeyword