diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 2e5a1dac19235..00140848865b4 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6634,6 +6634,15 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ inferred delegate type + + line span directive + + + The #line directive value is missing or out of range + + + The #line directive end position must be greater than or equal to the start position + Comparison of function pointers might yield an unexpected result, since pointers to the same function may be distinct. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 16f5d833134d0..d13a861e65fbe 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1960,6 +1960,8 @@ internal enum ErrorCode ERR_BuilderAttributeDisallowed = 8935, ERR_FeatureNotAvailableInVersion10 = 8936, ERR_SimpleProgramIsEmpty = 8937, + ERR_LineSpanDirectiveInvalidValue = 8938, + ERR_LineSpanDirectiveEndLessThanStart = 8939, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 50a2a6dc96cda..65c413719f3a6 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -229,6 +229,7 @@ internal enum MessageID IDS_FeatureLambdaReturnType = MessageBase + 12804, IDS_AsyncMethodBuilderOverride = MessageBase + 12805, IDS_FeatureImplicitImplementationOfNonPublicMemebers = MessageBase + 12806, + IDS_FeatureLineSpanDirective = MessageBase + 12807, } // Message IDs may refer to strings that need to be localized. @@ -354,6 +355,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_AsyncMethodBuilderOverride: // semantic check case MessageID.IDS_FeatureConstantInterpolatedStrings: // semantic check case MessageID.IDS_FeatureImplicitImplementationOfNonPublicMemebers: // semantic check + case MessageID.IDS_FeatureLineSpanDirective: return LanguageVersion.CSharp10; // C# 9.0 features. diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 089c1b1d58d3e..9450e00d62f58 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1180,7 +1180,7 @@ directive_trivia | end_if_directive_trivia | end_region_directive_trivia | error_directive_trivia - | line_directive_trivia + | line_or_span_directive_trivia | load_directive_trivia | nullable_directive_trivia | pragma_checksum_directive_trivia @@ -1234,10 +1234,23 @@ error_directive_trivia : '#' 'error' ; +line_or_span_directive_trivia + : line_directive_trivia + | line_span_directive_trivia + ; + line_directive_trivia : '#' 'line' (numeric_literal_token | 'default' | 'hidden') string_literal_token? ; +line_span_directive_trivia + : '#' 'line' line_directive_position '-' line_directive_position numeric_literal_token? string_literal_token + ; + +line_directive_position + : '(' numeric_literal_token ',' numeric_literal_token ')' + ; + load_directive_trivia : '#' 'load' string_literal_token ; 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 05a6ef6458e3f..091928df6b8dc 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 @@ -32208,7 +32208,29 @@ static UndefDirectiveTriviaSyntax() } } - internal sealed partial class LineDirectiveTriviaSyntax : DirectiveTriviaSyntax + internal abstract partial class LineOrSpanDirectiveTriviaSyntax : DirectiveTriviaSyntax + { + internal LineOrSpanDirectiveTriviaSyntax(SyntaxKind kind, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + } + + internal LineOrSpanDirectiveTriviaSyntax(SyntaxKind kind) + : base(kind) + { + } + + protected LineOrSpanDirectiveTriviaSyntax(ObjectReader reader) + : base(reader) + { + } + + public abstract SyntaxToken LineKeyword { get; } + + public abstract SyntaxToken? File { get; } + } + + internal sealed partial class LineDirectiveTriviaSyntax : LineOrSpanDirectiveTriviaSyntax { internal readonly SyntaxToken hashToken; internal readonly SyntaxToken lineKeyword; @@ -32279,9 +32301,9 @@ internal LineDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, Synta } public override SyntaxToken HashToken => this.hashToken; - public SyntaxToken LineKeyword => this.lineKeyword; + public override SyntaxToken LineKeyword => this.lineKeyword; public SyntaxToken Line => this.line; - public SyntaxToken? File => this.file; + public override SyntaxToken? File => this.file; public override SyntaxToken EndOfDirectiveToken => this.endOfDirectiveToken; public override bool IsActive => this.isActive; @@ -32366,6 +32388,342 @@ static LineDirectiveTriviaSyntax() } } + internal sealed partial class LineDirectivePositionSyntax : CSharpSyntaxNode + { + internal readonly SyntaxToken openParenToken; + internal readonly SyntaxToken line; + internal readonly SyntaxToken commaToken; + internal readonly SyntaxToken character; + internal readonly SyntaxToken closeParenToken; + + internal LineDirectivePositionSyntax(SyntaxKind kind, SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 5; + this.AdjustFlagsAndWidth(openParenToken); + this.openParenToken = openParenToken; + this.AdjustFlagsAndWidth(line); + this.line = line; + this.AdjustFlagsAndWidth(commaToken); + this.commaToken = commaToken; + this.AdjustFlagsAndWidth(character); + this.character = character; + this.AdjustFlagsAndWidth(closeParenToken); + this.closeParenToken = closeParenToken; + } + + internal LineDirectivePositionSyntax(SyntaxKind kind, SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 5; + this.AdjustFlagsAndWidth(openParenToken); + this.openParenToken = openParenToken; + this.AdjustFlagsAndWidth(line); + this.line = line; + this.AdjustFlagsAndWidth(commaToken); + this.commaToken = commaToken; + this.AdjustFlagsAndWidth(character); + this.character = character; + this.AdjustFlagsAndWidth(closeParenToken); + this.closeParenToken = closeParenToken; + } + + internal LineDirectivePositionSyntax(SyntaxKind kind, SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken) + : base(kind) + { + this.SlotCount = 5; + this.AdjustFlagsAndWidth(openParenToken); + this.openParenToken = openParenToken; + this.AdjustFlagsAndWidth(line); + this.line = line; + this.AdjustFlagsAndWidth(commaToken); + this.commaToken = commaToken; + this.AdjustFlagsAndWidth(character); + this.character = character; + this.AdjustFlagsAndWidth(closeParenToken); + this.closeParenToken = closeParenToken; + } + + public SyntaxToken OpenParenToken => this.openParenToken; + public SyntaxToken Line => this.line; + public SyntaxToken CommaToken => this.commaToken; + public SyntaxToken Character => this.character; + public SyntaxToken CloseParenToken => this.closeParenToken; + + internal override GreenNode? GetSlot(int index) + => index switch + { + 0 => this.openParenToken, + 1 => this.line, + 2 => this.commaToken, + 3 => this.character, + 4 => this.closeParenToken, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.LineDirectivePositionSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitLineDirectivePosition(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitLineDirectivePosition(this); + + public LineDirectivePositionSyntax Update(SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken) + { + if (openParenToken != this.OpenParenToken || line != this.Line || commaToken != this.CommaToken || character != this.Character || closeParenToken != this.CloseParenToken) + { + var newNode = SyntaxFactory.LineDirectivePosition(openParenToken, line, commaToken, character, closeParenToken); + 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 LineDirectivePositionSyntax(this.Kind, this.openParenToken, this.line, this.commaToken, this.character, this.closeParenToken, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new LineDirectivePositionSyntax(this.Kind, this.openParenToken, this.line, this.commaToken, this.character, this.closeParenToken, GetDiagnostics(), annotations); + + internal LineDirectivePositionSyntax(ObjectReader reader) + : base(reader) + { + this.SlotCount = 5; + var openParenToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(openParenToken); + this.openParenToken = openParenToken; + var line = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(line); + this.line = line; + var commaToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(commaToken); + this.commaToken = commaToken; + var character = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(character); + this.character = character; + var closeParenToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(closeParenToken); + this.closeParenToken = closeParenToken; + } + + internal override void WriteTo(ObjectWriter writer) + { + base.WriteTo(writer); + writer.WriteValue(this.openParenToken); + writer.WriteValue(this.line); + writer.WriteValue(this.commaToken); + writer.WriteValue(this.character); + writer.WriteValue(this.closeParenToken); + } + + static LineDirectivePositionSyntax() + { + ObjectBinder.RegisterTypeReader(typeof(LineDirectivePositionSyntax), r => new LineDirectivePositionSyntax(r)); + } + } + + internal sealed partial class LineSpanDirectiveTriviaSyntax : LineOrSpanDirectiveTriviaSyntax + { + internal readonly SyntaxToken hashToken; + internal readonly SyntaxToken lineKeyword; + internal readonly LineDirectivePositionSyntax start; + internal readonly SyntaxToken minusToken; + internal readonly LineDirectivePositionSyntax end; + internal readonly SyntaxToken? characterOffset; + internal readonly SyntaxToken file; + internal readonly SyntaxToken endOfDirectiveToken; + internal readonly bool isActive; + + internal LineSpanDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken? characterOffset, SyntaxToken file, SyntaxToken endOfDirectiveToken, bool isActive, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 8; + this.AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + this.AdjustFlagsAndWidth(lineKeyword); + this.lineKeyword = lineKeyword; + this.AdjustFlagsAndWidth(start); + this.start = start; + this.AdjustFlagsAndWidth(minusToken); + this.minusToken = minusToken; + this.AdjustFlagsAndWidth(end); + this.end = end; + if (characterOffset != null) + { + this.AdjustFlagsAndWidth(characterOffset); + this.characterOffset = characterOffset; + } + this.AdjustFlagsAndWidth(file); + this.file = file; + this.AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = isActive; + } + + internal LineSpanDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken? characterOffset, SyntaxToken file, SyntaxToken endOfDirectiveToken, bool isActive, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 8; + this.AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + this.AdjustFlagsAndWidth(lineKeyword); + this.lineKeyword = lineKeyword; + this.AdjustFlagsAndWidth(start); + this.start = start; + this.AdjustFlagsAndWidth(minusToken); + this.minusToken = minusToken; + this.AdjustFlagsAndWidth(end); + this.end = end; + if (characterOffset != null) + { + this.AdjustFlagsAndWidth(characterOffset); + this.characterOffset = characterOffset; + } + this.AdjustFlagsAndWidth(file); + this.file = file; + this.AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = isActive; + } + + internal LineSpanDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken? characterOffset, SyntaxToken file, SyntaxToken endOfDirectiveToken, bool isActive) + : base(kind) + { + this.SlotCount = 8; + this.AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + this.AdjustFlagsAndWidth(lineKeyword); + this.lineKeyword = lineKeyword; + this.AdjustFlagsAndWidth(start); + this.start = start; + this.AdjustFlagsAndWidth(minusToken); + this.minusToken = minusToken; + this.AdjustFlagsAndWidth(end); + this.end = end; + if (characterOffset != null) + { + this.AdjustFlagsAndWidth(characterOffset); + this.characterOffset = characterOffset; + } + this.AdjustFlagsAndWidth(file); + this.file = file; + this.AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = isActive; + } + + public override SyntaxToken HashToken => this.hashToken; + public override SyntaxToken LineKeyword => this.lineKeyword; + public LineDirectivePositionSyntax Start => this.start; + public SyntaxToken MinusToken => this.minusToken; + public LineDirectivePositionSyntax End => this.end; + public SyntaxToken? CharacterOffset => this.characterOffset; + public override SyntaxToken File => this.file; + 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.lineKeyword, + 2 => this.start, + 3 => this.minusToken, + 4 => this.end, + 5 => this.characterOffset, + 6 => this.file, + 7 => this.endOfDirectiveToken, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.LineSpanDirectiveTriviaSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitLineSpanDirectiveTrivia(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitLineSpanDirectiveTrivia(this); + + public LineSpanDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken characterOffset, SyntaxToken file, SyntaxToken endOfDirectiveToken, bool isActive) + { + if (hashToken != this.HashToken || lineKeyword != this.LineKeyword || start != this.Start || minusToken != this.MinusToken || end != this.End || characterOffset != this.CharacterOffset || file != this.File || endOfDirectiveToken != this.EndOfDirectiveToken) + { + var newNode = SyntaxFactory.LineSpanDirectiveTrivia(hashToken, lineKeyword, start, minusToken, end, characterOffset, file, 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 LineSpanDirectiveTriviaSyntax(this.Kind, this.hashToken, this.lineKeyword, this.start, this.minusToken, this.end, this.characterOffset, this.file, this.endOfDirectiveToken, this.isActive, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new LineSpanDirectiveTriviaSyntax(this.Kind, this.hashToken, this.lineKeyword, this.start, this.minusToken, this.end, this.characterOffset, this.file, this.endOfDirectiveToken, this.isActive, GetDiagnostics(), annotations); + + internal LineSpanDirectiveTriviaSyntax(ObjectReader reader) + : base(reader) + { + this.SlotCount = 8; + var hashToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + var lineKeyword = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(lineKeyword); + this.lineKeyword = lineKeyword; + var start = (LineDirectivePositionSyntax)reader.ReadValue(); + AdjustFlagsAndWidth(start); + this.start = start; + var minusToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(minusToken); + this.minusToken = minusToken; + var end = (LineDirectivePositionSyntax)reader.ReadValue(); + AdjustFlagsAndWidth(end); + this.end = end; + var characterOffset = (SyntaxToken?)reader.ReadValue(); + if (characterOffset != null) + { + AdjustFlagsAndWidth(characterOffset); + this.characterOffset = characterOffset; + } + var file = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(file); + this.file = file; + var endOfDirectiveToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = (bool)reader.ReadBoolean(); + } + + internal override void WriteTo(ObjectWriter writer) + { + base.WriteTo(writer); + writer.WriteValue(this.hashToken); + writer.WriteValue(this.lineKeyword); + writer.WriteValue(this.start); + writer.WriteValue(this.minusToken); + writer.WriteValue(this.end); + writer.WriteValue(this.characterOffset); + writer.WriteValue(this.file); + writer.WriteValue(this.endOfDirectiveToken); + writer.WriteBoolean(this.isActive); + } + + static LineSpanDirectiveTriviaSyntax() + { + ObjectBinder.RegisterTypeReader(typeof(LineSpanDirectiveTriviaSyntax), r => new LineSpanDirectiveTriviaSyntax(r)); + } + } + internal sealed partial class PragmaWarningDirectiveTriviaSyntax : DirectiveTriviaSyntax { internal readonly SyntaxToken hashToken; @@ -33481,6 +33839,8 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitDefineDirectiveTrivia(DefineDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitUndefDirectiveTrivia(UndefDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitLineDirectiveTrivia(LineDirectiveTriviaSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitLineDirectivePosition(LineDirectivePositionSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitLineSpanDirectiveTrivia(LineSpanDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitPragmaWarningDirectiveTrivia(PragmaWarningDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitPragmaChecksumDirectiveTrivia(PragmaChecksumDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node) => this.DefaultVisit(node); @@ -33717,6 +34077,8 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitDefineDirectiveTrivia(DefineDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitUndefDirectiveTrivia(UndefDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitLineDirectiveTrivia(LineDirectiveTriviaSyntax node) => this.DefaultVisit(node); + public virtual void VisitLineDirectivePosition(LineDirectivePositionSyntax node) => this.DefaultVisit(node); + public virtual void VisitLineSpanDirectiveTrivia(LineSpanDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitPragmaWarningDirectiveTrivia(PragmaWarningDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitPragmaChecksumDirectiveTrivia(PragmaChecksumDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node) => this.DefaultVisit(node); @@ -34405,6 +34767,12 @@ public override CSharpSyntaxNode VisitUndefDirectiveTrivia(UndefDirectiveTriviaS public override CSharpSyntaxNode VisitLineDirectiveTrivia(LineDirectiveTriviaSyntax node) => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.LineKeyword), (SyntaxToken)Visit(node.Line), (SyntaxToken)Visit(node.File), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive); + public override CSharpSyntaxNode VisitLineDirectivePosition(LineDirectivePositionSyntax node) + => node.Update((SyntaxToken)Visit(node.OpenParenToken), (SyntaxToken)Visit(node.Line), (SyntaxToken)Visit(node.CommaToken), (SyntaxToken)Visit(node.Character), (SyntaxToken)Visit(node.CloseParenToken)); + + public override CSharpSyntaxNode VisitLineSpanDirectiveTrivia(LineSpanDirectiveTriviaSyntax node) + => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.LineKeyword), (LineDirectivePositionSyntax)Visit(node.Start), (SyntaxToken)Visit(node.MinusToken), (LineDirectivePositionSyntax)Visit(node.End), (SyntaxToken)Visit(node.CharacterOffset), (SyntaxToken)Visit(node.File), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive); + public override CSharpSyntaxNode VisitPragmaWarningDirectiveTrivia(PragmaWarningDirectiveTriviaSyntax node) => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.PragmaKeyword), (SyntaxToken)Visit(node.WarningKeyword), (SyntaxToken)Visit(node.DisableOrRestoreKeyword), VisitList(node.ErrorCodes), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive); @@ -39218,6 +39586,53 @@ public LineDirectiveTriviaSyntax LineDirectiveTrivia(SyntaxToken hashToken, Synt return new LineDirectiveTriviaSyntax(SyntaxKind.LineDirectiveTrivia, hashToken, lineKeyword, line, file, endOfDirectiveToken, isActive, this.context); } + public LineDirectivePositionSyntax LineDirectivePosition(SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken) + { +#if DEBUG + if (openParenToken == null) throw new ArgumentNullException(nameof(openParenToken)); + if (openParenToken.Kind != SyntaxKind.OpenParenToken) throw new ArgumentException(nameof(openParenToken)); + if (line == null) throw new ArgumentNullException(nameof(line)); + if (line.Kind != SyntaxKind.NumericLiteralToken) throw new ArgumentException(nameof(line)); + if (commaToken == null) throw new ArgumentNullException(nameof(commaToken)); + if (commaToken.Kind != SyntaxKind.CommaToken) throw new ArgumentException(nameof(commaToken)); + if (character == null) throw new ArgumentNullException(nameof(character)); + if (character.Kind != SyntaxKind.NumericLiteralToken) throw new ArgumentException(nameof(character)); + if (closeParenToken == null) throw new ArgumentNullException(nameof(closeParenToken)); + if (closeParenToken.Kind != SyntaxKind.CloseParenToken) throw new ArgumentException(nameof(closeParenToken)); +#endif + + return new LineDirectivePositionSyntax(SyntaxKind.LineDirectivePosition, openParenToken, line, commaToken, character, closeParenToken, this.context); + } + + public LineSpanDirectiveTriviaSyntax LineSpanDirectiveTrivia(SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken? characterOffset, SyntaxToken file, 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 (lineKeyword == null) throw new ArgumentNullException(nameof(lineKeyword)); + if (lineKeyword.Kind != SyntaxKind.LineKeyword) throw new ArgumentException(nameof(lineKeyword)); + if (start == null) throw new ArgumentNullException(nameof(start)); + if (minusToken == null) throw new ArgumentNullException(nameof(minusToken)); + if (minusToken.Kind != SyntaxKind.MinusToken) throw new ArgumentException(nameof(minusToken)); + if (end == null) throw new ArgumentNullException(nameof(end)); + if (characterOffset != null) + { + switch (characterOffset.Kind) + { + case SyntaxKind.NumericLiteralToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(characterOffset)); + } + } + if (file == null) throw new ArgumentNullException(nameof(file)); + if (file.Kind != SyntaxKind.StringLiteralToken) throw new ArgumentException(nameof(file)); + if (endOfDirectiveToken == null) throw new ArgumentNullException(nameof(endOfDirectiveToken)); + if (endOfDirectiveToken.Kind != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken)); +#endif + + return new LineSpanDirectiveTriviaSyntax(SyntaxKind.LineSpanDirectiveTrivia, hashToken, lineKeyword, start, minusToken, end, characterOffset, file, endOfDirectiveToken, isActive, this.context); + } + public PragmaWarningDirectiveTriviaSyntax PragmaWarningDirectiveTrivia(SyntaxToken hashToken, SyntaxToken pragmaKeyword, SyntaxToken warningKeyword, SyntaxToken disableOrRestoreKeyword, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList errorCodes, SyntaxToken endOfDirectiveToken, bool isActive) { #if DEBUG @@ -44131,6 +44546,53 @@ public static LineDirectiveTriviaSyntax LineDirectiveTrivia(SyntaxToken hashToke return new LineDirectiveTriviaSyntax(SyntaxKind.LineDirectiveTrivia, hashToken, lineKeyword, line, file, endOfDirectiveToken, isActive); } + public static LineDirectivePositionSyntax LineDirectivePosition(SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken) + { +#if DEBUG + if (openParenToken == null) throw new ArgumentNullException(nameof(openParenToken)); + if (openParenToken.Kind != SyntaxKind.OpenParenToken) throw new ArgumentException(nameof(openParenToken)); + if (line == null) throw new ArgumentNullException(nameof(line)); + if (line.Kind != SyntaxKind.NumericLiteralToken) throw new ArgumentException(nameof(line)); + if (commaToken == null) throw new ArgumentNullException(nameof(commaToken)); + if (commaToken.Kind != SyntaxKind.CommaToken) throw new ArgumentException(nameof(commaToken)); + if (character == null) throw new ArgumentNullException(nameof(character)); + if (character.Kind != SyntaxKind.NumericLiteralToken) throw new ArgumentException(nameof(character)); + if (closeParenToken == null) throw new ArgumentNullException(nameof(closeParenToken)); + if (closeParenToken.Kind != SyntaxKind.CloseParenToken) throw new ArgumentException(nameof(closeParenToken)); +#endif + + return new LineDirectivePositionSyntax(SyntaxKind.LineDirectivePosition, openParenToken, line, commaToken, character, closeParenToken); + } + + public static LineSpanDirectiveTriviaSyntax LineSpanDirectiveTrivia(SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken? characterOffset, SyntaxToken file, 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 (lineKeyword == null) throw new ArgumentNullException(nameof(lineKeyword)); + if (lineKeyword.Kind != SyntaxKind.LineKeyword) throw new ArgumentException(nameof(lineKeyword)); + if (start == null) throw new ArgumentNullException(nameof(start)); + if (minusToken == null) throw new ArgumentNullException(nameof(minusToken)); + if (minusToken.Kind != SyntaxKind.MinusToken) throw new ArgumentException(nameof(minusToken)); + if (end == null) throw new ArgumentNullException(nameof(end)); + if (characterOffset != null) + { + switch (characterOffset.Kind) + { + case SyntaxKind.NumericLiteralToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(characterOffset)); + } + } + if (file == null) throw new ArgumentNullException(nameof(file)); + if (file.Kind != SyntaxKind.StringLiteralToken) throw new ArgumentException(nameof(file)); + if (endOfDirectiveToken == null) throw new ArgumentNullException(nameof(endOfDirectiveToken)); + if (endOfDirectiveToken.Kind != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken)); +#endif + + return new LineSpanDirectiveTriviaSyntax(SyntaxKind.LineSpanDirectiveTrivia, hashToken, lineKeyword, start, minusToken, end, characterOffset, file, endOfDirectiveToken, isActive); + } + public static PragmaWarningDirectiveTriviaSyntax PragmaWarningDirectiveTrivia(SyntaxToken hashToken, SyntaxToken pragmaKeyword, SyntaxToken warningKeyword, SyntaxToken disableOrRestoreKeyword, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList errorCodes, SyntaxToken endOfDirectiveToken, bool isActive) { #if DEBUG @@ -44483,6 +44945,8 @@ internal static IEnumerable GetNodeTypes() typeof(DefineDirectiveTriviaSyntax), typeof(UndefDirectiveTriviaSyntax), typeof(LineDirectiveTriviaSyntax), + typeof(LineDirectivePositionSyntax), + typeof(LineSpanDirectiveTriviaSyntax), typeof(PragmaWarningDirectiveTriviaSyntax), typeof(PragmaChecksumDirectiveTriviaSyntax), typeof(ReferenceDirectiveTriviaSyntax), 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 10c143022e4ce..055f9fbf7187b 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 @@ -693,6 +693,12 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a LineDirectiveTriviaSyntax node. public virtual TResult? VisitLineDirectiveTrivia(LineDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a LineDirectivePositionSyntax node. + public virtual TResult? VisitLineDirectivePosition(LineDirectivePositionSyntax node) => this.DefaultVisit(node); + + /// Called when the visitor visits a LineSpanDirectiveTriviaSyntax node. + public virtual TResult? VisitLineSpanDirectiveTrivia(LineSpanDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a PragmaWarningDirectiveTriviaSyntax node. public virtual TResult? VisitPragmaWarningDirectiveTrivia(PragmaWarningDirectiveTriviaSyntax node) => this.DefaultVisit(node); @@ -1392,6 +1398,12 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a LineDirectiveTriviaSyntax node. public virtual void VisitLineDirectiveTrivia(LineDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a LineDirectivePositionSyntax node. + public virtual void VisitLineDirectivePosition(LineDirectivePositionSyntax node) => this.DefaultVisit(node); + + /// Called when the visitor visits a LineSpanDirectiveTriviaSyntax node. + public virtual void VisitLineSpanDirectiveTrivia(LineSpanDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a PragmaWarningDirectiveTriviaSyntax node. public virtual void VisitPragmaWarningDirectiveTrivia(PragmaWarningDirectiveTriviaSyntax node) => this.DefaultVisit(node); @@ -2091,6 +2103,12 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor public override SyntaxNode? VisitLineDirectiveTrivia(LineDirectiveTriviaSyntax node) => node.Update(VisitToken(node.HashToken), VisitToken(node.LineKeyword), VisitToken(node.Line), VisitToken(node.File), VisitToken(node.EndOfDirectiveToken), node.IsActive); + public override SyntaxNode? VisitLineDirectivePosition(LineDirectivePositionSyntax node) + => node.Update(VisitToken(node.OpenParenToken), VisitToken(node.Line), VisitToken(node.CommaToken), VisitToken(node.Character), VisitToken(node.CloseParenToken)); + + public override SyntaxNode? VisitLineSpanDirectiveTrivia(LineSpanDirectiveTriviaSyntax node) + => node.Update(VisitToken(node.HashToken), VisitToken(node.LineKeyword), (LineDirectivePositionSyntax?)Visit(node.Start) ?? throw new ArgumentNullException("start"), VisitToken(node.MinusToken), (LineDirectivePositionSyntax?)Visit(node.End) ?? throw new ArgumentNullException("end"), VisitToken(node.CharacterOffset), VisitToken(node.File), VisitToken(node.EndOfDirectiveToken), node.IsActive); + public override SyntaxNode? VisitPragmaWarningDirectiveTrivia(PragmaWarningDirectiveTriviaSyntax node) => node.Update(VisitToken(node.HashToken), VisitToken(node.PragmaKeyword), VisitToken(node.WarningKeyword), VisitToken(node.DisableOrRestoreKeyword), VisitList(node.ErrorCodes), VisitToken(node.EndOfDirectiveToken), node.IsActive); @@ -6090,6 +6108,48 @@ public static LineDirectiveTriviaSyntax LineDirectiveTrivia(SyntaxToken line, Sy public static LineDirectiveTriviaSyntax LineDirectiveTrivia(SyntaxToken line, bool isActive) => SyntaxFactory.LineDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.LineKeyword), line, default, SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive); + /// Creates a new LineDirectivePositionSyntax instance. + public static LineDirectivePositionSyntax LineDirectivePosition(SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken) + { + if (openParenToken.Kind() != SyntaxKind.OpenParenToken) throw new ArgumentException(nameof(openParenToken)); + if (line.Kind() != SyntaxKind.NumericLiteralToken) throw new ArgumentException(nameof(line)); + if (commaToken.Kind() != SyntaxKind.CommaToken) throw new ArgumentException(nameof(commaToken)); + if (character.Kind() != SyntaxKind.NumericLiteralToken) throw new ArgumentException(nameof(character)); + if (closeParenToken.Kind() != SyntaxKind.CloseParenToken) throw new ArgumentException(nameof(closeParenToken)); + return (LineDirectivePositionSyntax)Syntax.InternalSyntax.SyntaxFactory.LineDirectivePosition((Syntax.InternalSyntax.SyntaxToken)openParenToken.Node!, (Syntax.InternalSyntax.SyntaxToken)line.Node!, (Syntax.InternalSyntax.SyntaxToken)commaToken.Node!, (Syntax.InternalSyntax.SyntaxToken)character.Node!, (Syntax.InternalSyntax.SyntaxToken)closeParenToken.Node!).CreateRed(); + } + + /// Creates a new LineDirectivePositionSyntax instance. + public static LineDirectivePositionSyntax LineDirectivePosition(SyntaxToken line, SyntaxToken character) + => SyntaxFactory.LineDirectivePosition(SyntaxFactory.Token(SyntaxKind.OpenParenToken), line, SyntaxFactory.Token(SyntaxKind.CommaToken), character, SyntaxFactory.Token(SyntaxKind.CloseParenToken)); + + /// Creates a new LineSpanDirectiveTriviaSyntax instance. + public static LineSpanDirectiveTriviaSyntax LineSpanDirectiveTrivia(SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken characterOffset, SyntaxToken file, SyntaxToken endOfDirectiveToken, bool isActive) + { + if (hashToken.Kind() != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken)); + if (lineKeyword.Kind() != SyntaxKind.LineKeyword) throw new ArgumentException(nameof(lineKeyword)); + if (start == null) throw new ArgumentNullException(nameof(start)); + if (minusToken.Kind() != SyntaxKind.MinusToken) throw new ArgumentException(nameof(minusToken)); + if (end == null) throw new ArgumentNullException(nameof(end)); + switch (characterOffset.Kind()) + { + case SyntaxKind.NumericLiteralToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(characterOffset)); + } + if (file.Kind() != SyntaxKind.StringLiteralToken) throw new ArgumentException(nameof(file)); + if (endOfDirectiveToken.Kind() != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken)); + return (LineSpanDirectiveTriviaSyntax)Syntax.InternalSyntax.SyntaxFactory.LineSpanDirectiveTrivia((Syntax.InternalSyntax.SyntaxToken)hashToken.Node!, (Syntax.InternalSyntax.SyntaxToken)lineKeyword.Node!, (Syntax.InternalSyntax.LineDirectivePositionSyntax)start.Green, (Syntax.InternalSyntax.SyntaxToken)minusToken.Node!, (Syntax.InternalSyntax.LineDirectivePositionSyntax)end.Green, (Syntax.InternalSyntax.SyntaxToken?)characterOffset.Node, (Syntax.InternalSyntax.SyntaxToken)file.Node!, (Syntax.InternalSyntax.SyntaxToken)endOfDirectiveToken.Node!, isActive).CreateRed(); + } + + /// Creates a new LineSpanDirectiveTriviaSyntax instance. + public static LineSpanDirectiveTriviaSyntax LineSpanDirectiveTrivia(LineDirectivePositionSyntax start, LineDirectivePositionSyntax end, SyntaxToken characterOffset, SyntaxToken file, bool isActive) + => SyntaxFactory.LineSpanDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.LineKeyword), start, SyntaxFactory.Token(SyntaxKind.MinusToken), end, characterOffset, file, SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive); + + /// Creates a new LineSpanDirectiveTriviaSyntax instance. + public static LineSpanDirectiveTriviaSyntax LineSpanDirectiveTrivia(LineDirectivePositionSyntax start, LineDirectivePositionSyntax end, SyntaxToken file, bool isActive) + => SyntaxFactory.LineSpanDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.LineKeyword), start, SyntaxFactory.Token(SyntaxKind.MinusToken), end, default, file, SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive); + /// Creates a new PragmaWarningDirectiveTriviaSyntax instance. public static PragmaWarningDirectiveTriviaSyntax PragmaWarningDirectiveTrivia(SyntaxToken hashToken, SyntaxToken pragmaKeyword, SyntaxToken warningKeyword, SyntaxToken disableOrRestoreKeyword, SeparatedSyntaxList errorCodes, 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 26972dcd57d15..042ac762357c0 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 @@ -15138,13 +15138,32 @@ public UndefDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken unde public UndefDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.UndefKeyword, this.Name, this.EndOfDirectiveToken, isActive); } + public abstract partial class LineOrSpanDirectiveTriviaSyntax : DirectiveTriviaSyntax + { + internal LineOrSpanDirectiveTriviaSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public abstract SyntaxToken LineKeyword { get; } + public LineOrSpanDirectiveTriviaSyntax WithLineKeyword(SyntaxToken lineKeyword) => WithLineKeywordCore(lineKeyword); + internal abstract LineOrSpanDirectiveTriviaSyntax WithLineKeywordCore(SyntaxToken lineKeyword); + + public abstract SyntaxToken File { get; } + public LineOrSpanDirectiveTriviaSyntax WithFile(SyntaxToken file) => WithFileCore(file); + internal abstract LineOrSpanDirectiveTriviaSyntax WithFileCore(SyntaxToken file); + + public new LineOrSpanDirectiveTriviaSyntax WithHashToken(SyntaxToken hashToken) => (LineOrSpanDirectiveTriviaSyntax)WithHashTokenCore(hashToken); + public new LineOrSpanDirectiveTriviaSyntax WithEndOfDirectiveToken(SyntaxToken endOfDirectiveToken) => (LineOrSpanDirectiveTriviaSyntax)WithEndOfDirectiveTokenCore(endOfDirectiveToken); + } + /// /// This node is associated with the following syntax kinds: /// /// /// /// - public sealed partial class LineDirectiveTriviaSyntax : DirectiveTriviaSyntax + public sealed partial class LineDirectiveTriviaSyntax : LineOrSpanDirectiveTriviaSyntax { internal LineDirectiveTriviaSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) @@ -15154,11 +15173,11 @@ internal LineDirectiveTriviaSyntax(InternalSyntax.CSharpSyntaxNode green, Syntax public override SyntaxToken HashToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectiveTriviaSyntax)this.Green).hashToken, Position, 0); - public SyntaxToken LineKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectiveTriviaSyntax)this.Green).lineKeyword, GetChildPosition(1), GetChildIndex(1)); + public override SyntaxToken LineKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectiveTriviaSyntax)this.Green).lineKeyword, GetChildPosition(1), GetChildIndex(1)); public SyntaxToken Line => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectiveTriviaSyntax)this.Green).line, GetChildPosition(2), GetChildIndex(2)); - public SyntaxToken File + public override SyntaxToken File { get { @@ -15192,14 +15211,153 @@ public LineDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken lineK internal override DirectiveTriviaSyntax WithHashTokenCore(SyntaxToken hashToken) => WithHashToken(hashToken); public new LineDirectiveTriviaSyntax WithHashToken(SyntaxToken hashToken) => Update(hashToken, this.LineKeyword, this.Line, this.File, this.EndOfDirectiveToken, this.IsActive); - public LineDirectiveTriviaSyntax WithLineKeyword(SyntaxToken lineKeyword) => Update(this.HashToken, lineKeyword, this.Line, this.File, this.EndOfDirectiveToken, this.IsActive); + internal override LineOrSpanDirectiveTriviaSyntax WithLineKeywordCore(SyntaxToken lineKeyword) => WithLineKeyword(lineKeyword); + public new LineDirectiveTriviaSyntax WithLineKeyword(SyntaxToken lineKeyword) => Update(this.HashToken, lineKeyword, this.Line, this.File, this.EndOfDirectiveToken, this.IsActive); public LineDirectiveTriviaSyntax WithLine(SyntaxToken line) => Update(this.HashToken, this.LineKeyword, line, this.File, this.EndOfDirectiveToken, this.IsActive); - public LineDirectiveTriviaSyntax WithFile(SyntaxToken file) => Update(this.HashToken, this.LineKeyword, this.Line, file, this.EndOfDirectiveToken, this.IsActive); + internal override LineOrSpanDirectiveTriviaSyntax WithFileCore(SyntaxToken file) => WithFile(file); + public new LineDirectiveTriviaSyntax WithFile(SyntaxToken file) => Update(this.HashToken, this.LineKeyword, this.Line, file, this.EndOfDirectiveToken, this.IsActive); internal override DirectiveTriviaSyntax WithEndOfDirectiveTokenCore(SyntaxToken endOfDirectiveToken) => WithEndOfDirectiveToken(endOfDirectiveToken); public new LineDirectiveTriviaSyntax WithEndOfDirectiveToken(SyntaxToken endOfDirectiveToken) => Update(this.HashToken, this.LineKeyword, this.Line, this.File, endOfDirectiveToken, this.IsActive); public LineDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.LineKeyword, this.Line, this.File, this.EndOfDirectiveToken, isActive); } + /// + /// This node is associated with the following syntax kinds: + /// + /// + /// + /// + public sealed partial class LineDirectivePositionSyntax : CSharpSyntaxNode + { + + internal LineDirectivePositionSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public SyntaxToken OpenParenToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectivePositionSyntax)this.Green).openParenToken, Position, 0); + + public SyntaxToken Line => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectivePositionSyntax)this.Green).line, GetChildPosition(1), GetChildIndex(1)); + + public SyntaxToken CommaToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectivePositionSyntax)this.Green).commaToken, GetChildPosition(2), GetChildIndex(2)); + + public SyntaxToken Character => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectivePositionSyntax)this.Green).character, GetChildPosition(3), GetChildIndex(3)); + + public SyntaxToken CloseParenToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineDirectivePositionSyntax)this.Green).closeParenToken, GetChildPosition(4), GetChildIndex(4)); + + internal override SyntaxNode? GetNodeSlot(int index) => null; + + internal override SyntaxNode? GetCachedSlot(int index) => null; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitLineDirectivePosition(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitLineDirectivePosition(this); + + public LineDirectivePositionSyntax Update(SyntaxToken openParenToken, SyntaxToken line, SyntaxToken commaToken, SyntaxToken character, SyntaxToken closeParenToken) + { + if (openParenToken != this.OpenParenToken || line != this.Line || commaToken != this.CommaToken || character != this.Character || closeParenToken != this.CloseParenToken) + { + var newNode = SyntaxFactory.LineDirectivePosition(openParenToken, line, commaToken, character, closeParenToken); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + public LineDirectivePositionSyntax WithOpenParenToken(SyntaxToken openParenToken) => Update(openParenToken, this.Line, this.CommaToken, this.Character, this.CloseParenToken); + public LineDirectivePositionSyntax WithLine(SyntaxToken line) => Update(this.OpenParenToken, line, this.CommaToken, this.Character, this.CloseParenToken); + public LineDirectivePositionSyntax WithCommaToken(SyntaxToken commaToken) => Update(this.OpenParenToken, this.Line, commaToken, this.Character, this.CloseParenToken); + public LineDirectivePositionSyntax WithCharacter(SyntaxToken character) => Update(this.OpenParenToken, this.Line, this.CommaToken, character, this.CloseParenToken); + public LineDirectivePositionSyntax WithCloseParenToken(SyntaxToken closeParenToken) => Update(this.OpenParenToken, this.Line, this.CommaToken, this.Character, closeParenToken); + } + + /// + /// This node is associated with the following syntax kinds: + /// + /// + /// + /// + public sealed partial class LineSpanDirectiveTriviaSyntax : LineOrSpanDirectiveTriviaSyntax + { + private LineDirectivePositionSyntax? start; + private LineDirectivePositionSyntax? end; + + internal LineSpanDirectiveTriviaSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public override SyntaxToken HashToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).hashToken, Position, 0); + + public override SyntaxToken LineKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).lineKeyword, GetChildPosition(1), GetChildIndex(1)); + + public LineDirectivePositionSyntax Start => GetRed(ref this.start, 2)!; + + public SyntaxToken MinusToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).minusToken, GetChildPosition(3), GetChildIndex(3)); + + public LineDirectivePositionSyntax End => GetRed(ref this.end, 4)!; + + public SyntaxToken CharacterOffset + { + get + { + var slot = ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).characterOffset; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(5), GetChildIndex(5)) : default; + } + } + + public override SyntaxToken File => new SyntaxToken(this, ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).file, GetChildPosition(6), GetChildIndex(6)); + + public override SyntaxToken EndOfDirectiveToken => new SyntaxToken(this, ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).endOfDirectiveToken, GetChildPosition(7), GetChildIndex(7)); + + public override bool IsActive => ((Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax)this.Green).IsActive; + + internal override SyntaxNode? GetNodeSlot(int index) + => index switch + { + 2 => GetRed(ref this.start, 2)!, + 4 => GetRed(ref this.end, 4)!, + _ => null, + }; + + internal override SyntaxNode? GetCachedSlot(int index) + => index switch + { + 2 => this.start, + 4 => this.end, + _ => null, + }; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitLineSpanDirectiveTrivia(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitLineSpanDirectiveTrivia(this); + + public LineSpanDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken lineKeyword, LineDirectivePositionSyntax start, SyntaxToken minusToken, LineDirectivePositionSyntax end, SyntaxToken characterOffset, SyntaxToken file, SyntaxToken endOfDirectiveToken, bool isActive) + { + if (hashToken != this.HashToken || lineKeyword != this.LineKeyword || start != this.Start || minusToken != this.MinusToken || end != this.End || characterOffset != this.CharacterOffset || file != this.File || endOfDirectiveToken != this.EndOfDirectiveToken) + { + var newNode = SyntaxFactory.LineSpanDirectiveTrivia(hashToken, lineKeyword, start, minusToken, end, characterOffset, file, 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 LineSpanDirectiveTriviaSyntax WithHashToken(SyntaxToken hashToken) => Update(hashToken, this.LineKeyword, this.Start, this.MinusToken, this.End, this.CharacterOffset, this.File, this.EndOfDirectiveToken, this.IsActive); + internal override LineOrSpanDirectiveTriviaSyntax WithLineKeywordCore(SyntaxToken lineKeyword) => WithLineKeyword(lineKeyword); + public new LineSpanDirectiveTriviaSyntax WithLineKeyword(SyntaxToken lineKeyword) => Update(this.HashToken, lineKeyword, this.Start, this.MinusToken, this.End, this.CharacterOffset, this.File, this.EndOfDirectiveToken, this.IsActive); + public LineSpanDirectiveTriviaSyntax WithStart(LineDirectivePositionSyntax start) => Update(this.HashToken, this.LineKeyword, start, this.MinusToken, this.End, this.CharacterOffset, this.File, this.EndOfDirectiveToken, this.IsActive); + public LineSpanDirectiveTriviaSyntax WithMinusToken(SyntaxToken minusToken) => Update(this.HashToken, this.LineKeyword, this.Start, minusToken, this.End, this.CharacterOffset, this.File, this.EndOfDirectiveToken, this.IsActive); + public LineSpanDirectiveTriviaSyntax WithEnd(LineDirectivePositionSyntax end) => Update(this.HashToken, this.LineKeyword, this.Start, this.MinusToken, end, this.CharacterOffset, this.File, this.EndOfDirectiveToken, this.IsActive); + public LineSpanDirectiveTriviaSyntax WithCharacterOffset(SyntaxToken characterOffset) => Update(this.HashToken, this.LineKeyword, this.Start, this.MinusToken, this.End, characterOffset, this.File, this.EndOfDirectiveToken, this.IsActive); + internal override LineOrSpanDirectiveTriviaSyntax WithFileCore(SyntaxToken file) => WithFile(file); + public new LineSpanDirectiveTriviaSyntax WithFile(SyntaxToken file) => Update(this.HashToken, this.LineKeyword, this.Start, this.MinusToken, this.End, this.CharacterOffset, file, this.EndOfDirectiveToken, this.IsActive); + internal override DirectiveTriviaSyntax WithEndOfDirectiveTokenCore(SyntaxToken endOfDirectiveToken) => WithEndOfDirectiveToken(endOfDirectiveToken); + public new LineSpanDirectiveTriviaSyntax WithEndOfDirectiveToken(SyntaxToken endOfDirectiveToken) => Update(this.HashToken, this.LineKeyword, this.Start, this.MinusToken, this.End, this.CharacterOffset, this.File, endOfDirectiveToken, this.IsActive); + public LineSpanDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.LineKeyword, this.Start, this.MinusToken, this.End, this.CharacterOffset, this.File, 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 5f507266f694c..066be0b8d5350 100644 --- a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs @@ -10,7 +10,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax { - using System.Reflection; using Microsoft.CodeAnalysis.Syntax.InternalSyntax; internal class DirectiveParser : SyntaxParser @@ -87,7 +86,10 @@ public CSharpSyntaxNode ParseDirective( break; case SyntaxKind.LineKeyword: - result = this.ParseLineDirective(hash, this.EatContextualToken(contextualKind), isActive); + var lineKeyword = this.EatContextualToken(contextualKind); + result = (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) ? + this.ParseLineSpanDirective(hash, lineKeyword, isActive) : + this.ParseLineDirective(hash, lineKeyword, isActive); break; case SyntaxKind.PragmaKeyword: @@ -357,6 +359,9 @@ private DirectiveTriviaSyntax ParseErrorOrWarningDirective(SyntaxToken hash, Syn } } + private const int MaxLineValue = 0xfeefed; + private const int MaxCharacterValue = 0x10000; + private DirectiveTriviaSyntax ParseLineDirective(SyntaxToken hash, SyntaxToken id, bool isActive) { SyntaxToken line; @@ -377,7 +382,7 @@ private DirectiveTriviaSyntax ParseLineDirective(SyntaxToken hash, SyntaxToken i { line = this.AddError(line, ErrorCode.ERR_InvalidLineNumber); } - else if ((int)line.Value > 0xfeefed) + else if ((int)line.Value > MaxLineValue) { line = this.AddError(line, ErrorCode.WRN_TooManyLinesForDebugger); } @@ -397,6 +402,77 @@ private DirectiveTriviaSyntax ParseLineDirective(SyntaxToken hash, SyntaxToken i return SyntaxFactory.LineDirectiveTrivia(hash, id, line, file, end, isActive); } + private LineSpanDirectiveTriviaSyntax ParseLineSpanDirective(SyntaxToken hash, SyntaxToken lineKeyword, bool isActive) + { + Debug.Assert(CurrentToken.Kind == SyntaxKind.OpenParenToken); + + if (isActive) + { + lineKeyword = CheckFeatureAvailability(lineKeyword, MessageID.IDS_FeatureLineSpanDirective); + } + + bool reportError = isActive; + var start = ParseLineDirectivePosition(ref reportError, out int startLine, out int startCharacter); + + var minus = EatToken(SyntaxKind.MinusToken, reportError: reportError); + if (minus.IsMissing) reportError = false; + + var end = ParseLineDirectivePosition(ref reportError, out int endLine, out int endCharacter); + if (reportError && + (endLine < startLine || (endLine == startLine && endCharacter < startCharacter))) + { + end = this.AddError(end, ErrorCode.ERR_LineSpanDirectiveEndLessThanStart); + } + + var characterOffset = (CurrentToken.Kind == SyntaxKind.NumericLiteralToken) ? + ParseLineDirectiveNumericLiteral(ref reportError, minValue: 1, maxValue: MaxCharacterValue, out _) : + null; + + var file = EatToken(SyntaxKind.StringLiteralToken, ErrorCode.ERR_MissingPPFile, reportError: reportError); + if (file.IsMissing) reportError = false; + + var endOfDirective = this.ParseEndOfDirective(ignoreErrors: !reportError); + return SyntaxFactory.LineSpanDirectiveTrivia(hash, lineKeyword, start, minus, end, characterOffset, file, endOfDirective, isActive); + } + + private LineDirectivePositionSyntax ParseLineDirectivePosition(ref bool reportError, out int line, out int character) + { + var openParen = EatToken(SyntaxKind.OpenParenToken, reportError); + if (openParen.IsMissing) reportError = false; + + var lineToken = ParseLineDirectiveNumericLiteral(ref reportError, minValue: 1, maxValue: MaxLineValue, out line); + + var comma = EatToken(SyntaxKind.CommaToken, reportError); + if (comma.IsMissing) reportError = false; + + var characterToken = ParseLineDirectiveNumericLiteral(ref reportError, minValue: 1, maxValue: MaxCharacterValue, out character); + + var closeParen = EatToken(SyntaxKind.CloseParenToken, reportError); + if (closeParen.IsMissing) reportError = false; + + return SyntaxFactory.LineDirectivePosition(openParen, lineToken, comma, characterToken, closeParen); + } + + private SyntaxToken ParseLineDirectiveNumericLiteral(ref bool reportError, int minValue, int maxValue, out int value) + { + var token = this.EatToken(SyntaxKind.NumericLiteralToken, ErrorCode.ERR_LineSpanDirectiveInvalidValue, reportError: reportError); + value = 0; + if (token.IsMissing) + { + reportError = false; + } + else if (token.Kind == SyntaxKind.NumericLiteralToken) + { + value = (int)token.Value; + if (value < minValue || value > maxValue) + { + token = this.AddError(token, ErrorCode.ERR_LineSpanDirectiveInvalidValue); + reportError = false; + } + } + return token; + } + private DirectiveTriviaSyntax ParseReferenceDirective(SyntaxToken hash, SyntaxToken keyword, bool isActive, bool isFollowingToken) { if (isActive) diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs index ea6e89a6cdfb9..42af25f897c20 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs @@ -2836,6 +2836,11 @@ private bool ScanDirectiveToken(ref TokenInfo info) info.Kind = SyntaxKind.CommaToken; break; + case '-': + TextWindow.AdvanceChar(); + info.Kind = SyntaxKind.MinusToken; + 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 2f2973cf92b42..fbb7cc230cb6e 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,5 +1,7 @@ abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +abstract Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken +abstract Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.LineKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp10 = 1000 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax @@ -8,17 +10,66 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.Character.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.CloseParenToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.CommaToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.Line.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.OpenParenToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SyntaxToken line, Microsoft.CodeAnalysis.SyntaxToken commaToken, Microsoft.CodeAnalysis.SyntaxToken character, Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithCharacter(Microsoft.CodeAnalysis.SyntaxToken character) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithCloseParenToken(Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithCommaToken(Microsoft.CodeAnalysis.SyntaxToken commaToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithLine(Microsoft.CodeAnalysis.SyntaxToken line) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithOpenParenToken(Microsoft.CodeAnalysis.SyntaxToken openParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.LineKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.LineKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.WithEndOfDirectiveToken(Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.WithFile(Microsoft.CodeAnalysis.SyntaxToken file) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.WithHashToken(Microsoft.CodeAnalysis.SyntaxToken hashToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.WithLineKeyword(Microsoft.CodeAnalysis.SyntaxToken lineKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.CharacterOffset.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.End.get -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.MinusToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.Start.get -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken lineKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax start, Microsoft.CodeAnalysis.SyntaxToken minusToken, Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax end, Microsoft.CodeAnalysis.SyntaxToken characterOffset, Microsoft.CodeAnalysis.SyntaxToken file, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithCharacterOffset(Microsoft.CodeAnalysis.SyntaxToken characterOffset) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithEnd(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax end) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithEndOfDirectiveToken(Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithFile(Microsoft.CodeAnalysis.SyntaxToken file) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithHashToken(Microsoft.CodeAnalysis.SyntaxToken hashToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithIsActive(bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithLineKeyword(Microsoft.CodeAnalysis.SyntaxToken lineKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithMinusToken(Microsoft.CodeAnalysis.SyntaxToken minusToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.WithStart(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax start) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.ExpressionColon.get -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax expressionColon, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.WithExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax expressionColon) -> Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionColon = 9069 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.LineDirectivePosition = 9070 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.LineSpanDirectiveTrivia = 9071 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.RecordStructDeclaration = 9068 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitLineDirectivePosition(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitLineSpanDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +override Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.EndOfDirectiveToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.HashToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.IsActive.get -> bool +override Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax.LineKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken @@ -32,6 +83,11 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.Update( Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxToken arrowToken, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList attributeLists) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LineDirectivePosition(Microsoft.CodeAnalysis.SyntaxToken line, Microsoft.CodeAnalysis.SyntaxToken character) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LineDirectivePosition(Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.SyntaxToken line, Microsoft.CodeAnalysis.SyntaxToken commaToken, Microsoft.CodeAnalysis.SyntaxToken character, Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LineSpanDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken lineKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax start, Microsoft.CodeAnalysis.SyntaxToken minusToken, Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax end, Microsoft.CodeAnalysis.SyntaxToken characterOffset, Microsoft.CodeAnalysis.SyntaxToken file, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LineSpanDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax start, Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax end, Microsoft.CodeAnalysis.SyntaxToken characterOffset, Microsoft.CodeAnalysis.SyntaxToken file, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LineSpanDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax start, Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax end, Microsoft.CodeAnalysis.SyntaxToken file, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax.WithReturnType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedLambdaExpression(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedLambdaExpression(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedLambdaExpressionSyntax @@ -55,7 +111,11 @@ Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax.GlobalKeyword.get -> M Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken globalKeyword, Microsoft.CodeAnalysis.SyntaxToken usingKeyword, Microsoft.CodeAnalysis.SyntaxToken staticKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.NameEqualsSyntax alias, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax.WithGlobalKeyword(Microsoft.CodeAnalysis.SyntaxToken globalKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLineDirectivePosition(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLineSpanDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLineDirectivePosition(Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitLineSpanDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.LineSpanDirectiveTriviaSyntax node) -> TResult *REMOVED*Microsoft.CodeAnalysis.CSharp.SyntaxKind.DataKeyword = 8441 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.GetLineMappings(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.OperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs index a254beef31ac5..8fed47ddfd464 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs @@ -21,27 +21,34 @@ public CSharpLineDirectiveMap(SyntaxTree syntaxTree) // Add all active #line directives under trivia into the list, in source code order. protected override bool ShouldAddDirective(DirectiveTriviaSyntax directive) { - return directive.IsActive && directive.Kind() == SyntaxKind.LineDirectiveTrivia; + return directive.IsActive && (directive.Kind() is SyntaxKind.LineDirectiveTrivia or SyntaxKind.LineSpanDirectiveTrivia); } // Given a directive and the previous entry, create a new entry. protected override LineMappingEntry GetEntry(DirectiveTriviaSyntax directiveNode, SourceText sourceText, LineMappingEntry previous) { Debug.Assert(ShouldAddDirective(directiveNode)); - var directive = (LineDirectiveTriviaSyntax)directiveNode; // Get line number of NEXT line, hence the +1. - var directiveLineNumber = sourceText.Lines.IndexOf(directive.SpanStart) + 1; + var directiveLineNumber = sourceText.Lines.IndexOf(directiveNode.SpanStart) + 1; + + if (directiveNode is LineSpanDirectiveTriviaSyntax spanDirective) + { + return GetLineSpanDirectiveEntry(spanDirective, directiveLineNumber); + } + + var directive = (LineDirectiveTriviaSyntax)directiveNode; // The default for the current entry does the same thing as the previous entry, except // resetting hidden. var unmappedLine = directiveLineNumber; - var mappedLine = previous.MappedLine + directiveLineNumber - previous.UnmappedLine; - var mappedPathOpt = previous.MappedPathOpt; + var mappedLine = (previous.State == PositionState.RemappedSpan) ? unmappedLine : previous.MappedLine + directiveLineNumber - previous.UnmappedLine; + var mappedPathOpt = (previous.State == PositionState.RemappedSpan) ? null : previous.MappedPathOpt; PositionState state = PositionState.Unmapped; // Modify the current entry based on the directive. SyntaxToken lineToken = directive.Line; + if (!lineToken.IsMissing) { switch (lineToken.Kind()) @@ -86,6 +93,78 @@ protected override LineMappingEntry GetEntry(DirectiveTriviaSyntax directiveNode state); } + private static LineMappingEntry GetLineSpanDirectiveEntry(LineSpanDirectiveTriviaSyntax spanDirective, int unmappedLine) + { + if (!spanDirective.HasErrors && + tryGetPosition(spanDirective.Start, isEnd: false, out LinePosition mappedStart) && + tryGetPosition(spanDirective.End, isEnd: true, out LinePosition mappedEnd) && + tryGetOptionalCharacterOffset(spanDirective.CharacterOffset, out int? characterOffset) && + tryGetStringLiteralValue(spanDirective.File, out string? mappedPathOpt)) + { + return new LineMappingEntry(unmappedLine, new LinePositionSpan(mappedStart, mappedEnd), characterOffset, mappedPathOpt); + } + return new LineMappingEntry(unmappedLine, unmappedLine, mappedPathOpt: null, PositionState.Unmapped); + + static bool tryGetOneBasedNumericLiteralValue(in SyntaxToken token, out int value) + { + if (!token.IsMissing && + token.Kind() == SyntaxKind.NumericLiteralToken && + token.Value is int tokenValue) + { + // convert one-based line number into zero-based line number + value = tokenValue - 1; + return true; + } + value = 0; + return false; + } + + static bool tryGetStringLiteralValue(in SyntaxToken token, out string? value) + { + if (token.Kind() == SyntaxKind.StringLiteralToken) + { + value = (string?)token.Value; + return true; + } + value = null; + return false; + } + + // returns false on error + static bool tryGetOptionalCharacterOffset(in SyntaxToken token, out int? value) + { + if (!token.IsMissing) + { + if (token.Kind() == SyntaxKind.None) + { + value = null; + return true; + } + int val = 0; + if (tryGetOneBasedNumericLiteralValue(token, out val)) + { + value = val; + return true; + } + } + value = null; + return false; + } + + // returns false on error + static bool tryGetPosition(LineDirectivePositionSyntax syntax, bool isEnd, out LinePosition position) + { + if (tryGetOneBasedNumericLiteralValue(syntax.Line, out int line) && + tryGetOneBasedNumericLiteralValue(syntax.Character, out int character)) + { + position = new LinePosition(line, isEnd ? character + 1 : character); + return true; + } + position = default; + return false; + } + } + protected override LineMappingEntry InitializeFirstEntry() { // The first entry of the map is always 0,0,null,Unmapped -- the default mapping. @@ -125,6 +204,7 @@ public override LineVisibility GetLineVisibility(SourceText sourceText, int posi } case PositionState.Remapped: + case PositionState.RemappedSpan: return LineVisibility.Visible; case PositionState.Hidden: diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 19bfc6ccf01aa..b8cabf7960bcb 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -4735,12 +4735,16 @@ - + + + + + - + @@ -4748,7 +4752,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 5c1e743b646ce..24e70583c7166 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -859,5 +859,7 @@ public enum SyntaxKind : ushort RecordStructDeclaration = 9068, ExpressionColon = 9069, + LineDirectivePosition = 9070, + LineSpanDirectiveTrivia = 9071, } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 00c44edd37bc8..9b51d4afad7d7 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -247,6 +247,7 @@ public static bool IsPreprocessorDirective(SyntaxKind kind) case SyntaxKind.ErrorDirectiveTrivia: case SyntaxKind.WarningDirectiveTrivia: case SyntaxKind.LineDirectiveTrivia: + case SyntaxKind.LineSpanDirectiveTrivia: case SyntaxKind.PragmaWarningDirectiveTrivia: case SyntaxKind.PragmaChecksumDirectiveTrivia: case SyntaxKind.ReferenceDirectiveTrivia: diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 681d371b2108c..0570a91630e99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -677,6 +677,16 @@ Metoda {0} s blokem iterátoru musí být asynchronní, aby vrátila {1}. + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Žádná přetížená metoda {0} neodpovídá ukazateli na funkci {1}. @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ac8302d5b02e1..5efe6c0756cda 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -677,6 +677,16 @@ Die Methode "{0}" mit einem Iteratorblock muss "async" lauten, um "{1}" zurückzugeben. + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Keine Überladung für "{0}" stimmt mit dem Funktionszeiger "{1}" überein. @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 28449569693d6..8fcc19dddb568 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -677,6 +677,16 @@ El método "{0}" con un bloqueo de iterador debe ser "asincrónico" para devolver "{1}" + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Ninguna sobrecarga correspondiente a "{0}" coincide con el puntero de función "{1}". @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 5ae4c587debd5..110006141209d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -677,6 +677,16 @@ La méthode '{0}' avec un bloc itérateur doit être 'async' pour retourner '{1}' + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Aucune surcharge pour '{0}' ne correspond au pointeur de fonction '{1}' @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 687c587c69e33..2d8aeadcb0b27 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -677,6 +677,16 @@ Il metodo '{0}' con un blocco iteratore deve essere 'async' per restituire '{1}' + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Nessun overload per '{0}' corrisponde al puntatore a funzione '{1}' @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 584fab8afb86f..3c374bd1f9a63 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -677,6 +677,16 @@ 反復子ブロックを伴うメソッド '{0}' が '{1}' を返すには 'async' でなければなりません + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' 関数ポインター '{1}' に一致する '{0}' のオーバーロードはありません @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index aad215b024ad0..d9f1755292662 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -677,6 +677,16 @@ '{1}'을(를) 반환하려면 반복기 블록이 있는 '{0}' 메서드가 '비동기'여야 합니다. + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' 함수 포인터 '{1}'과(와) 일치하는 '{0}'에 대한 오버로드가 없습니다. @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 16e776b6e992f..e50dc5ffa7d45 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -677,6 +677,16 @@ Metoda „{0}” z blokiem iteratora musi być oznaczona jako „async”, aby zwrócić „{1}” + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Żadne z przeciążeń dla elementu „{0}” nie pasuje do wskaźnika funkcji „{1}” @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 8fde0b712cdbf..7733143f14c35 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -677,6 +677,16 @@ O método '{0}' com um bloco do iterador deve ser 'async' para retornar '{1}' + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Nenhuma sobrecarga de '{0}' corresponde ao ponteiro de função '{1}' @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 95c292a3ec8fa..1faec76c91625 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -677,6 +677,16 @@ Чтобы возвращать "{1}", метод "{0}" с блоком итератора должен быть асинхронным ("async"). + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' Нет перегруженного метода для "{0}", который соответствует указателю на функцию "{1}". @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index e86a57c511822..df71595d36eea 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -677,6 +677,16 @@ Yineleyici bloku olan '{0}' yönteminin '{1}' döndürmek için 'zaman uyumsuz' olması gerekir + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' '{0}' için aşırı yüklemelerin hiçbiri '{1}' işlev işaretçisiyle eşleşmiyor @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 150bb0eb61286..bc57dfcd8bcba 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -677,6 +677,16 @@ 具有迭代器块的方法“{0}”必须是“异步的”,这样才能返回“{1}” + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' “{0}”没有与函数指针“{1}”匹配的重载 @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 4ee731bb84b8a..98acd471b6610 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -677,6 +677,16 @@ 具有迭代區塊的方法 '{0}' 必須為「非同步」才能傳回 '{1}' + + The #line directive end position must be greater than or equal to the start position + The #line directive end position must be greater than or equal to the start position + + + + The #line directive value is missing or out of range + The #line directive value is missing or out of range + + No overload for '{0}' matches function pointer '{1}' '{0}' 沒有任何多載符合函式指標 '{1}' @@ -1137,6 +1147,11 @@ lambda return type + + line span directive + line span directive + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/LineSpanDirectiveTests.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/LineSpanDirectiveTests.cs new file mode 100644 index 0000000000000..956e37f1f6688 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/LineSpanDirectiveTests.cs @@ -0,0 +1,593 @@ +// 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; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class LineSpanDirectiveTests : CSharpTestBase + { + [Fact] + public void LineSpanDirective_SingleLine() + { + string sourceA = +@" A1(); A2(); A3(); //123 +//4567890 +".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"class Program +{ + static void Main() + { +#line (1, 16) - (1, 26) 15 ""a.cs"" + B1(); A2(); A3(); B4(); + B5(); + } +} +".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "b.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(0,0)-(3,7) -> : (0,0)-(3,7)", + "(5,0)-(9,0),14 -> a.cs: (0,15)-(0,26)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"B1();", @"[|A2(); A3();|]"), + (@"A2();", @"[|A2(); A3();|]"), + (@"A3();", @"[|A3();|]"), + (@"B4();", @"[|//123|]"), + (@"B5();", @"[|0|]"), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + [Fact] + public void LineSpanDirective_MultiLine() + { + string sourceA = +@" A1(); A2(); A3(); //123 +//4567890 +//ABCDEF +".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"class Program +{ + static void Main() + { +#line (1, 16) - (5, 26) 15 ""a.cs"" + B1(); A2(); A3(); B4(); + B5(); + } +} +".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "b.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(0,0)-(3,7) -> : (0,0)-(3,7)", + "(5,0)-(9,0),14 -> a.cs: (0,15)-(4,26)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"B1();", @"[|A2(); A3(); //123 +//4567890 +//ABCDEF +|]".NormalizeLineEndings()), + (@"A2();", @"[|A2(); A3(); //123 +//4567890 +//ABCDEF +|]".NormalizeLineEndings()), + (@"A3();", @"[|A3();|]"), + (@"B4();", @"[|//123|]"), + (@"B5();", @"[|0|]"), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + [Fact] + public void InvalidSpans() + { + string source = +@"class Program +{ + static void Main() + { +#line (10, 20) - (10, 20) ""A"" + F(); +#line (10, 20) - (10, 19) ""B"" + F(); +#line (10, 20) - (9, 20) ""C"" + F(); +#line (10, 20) - (11, 19) ""D"" + F(); + } + static void F() { } +}".NormalizeLineEndings(); + + var tree = SyntaxFactory.ParseSyntaxTree(source); + var comp = CreateCompilation(tree); + comp.VerifyDiagnostics( + // (9,18): error CS8939: The #line directive end position must be greater than or equal to the start position + // #line (10, 20) - (9, 20) "C" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveEndLessThanStart, "(9, 20)").WithLocation(9, 18), + // A(11,18): error CS8939: The #line directive end position must be greater than or equal to the start position + // #line (10, 20) - (10, 19) "B" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveEndLessThanStart, "(10, 19)").WithLocation(11, 18)); + + var actualLineMappings = GetLineMappings(tree); + var expectedLineMappings = new[] + { + "(0,0)-(3,7) -> : (0,0)-(3,7)", + "(5,0)-(5,14) -> A: (9,19)-(9,20)", + "(7,0)-(7,14) -> : (7,0)-(7,14)", + "(9,0)-(9,14) -> : (9,0)-(9,14)", + "(11,0)-(14,1) -> D: (9,19)-(10,19)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + } + + // 1. First and subsequent spans + [WorkItem(4747, "https://github.com/dotnet/csharplang/issues/4747")] + [Fact] + public void LineSpanDirective_Example1() + { + string sourceA = +@" A();B( +);C(); + D(); +".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"class Program +{ + static void Main() { +#line (1,10)-(1,15) 3 ""a"" // 3 + A();B( // 4 +);C(); // 5 + D(); // 6 + } + static void A() { } + static void B() { } + static void C() { } + static void D() { } +}".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "b.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(0,0)-(2,23) -> : (0,0)-(2,23)", + "(4,0)-(12,1),2 -> a: (0,9)-(0,15)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"A();", @"[|A();B(|]"), + (@"B( // 4...", @"[|B( +);|]".NormalizeLineEndings()), + (@"C();", @"[|C();|]"), + (@"D();", @"[|D();|]"), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + // 2. Character offset + [WorkItem(4747, "https://github.com/dotnet/csharplang/issues/4747")] + [Fact] + public void LineSpanDirective_Example2() + { + string sourceA = +@"@page ""/"" +@F(() => 1+1, + () => 2+2 +)".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"#line hidden +class Page +{ +void Render() +{ +#line (2,2)-(4,1) 16 ""page.razor"" // spanof('F(...)') + _builder.Add(F(() => 1+1, // 5 + () => 2+2 // 6 +)); // 7 +#line hidden +} +}".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "page.razor.g.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(1,0)-(4,3) -> : (0,0)-(0,0)", + "(6,0)-(8,40),15 -> page.razor: (1,1)-(3,1)", + "(10,0)-(11,1) -> : (0,0)-(0,0)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var textB = SourceText.From(sourceB); + var actualVisibility = textB.Lines.Select(line => treeB.GetLineVisibility(line.Start)).ToImmutableArray(); + var expectedVisibility = new[] + { + LineVisibility.BeforeFirstLineDirective, + LineVisibility.Hidden, + LineVisibility.Hidden, + LineVisibility.Hidden, + LineVisibility.Hidden, + LineVisibility.Hidden, + LineVisibility.Visible, + LineVisibility.Visible, + LineVisibility.Visible, + LineVisibility.Visible, + LineVisibility.Hidden, + LineVisibility.Hidden, + }; + AssertEx.Equal(expectedVisibility, actualVisibility); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"_builder.Add(F(() => 1+1, // 5...", @"[|F(() => 1+1, + () => 2+2 +)|]".NormalizeLineEndings()), + (@"1+1", @"[|1+1|]"), + (@"2+2", @"[|2+2|]"), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + // 3. Razor: Single-line span + [WorkItem(4747, "https://github.com/dotnet/csharplang/issues/4747")] + [Fact] + public void LineSpanDirective_Example3() + { + string sourceA = +@"@page ""/"" +Time: @DateTime.Now +".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"#line hidden +class Page +{ +void Render() +{ + _builder.Add(""Time:""); +#line (2,8)-(2,19) 15 ""page.razor"" // spanof('DateTime.Now') + _builder.Add(DateTime.Now); +#line hidden +} +}".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "page.razor.g.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(1,0)-(5,26) -> : (0,0)-(0,0)", + "(7,0)-(7,31),14 -> page.razor: (1,7)-(1,19)", + "(9,0)-(10,1) -> : (0,0)-(0,0)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"_builder.Add(""Time:"");", @"[||]"), + (@"_builder.Add(DateTime.Now);", @"[|DateTime.Now|]"), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + // 4. Razor: Multi-line span + [WorkItem(4747, "https://github.com/dotnet/csharplang/issues/4747")] + [Fact] + public void LineSpanDirective_Example4() + { + string sourceA = +@"@page ""/"" +@JsonToHtml(@"" +{ + """"key1"""": """"value1"""", + """"key2"""": """"value2"""" +}"")".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"#line hidden +class Page +{ +void Render() +{ +#line (2,2)-(6,3) 16 ""page.razor"" // spanof('JsonToHtml(...)') + _builder.Add(JsonToHtml(@"" +{ + """"key1"""": """"value1"""", + """"key2"""": """"value2"""" +}"")); +#line hidden +} +}".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "page.razor.g.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(1,0)-(4,3) -> : (0,0)-(0,0)", + "(6,0)-(10,7),15 -> page.razor: (1,1)-(5,3)", + "(12,0)-(13,1) -> : (0,0)-(0,0)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"_builder.Add(JsonToHtml(@""...", @"[|JsonToHtml(@"" +{ + """"key1"""": """"value1"""", + """"key2"""": """"value2"""" +}"")|]".NormalizeLineEndings()), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + // 5i. Razor: block constructs + [WorkItem(4747, "https://github.com/dotnet/csharplang/issues/4747")] + [Fact] + public void LineSpanDirective_Example5i() + { + string sourceA = +@"@Html.Helper(() => +{ +

Hello World

+ @DateTime.Now +})".NormalizeLineEndings(); + var textA = SourceText.From(sourceA); + + string sourceB = +@"using System; +class Page +{ + Builder _builder; + void Execute() + { +#line (1, 2) - (5, 2) 22 ""a.razor"" // spanof('HtmlHelper(() => { ... })') + _builder.Add(Html.Helper(() => +#line 2 ""a.razor"" // lineof('{') + { +#line (4, 6) - (4, 17) 26 ""a.razor"" // spanof('DateTime.Now') + _builder.Add(DateTime.Now); +#line 5 ""a.razor"" // lineof('})') + }) +#line hidden + ); + } +}".NormalizeLineEndings(); + + var treeB = SyntaxFactory.ParseSyntaxTree(sourceB, path: "a.razor.g.cs"); + treeB.GetDiagnostics().Verify(); + + var actualLineMappings = GetLineMappings(treeB); + var expectedLineMappings = new[] + { + "(0,0)-(5,7) -> : (0,0)-(5,7)", + "(7,0)-(7,40),21 -> a.razor: (0,1)-(4,2)", + "(9,0)-(9,11) -> a.razor: (1,0)-(1,11)", + "(11,0)-(11,41),25 -> a.razor: (3,5)-(3,17)", + "(13,0)-(13,12) -> a.razor: (4,0)-(4,12)", + "(15,0)-(17,1) -> : (0,0)-(0,0)", + }; + AssertEx.Equal(expectedLineMappings, actualLineMappings); + + var statements = GetStatementsAndExpressionBodies(treeB); + var actualTextSpans = statements.SelectAsArray(s => GetTextMapping(textA, treeB, s)); + var expectedTextSpans = new[] + { + (@"_builder.Add(Html.Helper(() =>...", @"[|Html.Helper(() => +{ +

Hello World

+ @DateTime.Now +})|]".NormalizeLineEndings()), + (@"_builder.Add(DateTime.Now);", @"[|DateTime.Now|]"), + }; + AssertEx.Equal(expectedTextSpans, actualTextSpans); + } + + private static ImmutableArray GetStatementsAndExpressionBodies(SyntaxTree tree) + { + var builder = ArrayBuilder.GetInstance(); + foreach (var syntax in tree.GetRoot().DescendantNodesAndSelf()) + { + switch (syntax) + { + case ExpressionStatementSyntax: + builder.Add(syntax); + break; + case ParenthesizedLambdaExpressionSyntax lambda: + builder.AddIfNotNull(lambda.ExpressionBody); + break; + case SimpleLambdaExpressionSyntax lambda: + builder.AddIfNotNull(lambda.ExpressionBody); + break; + } + } + return builder.ToImmutableAndFree(); + } + + private static ImmutableArray GetLineMappings(SyntaxTree tree) + { + return tree.GetLineMappings().Select(mapping => mapping.ToString()!).ToImmutableArray(); + } + + private static (string, string) GetTextMapping(SourceText mappedText, SyntaxTree unmappedText, SyntaxNode syntax) + { + return (getDescription(syntax), getMapping(mappedText, unmappedText, syntax)); + + static string getDescription(SyntaxNode syntax) + { + var description = syntax.ToString(); + int index = description.IndexOfAny(new[] { '\r', '\n' }); + return index < 0 ? + description : + description.Substring(0, index) + "..."; + } + + static string getMapping(SourceText mappedText, SyntaxTree unmappedText, SyntaxNode syntax) + { + var mappedLineAndPositionSpan = unmappedText.GetMappedLineSpanAndVisibility(syntax.Span, out _); + var span = getTextSpan(mappedText.Lines, mappedLineAndPositionSpan.Span); + return $"[|{mappedText.GetSubText(span)}|]"; + } + + static TextSpan getTextSpan(TextLineCollection lines, LinePositionSpan span) + { + return TextSpan.FromBounds(getTextPosition(lines, span.Start), getTextPosition(lines, span.End)); + } + + static int getTextPosition(TextLineCollection lines, LinePosition position) + { + if (position.Line < lines.Count) + { + var line = lines[position.Line]; + return Math.Min(line.Start + position.Character, line.End); + } + return (lines.Count == 0) ? 0 : lines[^1].End; + } + } + + [Fact] + public void Diagnostics() + { + var source = +@"class Program +{ + static void Main() + { +#line (3, 3) - (6, 6) 8 ""a.txt"" + A(); +#line default + B(); +#line (1, 1) - (1, 100) ""b.txt"" + C(); + } +}".NormalizeLineEndings(); + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // b.txt(1,9): error CS0103: The name 'C' does not exist in the current context + // C(); + Diagnostic(ErrorCode.ERR_NameNotInContext, "C").WithArguments("C").WithLocation(1, 9), + // a.txt(3,4): error CS0103: The name 'A' does not exist in the current context + // A(); + Diagnostic(ErrorCode.ERR_NameNotInContext, "A").WithArguments("A").WithLocation(3, 4), + // (8,9): error CS0103: The name 'B' does not exist in the current context + // B(); + Diagnostic(ErrorCode.ERR_NameNotInContext, "B").WithArguments("B").WithLocation(8, 9)); + } + + [Fact] + public void SequencePoints() + { + var source = +@"class Program +{ + static void Main() + { +#line (3, 3) - (6, 6) 8 ""a.txt"" + A(); +#line default + B(); +#line (1, 1) - (1, 100) ""b.txt"" + C(); + } + static void A() { } + static void B() { } + static void C() { } +}".NormalizeLineEndings(); + var verifier = CompileAndVerify(source, options: TestOptions.DebugDll); + verifier.VerifyIL("Program.Main", sequencePoints: "Program.Main", expectedIL: +@"{ + // Code size 20 (0x14) + .maxstack 0 + -IL_0000: nop + -IL_0001: call ""void Program.A()"" + IL_0006: nop + -IL_0007: call ""void Program.B()"" + IL_000c: nop + -IL_000d: call ""void Program.C()"" + IL_0012: nop + -IL_0013: ret +}"); + verifier.VerifyPdb("Program.Main", expectedPdb: +@" + + + + + + + + + + + + + + + + + + + + + +"); + } + } +} 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 7518250d755d0..b480da073976c 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 @@ -688,6 +688,12 @@ private static Syntax.InternalSyntax.UndefDirectiveTriviaSyntax GenerateUndefDir private static Syntax.InternalSyntax.LineDirectiveTriviaSyntax GenerateLineDirectiveTrivia() => InternalSyntaxFactory.LineDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.LineKeyword), InternalSyntaxFactory.Literal(null, "1", 1, null), null, InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static Syntax.InternalSyntax.LineDirectivePositionSyntax GenerateLineDirectivePosition() + => InternalSyntaxFactory.LineDirectivePosition(InternalSyntaxFactory.Token(SyntaxKind.OpenParenToken), InternalSyntaxFactory.Literal(null, "1", 1, null), InternalSyntaxFactory.Token(SyntaxKind.CommaToken), InternalSyntaxFactory.Literal(null, "1", 1, null), InternalSyntaxFactory.Token(SyntaxKind.CloseParenToken)); + + private static Syntax.InternalSyntax.LineSpanDirectiveTriviaSyntax GenerateLineSpanDirectiveTrivia() + => InternalSyntaxFactory.LineSpanDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.LineKeyword), GenerateLineDirectivePosition(), InternalSyntaxFactory.Token(SyntaxKind.MinusToken), GenerateLineDirectivePosition(), null, InternalSyntaxFactory.Literal(null, "string", "string", null), InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static Syntax.InternalSyntax.PragmaWarningDirectiveTriviaSyntax GeneratePragmaWarningDirectiveTrivia() => InternalSyntaxFactory.PragmaWarningDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.PragmaKeyword), InternalSyntaxFactory.Token(SyntaxKind.WarningKeyword), InternalSyntaxFactory.Token(SyntaxKind.DisableKeyword), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); @@ -3592,6 +3598,38 @@ public void TestLineDirectiveTriviaFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestLineDirectivePositionFactoryAndProperties() + { + var node = GenerateLineDirectivePosition(); + + Assert.Equal(SyntaxKind.OpenParenToken, node.OpenParenToken.Kind); + Assert.Equal(SyntaxKind.NumericLiteralToken, node.Line.Kind); + Assert.Equal(SyntaxKind.CommaToken, node.CommaToken.Kind); + Assert.Equal(SyntaxKind.NumericLiteralToken, node.Character.Kind); + Assert.Equal(SyntaxKind.CloseParenToken, node.CloseParenToken.Kind); + + AttachAndCheckDiagnostics(node); + } + + [Fact] + public void TestLineSpanDirectiveTriviaFactoryAndProperties() + { + var node = GenerateLineSpanDirectiveTrivia(); + + Assert.Equal(SyntaxKind.HashToken, node.HashToken.Kind); + Assert.Equal(SyntaxKind.LineKeyword, node.LineKeyword.Kind); + Assert.NotNull(node.Start); + Assert.Equal(SyntaxKind.MinusToken, node.MinusToken.Kind); + Assert.NotNull(node.End); + Assert.Null(node.CharacterOffset); + Assert.Equal(SyntaxKind.StringLiteralToken, node.File.Kind); + Assert.Equal(SyntaxKind.EndOfDirectiveToken, node.EndOfDirectiveToken.Kind); + Assert.Equal(new bool(), node.IsActive); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestPragmaWarningDirectiveTriviaFactoryAndProperties() { @@ -9559,6 +9597,58 @@ public void TestLineDirectiveTriviaIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestLineDirectivePositionTokenDeleteRewriter() + { + var oldNode = GenerateLineDirectivePosition(); + 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 TestLineDirectivePositionIdentityRewriter() + { + var oldNode = GenerateLineDirectivePosition(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + + [Fact] + public void TestLineSpanDirectiveTriviaTokenDeleteRewriter() + { + var oldNode = GenerateLineSpanDirectiveTrivia(); + 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 TestLineSpanDirectiveTriviaIdentityRewriter() + { + var oldNode = GenerateLineSpanDirectiveTrivia(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestPragmaWarningDirectiveTriviaTokenDeleteRewriter() { @@ -10398,6 +10488,12 @@ private static UndefDirectiveTriviaSyntax GenerateUndefDirectiveTrivia() private static LineDirectiveTriviaSyntax GenerateLineDirectiveTrivia() => SyntaxFactory.LineDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.LineKeyword), SyntaxFactory.Literal("1", 1), default(SyntaxToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static LineDirectivePositionSyntax GenerateLineDirectivePosition() + => SyntaxFactory.LineDirectivePosition(SyntaxFactory.Token(SyntaxKind.OpenParenToken), SyntaxFactory.Literal("1", 1), SyntaxFactory.Token(SyntaxKind.CommaToken), SyntaxFactory.Literal("1", 1), SyntaxFactory.Token(SyntaxKind.CloseParenToken)); + + private static LineSpanDirectiveTriviaSyntax GenerateLineSpanDirectiveTrivia() + => SyntaxFactory.LineSpanDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.LineKeyword), GenerateLineDirectivePosition(), SyntaxFactory.Token(SyntaxKind.MinusToken), GenerateLineDirectivePosition(), default(SyntaxToken), SyntaxFactory.Literal("string", "string"), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static PragmaWarningDirectiveTriviaSyntax GeneratePragmaWarningDirectiveTrivia() => SyntaxFactory.PragmaWarningDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.PragmaKeyword), SyntaxFactory.Token(SyntaxKind.WarningKeyword), SyntaxFactory.Token(SyntaxKind.DisableKeyword), new SeparatedSyntaxList(), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); @@ -13302,6 +13398,38 @@ public void TestLineDirectiveTriviaFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestLineDirectivePositionFactoryAndProperties() + { + var node = GenerateLineDirectivePosition(); + + Assert.Equal(SyntaxKind.OpenParenToken, node.OpenParenToken.Kind()); + Assert.Equal(SyntaxKind.NumericLiteralToken, node.Line.Kind()); + Assert.Equal(SyntaxKind.CommaToken, node.CommaToken.Kind()); + Assert.Equal(SyntaxKind.NumericLiteralToken, node.Character.Kind()); + Assert.Equal(SyntaxKind.CloseParenToken, node.CloseParenToken.Kind()); + var newNode = node.WithOpenParenToken(node.OpenParenToken).WithLine(node.Line).WithCommaToken(node.CommaToken).WithCharacter(node.Character).WithCloseParenToken(node.CloseParenToken); + Assert.Equal(node, newNode); + } + + [Fact] + public void TestLineSpanDirectiveTriviaFactoryAndProperties() + { + var node = GenerateLineSpanDirectiveTrivia(); + + Assert.Equal(SyntaxKind.HashToken, node.HashToken.Kind()); + Assert.Equal(SyntaxKind.LineKeyword, node.LineKeyword.Kind()); + Assert.NotNull(node.Start); + Assert.Equal(SyntaxKind.MinusToken, node.MinusToken.Kind()); + Assert.NotNull(node.End); + Assert.Equal(SyntaxKind.None, node.CharacterOffset.Kind()); + Assert.Equal(SyntaxKind.StringLiteralToken, node.File.Kind()); + Assert.Equal(SyntaxKind.EndOfDirectiveToken, node.EndOfDirectiveToken.Kind()); + Assert.Equal(new bool(), node.IsActive); + var newNode = node.WithHashToken(node.HashToken).WithLineKeyword(node.LineKeyword).WithStart(node.Start).WithMinusToken(node.MinusToken).WithEnd(node.End).WithCharacterOffset(node.CharacterOffset).WithFile(node.File).WithEndOfDirectiveToken(node.EndOfDirectiveToken).WithIsActive(node.IsActive); + Assert.Equal(node, newNode); + } + [Fact] public void TestPragmaWarningDirectiveTriviaFactoryAndProperties() { @@ -19269,6 +19397,58 @@ public void TestLineDirectiveTriviaIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestLineDirectivePositionTokenDeleteRewriter() + { + var oldNode = GenerateLineDirectivePosition(); + 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 TestLineDirectivePositionIdentityRewriter() + { + var oldNode = GenerateLineDirectivePosition(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + + [Fact] + public void TestLineSpanDirectiveTriviaTokenDeleteRewriter() + { + var oldNode = GenerateLineSpanDirectiveTrivia(); + 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 TestLineSpanDirectiveTriviaIdentityRewriter() + { + var oldNode = GenerateLineSpanDirectiveTrivia(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestPragmaWarningDirectiveTriviaTokenDeleteRewriter() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LineSpanDirectiveParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LineSpanDirectiveParsingTests.cs new file mode 100644 index 0000000000000..6228c010f379d --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LineSpanDirectiveParsingTests.cs @@ -0,0 +1,1833 @@ +// 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.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class LineSpanDirectiveParsingTests : ParsingTests + { + public LineSpanDirectiveParsingTests(ITestOutputHelper output) : base(output) { } + + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) + { + return SyntaxFactory.ParseSyntaxTree(text, options: options); + } + + protected override CSharpSyntaxNode ParseNode(string text, CSharpParseOptions? options) + { + return SyntaxFactory.ParseExpression(text, options: options); + } + + private void UsingLineDirective(string text, CSharpParseOptions? options, params DiagnosticDescription[] expectedErrors) + { + var node = ParseTree(text, options).GetCompilationUnitRoot(); + Validate(text, node, expectedErrors); + UsingNode(node.GetDirectives().Single(d => d.Kind() is SyntaxKind.LineDirectiveTrivia or SyntaxKind.LineSpanDirectiveTrivia)); + } + + [Fact] + public void IsActive() + { + string source = +@"#if IsActive +#line (1, 2) - (3, 4) ""file.cs"" +#endif"; + + UsingLineDirective(source, TestOptions.Regular9); + verify(); + + UsingLineDirective(source, TestOptions.Regular9.WithPreprocessorSymbols("IsActive"), + // (2,2): error CS8773: Feature 'line span directive' is not available in C# 9.0. Please use language version 10.0 or greater. + // #line (1, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "line").WithArguments("line span directive", "10.0").WithLocation(2, 2)); + verify(); + + void verify() + { + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + } + + [Fact] + public void LineDirective_01() + { + string source = @"#line (1, 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, TestOptions.Regular9, + // (1,2): error CS8773: Feature 'line span directive' is not available in C# 9.0. Please use language version 10.0 or greater. + // #line (1, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "line").WithArguments("line span directive", "10.0").WithLocation(1, 2)); + verify(); + + UsingLineDirective(source, TestOptions.Regular10); + verify(); + + void verify() + { + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + } + + [Fact] + public void LineDirective_02() + { + string source = @"#line (1, 2) - (3, 4) 5 ""file.cs"""; + + UsingLineDirective(source, TestOptions.Regular9, + // (1,2): error CS8773: Feature 'line span directive' is not available in C# 9.0. Please use language version 10.0 or greater. + // #line (1, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "line").WithArguments("line span directive", "10.0").WithLocation(1, 2)); + verify(); + + UsingLineDirective(source, TestOptions.Regular10); + verify(); + + void verify() + { + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "5"); + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + } + + [Fact] + public void LineDirective_03() + { + string source = @"#line (1, 2) - (3, 4) """""; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void LineDirective_04() + { + string source = @" # line ( 1 , 2 ) - ( 3 , 4 ) 5 "" """; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "5"); + N(SyntaxKind.StringLiteralToken, "\" \""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void LineDirective_05() + { + string source = @"#line(1,2)-(3,4)""file.cs"""; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void LineDirective_06() + { + string source = @"#line(1,2)-(3,4)5""file.cs"""; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "5"); + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_01() + { + string source = @"#line ("; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line ( + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "").WithLocation(1, 8)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_02() + { + string source = @"#line (1"; + + UsingLineDirective(source, options: null, + // (1,9): error CS1003: Syntax error, ',' expected + // #line (1 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",", "").WithLocation(1, 9)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_03() + { + string source = @"#line (1,"; + + UsingLineDirective(source, options: null, + // (1,10): error CS8938: The #line directive value is missing or out of range + // #line (1, + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "").WithLocation(1, 10)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_04() + { + string source = @"#line (1, 2"; + + UsingLineDirective(source, options: null, + // (1,12): error CS1026: ) expected + // #line (1, 2 + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 12)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_05() + { + string source = @"#line (1, 2)"; + + UsingLineDirective(source, options: null, + // (1,13): error CS1003: Syntax error, '-' expected + // #line (1, 2) + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("-", "").WithLocation(1, 13)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_06() + { + string source = @"#line (1, 2) -"; + + UsingLineDirective(source, options: null, + // (1,15): error CS1003: Syntax error, '(' expected + // #line (1, 2) - + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(", "").WithLocation(1, 15)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_07() + { + string source = @"#line (1, 2) - ("; + + UsingLineDirective(source, options: null, + // (1,17): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - ( + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "").WithLocation(1, 17)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_08() + { + string source = @"#line (1, 2) - (3"; + + UsingLineDirective(source, options: null, + // (1,18): error CS1003: Syntax error, ',' expected + // #line (1, 2) - (3 + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",", "").WithLocation(1, 18)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_09() + { + string source = @"#line (1, 2) - (3,"; + + UsingLineDirective(source, options: null, + // (1,19): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - (3, + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "").WithLocation(1, 19)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_10() + { + string source = @"#line (1, 2) - (3, 4"; + + UsingLineDirective(source, options: null, + // (1,21): error CS1026: ) expected + // #line (1, 2) - (3, 4 + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 21)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_11() + { + string source = @"#line (1, 2) - (3, 4)"; + + UsingLineDirective(source, options: null, + // (1,22): error CS1578: Quoted file name, single-line comment or end-of-line expected + // #line (1, 2) - (3, 4) + Diagnostic(ErrorCode.ERR_MissingPPFile, "").WithLocation(1, 22)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_12() + { + string source = @"#line (1, 2) - (3, 4) 5"; + + UsingLineDirective(source, options: null, + // (1,24): error CS1578: Quoted file name, single-line comment or end-of-line expected + // #line (1, 2) - (3, 4) 5 + Diagnostic(ErrorCode.ERR_MissingPPFile, "").WithLocation(1, 24)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "5"); + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Incomplete_13() + { + ParseIncompleteSyntax(@"#line (1, 2) - (3, 4) 5 ""file.cs"""); + } + + [Fact] + public void Missing_01() + { + string source = @"#line 1, 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS1578: Quoted file name, single-line comment or end-of-line expected + // #line 1, 2) - 3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_MissingPPFile, ",").WithLocation(1, 8)); + + N(SyntaxKind.LineDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_02() + { + string source = @"#line (, 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line (, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, ",").WithLocation(1, 8)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_03() + { + string source = @"#line (1 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,10): error CS1003: Syntax error, ',' expected + // #line (1 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_SyntaxError, "2").WithArguments(",", "").WithLocation(1, 10)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + M(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_04() + { + string source = @"#line (1, ) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,11): error CS8938: The #line directive value is missing or out of range + // #line (1, ) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, ")").WithLocation(1, 11)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_05() + { + string source = @"#line (1, 2 - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,13): error CS1026: ) expected + // #line (1, 2 - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_CloseParenExpected, "-").WithLocation(1, 13)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_06() + { + string source = @"#line (1, 2) (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,14): error CS1003: Syntax error, '-' expected + // #line (1, 2) (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("-", "(").WithLocation(1, 14)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_07() + { + string source = @"#line (1, 2) - 3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,16): error CS1003: Syntax error, '(' expected + // #line (1, 2) - 3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_SyntaxError, "3").WithArguments("(", "").WithLocation(1, 16)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_08() + { + string source = @"#line (1, 2) - (, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,17): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - (, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, ",").WithLocation(1, 17)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_09() + { + string source = @"#line (1, 2) - (3 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,19): error CS1003: Syntax error, ',' expected + // #line (1, 2) - (3 4) "file.cs" + Diagnostic(ErrorCode.ERR_SyntaxError, "4").WithArguments(",", "").WithLocation(1, 19)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + M(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_10() + { + string source = @"#line (1, 2) - (3, ) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,20): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - (3, ) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, ")").WithLocation(1, 20)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void Missing_11() + { + string source = @"#line (1, 2) - (3, 4 ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,22): error CS1026: ) expected + // #line (1, 2) - (3, 4 "file.cs" + Diagnostic(ErrorCode.ERR_CloseParenExpected, @"""file.cs""").WithLocation(1, 22)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_01() + { + string source = @"#line ('1', 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line ('1', 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "'").WithLocation(1, 8)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_02() + { + string source = @"#line (1, ""2"") - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,11): error CS8938: The #line directive value is missing or out of range + // #line (1, "2") - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, @"""2""").WithLocation(1, 11)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"2\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_03() + { + string source = @"#line (1, 2) - (0b11, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,17): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - (0b11, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 17)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "0"); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_04() + { + string source = @"#line (1, 2) - (3, 0x04) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,20): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - (3, 0x04) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 20)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "0"); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_05() + { + string source = @"#line (null, 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line (null, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "null").WithLocation(1, 8)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_06() + { + string source = @"#line (1, true) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,11): error CS8938: The #line directive value is missing or out of range + // #line (1, true) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "true").WithLocation(1, 11)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_07() + { + string source = @"#line (1, 2) - (int, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,17): error CS8938: The #line directive value is missing or out of range + // #line (1, 2) - (int, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "int").WithLocation(1, 17)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_08() + { + string source = @"#line (1u, 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,9): error CS1003: Syntax error, ',' expected + // #line (1u, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_SyntaxError, "u").WithArguments(",", "").WithLocation(1, 9)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_09() + { + string source = @"#line (1, 2f) - (3, 4) "" """; + + UsingLineDirective(source, options: null, + // (1,12): error CS1026: ) expected + // #line (1, 2f) - (3, 4) " " + Diagnostic(ErrorCode.ERR_CloseParenExpected, "f").WithLocation(1, 12)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.MinusToken); + M(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void UnexpectedToken_11() + { + string source = @"#line (1, 2) - (3, 4) file.cs"; + + UsingLineDirective(source, options: null, + // (1,23): error CS1578: Quoted file name, single-line comment or end-of-line expected + // #line (1, 2) - (3, 4) file.cs + Diagnostic(ErrorCode.ERR_MissingPPFile, "file").WithLocation(1, 23)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "3"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "4"); + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifyValue_01() + { + string source = @"#line (-1, 2) - (3, 4) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line (-1, 2) - (3, 4) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "-").WithLocation(1, 8)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CommaToken); + M(SyntaxKind.NumericLiteralToken); + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "2"); + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.StringLiteralToken); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifyValue_02() + { + string source = @"#line (0, 0) - (0, 0) 0 ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line (0, 0) - (0, 0) 0 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 8), + // (1,11): error CS8938: The #line directive value is missing or out of range + // #line (0, 0) - (0, 0) 0 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 11), + // (1,17): error CS8938: The #line directive value is missing or out of range + // #line (0, 0) - (0, 0) 0 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 17), + // (1,20): error CS8938: The #line directive value is missing or out of range + // #line (0, 0) - (0, 0) 0 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 20), + // (1,23): error CS8938: The #line directive value is missing or out of range + // #line (0, 0) - (0, 0) 0 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "0").WithLocation(1, 23)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "0"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "0"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "0"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "0"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "0"); + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifyValue_03() + { + string source = @"#line (16707565, 65536) - (16707565, 65536) 65536 ""file.cs"""; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "16707565"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "65536"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "16707565"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "65536"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "65536"); + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifyValue_04() + { + string source = @"#line (16707566, 65537) - (16707566, 65537) 65537 ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,8): error CS8938: The #line directive value is missing or out of range + // #line (16707566, 65537) - (16707566, 65537) 65537 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "16707566").WithLocation(1, 8), + // (1,18): error CS8938: The #line directive value is missing or out of range + // #line (16707566, 65537) - (16707566, 65537) 65537 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "65537").WithLocation(1, 18), + // (1,28): error CS8938: The #line directive value is missing or out of range + // #line (16707566, 65537) - (16707566, 65537) 65537 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "16707566").WithLocation(1, 28), + // (1,38): error CS8938: The #line directive value is missing or out of range + // #line (16707566, 65537) - (16707566, 65537) 65537 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "65537").WithLocation(1, 38), + // (1,45): error CS8938: The #line directive value is missing or out of range + // #line (16707566, 65537) - (16707566, 65537) 65537 "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveInvalidValue, "65537").WithLocation(1, 45)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "16707566"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "65537"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "16707566"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "65537"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.NumericLiteralToken, "65537"); + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifySpan_01() + { + string source = @"#line (10, 20) - (10, 20) ""file.cs"""; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "10"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "20"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "10"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "20"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifySpan_02() + { + string source = @"#line (10, 20) - (10, 19) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,18): error CS8939: The #line directive end position must be greater than or equal to the start position + // #line (10, 20) - (10, 19) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveEndLessThanStart, "(10, 19)").WithLocation(1, 18)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "10"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "20"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "10"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "19"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifySpan_03() + { + string source = @"#line (10, 20) - (9, 20) ""file.cs"""; + + UsingLineDirective(source, options: null, + // (1,18): error CS8939: The #line directive end position must be greater than or equal to the start position + // #line (10, 20) - (9, 20) "file.cs" + Diagnostic(ErrorCode.ERR_LineSpanDirectiveEndLessThanStart, "(9, 20)").WithLocation(1, 18)); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "10"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "20"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "9"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "20"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + N(SyntaxKind.EndOfDirectiveToken); + } + EOF(); + } + + [Fact] + public void VerifySpan_04() + { + string source = @"#line (10, 20) - (11, 1) ""file.cs"""; + + UsingLineDirective(source, options: null); + + N(SyntaxKind.LineSpanDirectiveTrivia); + { + N(SyntaxKind.HashToken); + N(SyntaxKind.LineKeyword); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "10"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "20"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.MinusToken); + N(SyntaxKind.LineDirectivePosition); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralToken, "11"); + N(SyntaxKind.CommaToken); + N(SyntaxKind.NumericLiteralToken, "1"); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.StringLiteralToken, "\"file.cs\""); + 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 8f430d1182bfc..17f180d628916 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs @@ -93,10 +93,7 @@ internal void UsingStatement(string text, params DiagnosticDescription[] expecte internal void UsingStatement(string text, ParseOptions? options, params DiagnosticDescription[] expectedErrors) { var node = SyntaxFactory.ParseStatement(text, options: options); - // we validate the text roundtrips - Assert.Equal(text, node.ToFullString()); - var actualErrors = node.GetDiagnostics(); - actualErrors.Verify(expectedErrors); + Validate(text, node, expectedErrors); UsingNode(node); } @@ -126,12 +123,17 @@ internal void UsingExpression(string text, ParseOptions? options, params Diagnos } protected void UsingNode(string text, CSharpSyntaxNode node, DiagnosticDescription[] expectedErrors) + { + Validate(text, node, expectedErrors); + UsingNode(node); + } + + protected void Validate(string text, CSharpSyntaxNode node, params DiagnosticDescription[] expectedErrors) { // we validate the text roundtrips Assert.Equal(text, node.ToFullString()); var actualErrors = node.GetDiagnostics(); actualErrors.Verify(expectedErrors); - UsingNode(node); } internal void UsingExpression(string text, params DiagnosticDescription[] expectedErrors) @@ -190,15 +192,17 @@ protected SyntaxNodeOrToken N(SyntaxKind kind, string? value = null) try { Assert.True(_treeEnumerator!.MoveNext()); - Assert.Equal(kind, _treeEnumerator.Current.Kind()); - Assert.False(_treeEnumerator.Current.IsMissing); + var current = _treeEnumerator.Current; + + Assert.Equal(kind, current.Kind()); + Assert.False(current.IsMissing); if (value != null) { - Assert.Equal(_treeEnumerator.Current.ToString(), value); + Assert.Equal(current.ToString(), value); } - return _treeEnumerator.Current; + return current; } catch when (DumpAndCleanup()) { @@ -286,11 +290,13 @@ private void Print(SyntaxNodeOrToken node, bool dump) { case SyntaxKind.IdentifierToken: case SyntaxKind.NumericLiteralToken: + case SyntaxKind.StringLiteralToken: if (node.IsMissing) { goto default; } - _output.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), node.ToString()); + 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()); diff --git a/src/Compilers/Core/CodeAnalysisTest/LineMappingTests.cs b/src/Compilers/Core/CodeAnalysisTest/LineMappingTests.cs new file mode 100644 index 0000000000000..4609e8718c291 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/LineMappingTests.cs @@ -0,0 +1,45 @@ +// 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.Text; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests +{ + public class LineMappingTests + { + [Fact] + public void Equality() + { + var lineMappings = new LineMapping[] + { + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: true)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 2)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(2, 2)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 1), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(1, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("file.cs", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), 0, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 2)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 0), new LinePosition(2, 2)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(0, 1), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + new LineMapping(new LinePositionSpan(new LinePosition(1, 0), new LinePosition(1, 1)), null, new FileLinePositionSpan("", new LinePositionSpan(new LinePosition(0, 0), new LinePosition(1, 1)), hasMappedPath: false)), + }; + var equalityUnits = lineMappings.SelectMany((left, leftIndex) => lineMappings.Select((right, rightIndex) => CreateEqualityUnit(left, leftIndex, right, rightIndex))).ToArray(); + EqualityUtil.RunAll( + (left, right) => left == right, + (left, right) => left != right, + equalityUnits); + + static EqualityUnit CreateEqualityUnit(LineMapping left, int leftIndex, LineMapping right, int rightIndex) + { + var leftUnit = EqualityUnit.Create(left); + return (leftIndex == rightIndex) ? leftUnit.WithEqualValues(right) : leftUnit.WithNotEqualValues(right); + } + } + } +} diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index f57c54f93a2d1..c245c3fbfd1c2 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -50,18 +50,18 @@ Microsoft.CodeAnalysis.GeneratorPostInitializationContext.CancellationToken.get Microsoft.CodeAnalysis.GeneratorPostInitializationContext.GeneratorPostInitializationContext() -> void Microsoft.CodeAnalysis.IMethodSymbol.MethodImplementationFlags.get -> System.Reflection.MethodImplAttributes Microsoft.CodeAnalysis.ITypeSymbol.IsRecord.get -> bool -Microsoft.CodeAnalysis.Operations.ISwitchExpressionOperation.IsExhaustive.get -> bool Microsoft.CodeAnalysis.LineMapping -Microsoft.CodeAnalysis.LineMapping.Deconstruct(out Microsoft.CodeAnalysis.Text.LinePositionSpan span, out Microsoft.CodeAnalysis.FileLinePositionSpan mappedSpan) -> void +Microsoft.CodeAnalysis.LineMapping.CharacterOffset.get -> int? Microsoft.CodeAnalysis.LineMapping.Equals(Microsoft.CodeAnalysis.LineMapping other) -> bool Microsoft.CodeAnalysis.LineMapping.IsHidden.get -> bool Microsoft.CodeAnalysis.LineMapping.LineMapping() -> void -Microsoft.CodeAnalysis.LineMapping.LineMapping(Microsoft.CodeAnalysis.Text.LinePositionSpan span, Microsoft.CodeAnalysis.FileLinePositionSpan mappedSpan) -> void +Microsoft.CodeAnalysis.LineMapping.LineMapping(Microsoft.CodeAnalysis.Text.LinePositionSpan span, int? characterOffset, Microsoft.CodeAnalysis.FileLinePositionSpan mappedSpan) -> void Microsoft.CodeAnalysis.LineMapping.MappedSpan.get -> Microsoft.CodeAnalysis.FileLinePositionSpan Microsoft.CodeAnalysis.LineMapping.Span.get -> Microsoft.CodeAnalysis.Text.LinePositionSpan override Microsoft.CodeAnalysis.LineMapping.Equals(object? obj) -> bool override Microsoft.CodeAnalysis.LineMapping.GetHashCode() -> int override Microsoft.CodeAnalysis.LineMapping.ToString() -> string? +Microsoft.CodeAnalysis.Operations.ISwitchExpressionOperation.IsExhaustive.get -> bool Microsoft.CodeAnalysis.Operations.OperationWalker Microsoft.CodeAnalysis.Operations.OperationWalker.OperationWalker() -> void Microsoft.CodeAnalysis.SourceProductionContext diff --git a/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs b/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs index eff85cb1d1b61..111fe5aeca5b4 100644 --- a/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs +++ b/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis { @@ -29,6 +31,11 @@ public enum PositionState : byte /// Remapped, + /// + /// Used in C# for spans inside of #line (startLine, startChar) - (endLine, endChar) charOffset directive + /// + RemappedSpan, + /// /// Used in VB for spans inside of a #ExternalSource directive that followed an unknown span /// @@ -56,6 +63,12 @@ public enum PositionState : byte // 0-based line it maps to. public readonly int MappedLine; + // 0-based mapped span from enhanced #line directive. + public readonly LinePositionSpan MappedSpan; + + // optional 0-based character offset from enhanced #line directive. + public readonly int? UnmappedCharacterOffset; + // raw value from #line or #ExternalDirective, may be null public readonly string? MappedPathOpt; @@ -66,6 +79,8 @@ public LineMappingEntry(int unmappedLine) { this.UnmappedLine = unmappedLine; this.MappedLine = unmappedLine; + this.MappedSpan = default; + this.UnmappedCharacterOffset = null; this.MappedPathOpt = null; this.State = PositionState.Unmapped; } @@ -76,12 +91,30 @@ public LineMappingEntry( string? mappedPathOpt, PositionState state) { + Debug.Assert(state != PositionState.RemappedSpan); + this.UnmappedLine = unmappedLine; this.MappedLine = mappedLine; + this.MappedSpan = default; + this.UnmappedCharacterOffset = null; this.MappedPathOpt = mappedPathOpt; this.State = state; } + public LineMappingEntry( + int unmappedLine, + LinePositionSpan mappedSpan, + int? unmappedCharacterOffset, + string? mappedPathOpt) + { + this.UnmappedLine = unmappedLine; + this.MappedLine = -1; + this.MappedSpan = mappedSpan; + this.UnmappedCharacterOffset = unmappedCharacterOffset; + this.MappedPathOpt = mappedPathOpt; + this.State = PositionState.RemappedSpan; + } + public int CompareTo(LineMappingEntry other) => UnmappedLine.CompareTo(other.UnmappedLine); diff --git a/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.cs b/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.cs index 3ad2f7bdbc6cc..ed0ed6793fd1d 100644 --- a/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.cs +++ b/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.cs @@ -55,18 +55,49 @@ public FileLinePositionSpan TranslateSpan(SourceText sourceText, string treeFile return TranslateSpan(entry, treeFilePath, unmappedStartPos, unmappedEndPos); } - protected FileLinePositionSpan TranslateSpan(LineMappingEntry entry, string treeFilePath, LinePosition unmappedStartPos, LinePosition unmappedEndPos) + protected FileLinePositionSpan TranslateSpan(in LineMappingEntry entry, string treeFilePath, LinePosition unmappedStartPos, LinePosition unmappedEndPos) { string path = entry.MappedPathOpt ?? treeFilePath; - int mappedStartLine = unmappedStartPos.Line - entry.UnmappedLine + entry.MappedLine; - int mappedEndLine = unmappedEndPos.Line - entry.UnmappedLine + entry.MappedLine; - - return new FileLinePositionSpan( - path, - new LinePositionSpan( - (mappedStartLine == -1) ? new LinePosition(unmappedStartPos.Character) : new LinePosition(mappedStartLine, unmappedStartPos.Character), - (mappedEndLine == -1) ? new LinePosition(unmappedEndPos.Character) : new LinePosition(mappedEndLine, unmappedEndPos.Character)), - hasMappedPath: entry.MappedPathOpt != null); + var span = entry.State == PositionState.RemappedSpan ? + TranslateEnhancedLineDirectiveSpan(entry, unmappedStartPos, unmappedEndPos) : + TranslateLineDirectiveSpan(entry, unmappedStartPos, unmappedEndPos); + return new FileLinePositionSpan(path, span, hasMappedPath: entry.MappedPathOpt != null); + } + + private static LinePositionSpan TranslateLineDirectiveSpan(in LineMappingEntry entry, LinePosition unmappedStartPos, LinePosition unmappedEndPos) + { + return new LinePositionSpan(translatePosition(entry, unmappedStartPos), translatePosition(entry, unmappedEndPos)); + + static LinePosition translatePosition(in LineMappingEntry entry, LinePosition unmapped) + { + int mappedLine = unmapped.Line - entry.UnmappedLine + entry.MappedLine; + return (mappedLine == -1) ? new LinePosition(unmapped.Character) : new LinePosition(mappedLine, unmapped.Character); + } + } + + private static LinePositionSpan TranslateEnhancedLineDirectiveSpan(in LineMappingEntry entry, LinePosition unmappedStartPos, LinePosition unmappedEndPos) + { + // A span starting on the first line, at or before 'UnmappedCharacterOffset' is + // mapped to the entire 'MappedSpan', regardless of the size of the unmapped span, + // even if the unmapped span ends before 'UnmappedCharacterOffset'. + if (unmappedStartPos.Line == entry.UnmappedLine && + unmappedStartPos.Character <= entry.UnmappedCharacterOffset.GetValueOrDefault()) + { + return entry.MappedSpan; + } + + // A span starting on the first line after 'UnmappedCharacterOffset', or starting on + // a subseqent line, is mapped to a span of corresponding size. + return new LinePositionSpan(translatePosition(entry, unmappedStartPos), translatePosition(entry, unmappedEndPos)); + + static LinePosition translatePosition(in LineMappingEntry entry, LinePosition unmapped) + { + return new LinePosition( + unmapped.Line - entry.UnmappedLine + entry.MappedSpan.Start.Line, + unmapped.Line == entry.UnmappedLine ? + entry.MappedSpan.Start.Character + unmapped.Character - entry.UnmappedCharacterOffset.GetValueOrDefault() : + unmapped.Character); + } } /// @@ -151,26 +182,6 @@ current.State is PositionState.Unmapped or PositionState.Unknown && current.MappedLine == 0 && current.MappedPathOpt == null); - LineMapping CreateCurrentEntryMapping(in LineMappingEntry entry, int unmappedEndLine, int lineLength, int currentIndex) - { - var unmapped = new LinePositionSpan( - new LinePosition(entry.UnmappedLine, character: 0), - new LinePosition(unmappedEndLine, lineLength)); - - var isHidden = - entry.State == PositionState.Hidden || - entry.State == PositionState.Unknown && GetUnknownStateVisibility(currentIndex) == LineVisibility.Hidden; - - var mapped = isHidden ? default : new FileLinePositionSpan( - entry.MappedPathOpt ?? string.Empty, - new LinePositionSpan( - new LinePosition(entry.MappedLine, character: 0), - new LinePosition(entry.MappedLine + unmappedEndLine - entry.UnmappedLine, lineLength)), - hasMappedPath: entry.MappedPathOpt != null); - - return new LineMapping(unmapped, mapped); - } - for (int i = 1; i < Entries.Length; i++) { var next = Entries[i]; @@ -197,7 +208,7 @@ LineMapping CreateCurrentEntryMapping(in LineMappingEntry entry, int unmappedEnd var endLine = lines[unmappedEndLine]; int lineLength = endLine.EndIncludingLineBreak - endLine.Start; - yield return CreateCurrentEntryMapping(current, unmappedEndLine, lineLength, currentIndex: i - 1); + yield return CreateLineMapping(current, unmappedEndLine, lineLength, currentIndex: i - 1); } current = next; @@ -218,8 +229,38 @@ LineMapping CreateCurrentEntryMapping(in LineMappingEntry entry, int unmappedEnd int lineLength = lastLine.EndIncludingLineBreak - lastLine.Start; int unmappedEndLine = lastLine.LineNumber; - yield return CreateCurrentEntryMapping(current, unmappedEndLine, lineLength, currentIndex: Entries.Length - 1); + yield return CreateLineMapping(current, unmappedEndLine, lineLength, currentIndex: Entries.Length - 1); + } + } + + private LineMapping CreateLineMapping(in LineMappingEntry entry, int unmappedEndLine, int lineLength, int currentIndex) + { + var unmapped = new LinePositionSpan( + new LinePosition(entry.UnmappedLine, character: 0), + new LinePosition(unmappedEndLine, lineLength)); + + if (entry.State == PositionState.Hidden || + entry.State == PositionState.Unknown && GetUnknownStateVisibility(currentIndex) == LineVisibility.Hidden) + { + return new LineMapping(unmapped, characterOffset: null, mappedSpan: default); } + + string path = entry.MappedPathOpt ?? string.Empty; + bool hasMappedPath = entry.MappedPathOpt != null; + + if (entry.State == PositionState.RemappedSpan) + { + return new LineMapping( + unmapped, + characterOffset: entry.UnmappedCharacterOffset, + new FileLinePositionSpan(path, entry.MappedSpan, hasMappedPath)); + } + + var mappedSpan = new LinePositionSpan( + new LinePosition(entry.MappedLine, character: 0), + new LinePosition(entry.MappedLine + unmappedEndLine - entry.UnmappedLine, lineLength)); + var mapped = new FileLinePositionSpan(path, mappedSpan, hasMappedPath); + return new LineMapping(unmapped, characterOffset: null, mapped); } } } diff --git a/src/Compilers/Core/Portable/Syntax/LineMapping.cs b/src/Compilers/Core/Portable/Syntax/LineMapping.cs index c6be68d43339d..a481c4dc45bcf 100644 --- a/src/Compilers/Core/Portable/Syntax/LineMapping.cs +++ b/src/Compilers/Core/Portable/Syntax/LineMapping.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.Serialization; +using System.Text; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -19,6 +19,11 @@ namespace Microsoft.CodeAnalysis /// public readonly LinePositionSpan Span { get; } + /// + /// The optional offset in the syntax tree for the line immediately following an enhanced #line directive in C#. + /// + public readonly int? CharacterOffset { get; } + /// /// If the line mapping directive maps the span into an explicitly specified file the is true. /// If the path is not mapped is empty and is false. @@ -26,18 +31,13 @@ namespace Microsoft.CodeAnalysis /// public readonly FileLinePositionSpan MappedSpan { get; } - public LineMapping(LinePositionSpan span, FileLinePositionSpan mappedSpan) + public LineMapping(LinePositionSpan span, int? characterOffset, FileLinePositionSpan mappedSpan) { Span = span; + CharacterOffset = characterOffset; MappedSpan = mappedSpan; } - public void Deconstruct(out LinePositionSpan span, out FileLinePositionSpan mappedSpan) - { - span = Span; - mappedSpan = MappedSpan; - } - /// /// True if the line mapping marks hidden code. /// @@ -48,10 +48,10 @@ public override bool Equals(object? obj) => obj is LineMapping other && Equals(other); public bool Equals(LineMapping other) - => Span.Equals(other.Span) && MappedSpan.Equals(other.MappedSpan); + => Span.Equals(other.Span) && CharacterOffset.Equals(other.CharacterOffset) && MappedSpan.Equals(other.MappedSpan); public override int GetHashCode() - => Hash.Combine(Span.GetHashCode(), MappedSpan.GetHashCode()); + => Hash.Combine(Hash.Combine(Span.GetHashCode(), CharacterOffset.GetHashCode()), MappedSpan.GetHashCode()); public static bool operator ==(LineMapping left, LineMapping right) => left.Equals(right); @@ -60,6 +60,17 @@ public override int GetHashCode() => !(left == right); public override string? ToString() - => $"{Span} -> {MappedSpan}"; + { + var builder = new StringBuilder(); + builder.Append(Span); + if (CharacterOffset.HasValue) + { + builder.Append(","); + builder.Append(CharacterOffset.GetValueOrDefault()); + } + builder.Append(" -> "); + builder.Append(MappedSpan); + return builder.ToString(); + } } } diff --git a/src/Compilers/Test/Core/TestResource.resx b/src/Compilers/Test/Core/TestResource.resx index c337fa629026e..92ff5df0788cc 100644 --- a/src/Compilers/Test/Core/TestResource.resx +++ b/src/Compilers/Test/Core/TestResource.resx @@ -867,7 +867,9 @@ namespace Comments.XmlComments.UndocumentedKeywords #line 6 #line 2 "test.cs" #line default -#line hidden +#line hidden +#line (1, 1) - (2, 2) 3 "test.cs" + Option Infer On diff --git a/src/Compilers/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.vbproj b/src/Compilers/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.vbproj index 840b7ecd76e75..a4e3694d5dca4 100644 --- a/src/Compilers/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.vbproj +++ b/src/Compilers/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.vbproj @@ -8,7 +8,7 @@ ..\BasicCodeAnalysisRules.ruleset true - partial + partial true diff --git a/src/EditorFeatures/CSharp/Microsoft.CodeAnalysis.CSharp.EditorFeatures.csproj b/src/EditorFeatures/CSharp/Microsoft.CodeAnalysis.CSharp.EditorFeatures.csproj index 37cc1a409ee8d..71eb76a34f9e7 100644 --- a/src/EditorFeatures/CSharp/Microsoft.CodeAnalysis.CSharp.EditorFeatures.csproj +++ b/src/EditorFeatures/CSharp/Microsoft.CodeAnalysis.CSharp.EditorFeatures.csproj @@ -6,7 +6,7 @@ Microsoft.CodeAnalysis.Editor.CSharp netcoreapp3.1;netstandard2.0 true - partial + partial true diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index d75f775bae394..134ee1de0e3d7 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -7,7 +7,7 @@ netcoreapp3.1;netstandard2.0 true $(DefineConstants);EDITOR_FEATURES - partial + partial Microsoft.CodeAnalysis.EditorFeatures.Common diff --git a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj index 98ac2d73b3bf1..e5cccc88f95fc 100644 --- a/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj +++ b/src/EditorFeatures/Text/Microsoft.CodeAnalysis.EditorFeatures.Text.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.Text netcoreapp3.1;netstandard2.0 - partial + partial true diff --git a/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj b/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj index 9a3cb1ca4f787..528fe72cace76 100644 --- a/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj +++ b/src/EditorFeatures/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj @@ -5,7 +5,7 @@ Library netcoreapp3.1;netstandard2.0 - partial + partial true diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs index 8c72c5ce9ce97..f3ca5140cfa6c 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs @@ -180,8 +180,11 @@ void AddStatement(LinePositionSpan unmappedLineSpan, ActiveStatement activeState } var hasAnyLineDirectives = false; - foreach (var (unmappedSection, mappedSection) in oldTree.GetLineMappings(cancellationToken)) + foreach (var lineMapping in oldTree.GetLineMappings(cancellationToken)) { + var unmappedSection = lineMapping.Span; + var mappedSection = lineMapping.MappedSpan; + hasAnyLineDirectives = true; var targetPath = mappedSection.HasMappedPath ? mappedSection.Path : oldTree.FilePath; diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index fad332e9ccc56..7f3239d0f5526 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -6,7 +6,7 @@ Microsoft.CodeAnalysis.CSharp true netcoreapp3.1;netstandard2.0 - partial + partial true diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index fbf621a2a8872..606559a0d60f3 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -8,7 +8,7 @@ netcoreapp3.1;netstandard2.0 $(DefineConstants);WORKSPACE true - partial + partial true diff --git a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj index 2fc914af44c65..602bb056c19b5 100644 --- a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj +++ b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj @@ -5,7 +5,7 @@ Library netcoreapp3.1;netstandard2.0 - partial + partial true