Skip to content

Commit

Permalink
Support directive tokenization (#10979)
Browse files Browse the repository at this point in the history
* Add new preprocessor tests for the old parser, and copy their baselines over to the new parser for comparison.

* Add line whitespace tracking for erroring when a preprocessor token isn't the first thing on a line.

* Add initial support for parsing directives in the new lexer.

* Initial work on plumbing through parser changes to correctly support directives. Many more tests are required.

* Add more tests for other control flow blocks

* Several misc refactors

* Error on def and undef

* Start testing beginning-of-line enforcement.

* Report a warning when we see potential misplaced directives in disabled text.

* Add more tests, include the correct location in warnings.

* Usings, recomment GenerateBaselines

* Nullable annotations

* Update baselines, skip design time verification where tracked by #10981.

* Feedback.

* More feedback.
  • Loading branch information
333fred authored Oct 11, 2024
1 parent 87dafaf commit 07b168a
Show file tree
Hide file tree
Showing 322 changed files with 8,661 additions and 120 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp;
using Xunit;

namespace Microsoft.AspNetCore.Razor.Language.Legacy;

public class CSharpPreprocessorTest() : ParserTestBase(layer: TestProject.Layer.Compiler, validateSpanEditHandlers: true, useLegacyTokenizer: true)
{
[Fact]
public void Pragmas()
{
ParseDocumentTest("""
@{
#pragma warning disable 123
#pragma warning restore 123
#pragma checksum "file.cs" "{00000000-0000-0000-0000-000000000000}" "1234"
}
""");
}

[Fact]
public void NullableDirectives()
{
ParseDocumentTest("""
@{
#nullable enable
#nullable disable
#nullable restore
#nullable enable annotations
#nullable disable annotations
#nullable restore annotations
#nullable enable warnings
#nullable disable warnings
#nullable restore warnings
}
""");
}

[Fact]
public void DefineThenIfDef()
{
ParseDocumentTest("""
@{
#define SYMBOL
#if SYMBOL
#undef SYMBOL
#if SYMBOL
var x = 1;
#endif
#else
var x = 1;
#endif
}
""");
}

[Fact]
public void ErrorWarningLine()
{
ParseDocumentTest("""
@{
#line 1 "file.cs"
#error This is an error
#line default
#warning This is a warning
#line hidden
#line (1, 1) - (5, 60) 10 "partial-class.cs"
}
""");
}

[Fact]
public void Regions()
{
ParseDocumentTest("""
@{
#region MyRegion }
#endregion
}
""");
}

[Fact]
public void SimpleIfDef()
{
ParseDocumentTest("""
@{
#if true
var x = 1;
#endif
}
""");
}

[Fact]
public void IfDefFromParseOptions_Symbol()
{
IfDefFromParseOptions("SYMBOL");
}

[Fact]
public void IfDefFromParseOptions_Symbol2()
{
IfDefFromParseOptions("SYMBOL2");
}

[Fact]
public void IfDefFromParseOptions_None()
{
IfDefFromParseOptions(null);
}

private void IfDefFromParseOptions(string? directive)
{
var parseOptions = CSharpParseOptions.Default;

if (directive != null)
{
parseOptions = parseOptions.WithPreprocessorSymbols(ImmutableArray.Create(directive));
}

ParseDocumentTest("""
@{
#if SYMBOL
var x = 1;
#elif SYMBOL2
var x = 2;
#else
var x = 3;
#endif
}
""", parseOptions);
}

[Fact]
public void IfDefAcrossMultipleBlocks()
{
ParseDocumentTest("""
@{
#if false
var x = 1;
}
<div>
<p>Content</p>
</div>
@{
var y = 2;
#endif
}
""");
}

[Fact]
public void IfDefDisabledSectionUnbalanced()
{
ParseDocumentTest("""
@{
#if false
void M() {
#endif
}
""");
}

[Fact]
public void IfDefNotOnNewline_01()
{
ParseDocumentTest("""
@{ #if false }
<div>
<p>Content</p>
</div>
@{
#endif
}
""");
}

[Fact]
public void IfDefNotOnNewline_02()
{
ParseDocumentTest("""
@{#if false }
<div>
<p>Content</p>
</div>
@{
#endif
}
""");
}

[Fact]
public void ElIfNotOnNewline()
{
ParseDocumentTest("""
@{
#if true
}
<div>
<p>Content</p>
</div>
@{ #elif false }
<div>
<p>Content2</p>
</div>
@{
#endif
}
""");
}

[Fact]
public void ElseNotOnNewline()
{
ParseDocumentTest("""
@{
#if true
}
<div>
<p>Content</p>
</div>
@{ #else }
<div>
<p>Content2</p>
</div>
@{
#endif
}
""");
}

[Fact]
public void EndIfNotOnNewline()
{
ParseDocumentTest("""
@{
#if false
}
<div>
<p>Content</p>
</div>
@{ #endif }
""");
}

[Fact]
public void UsingStatementResults()
{
ParseDocumentTest("""
@using (var test = blah)
#if true
{
#endif
}
""");
}

[Fact]
public void IfStatementAfterIf()
{
ParseDocumentTest("""
@if (true)
#if true
{
#endif
}
""");
}

[Fact]
public void IfStatementAfterIfBlock()
{
ParseDocumentTest("""
@if (true)
{
#if true
}
#endif
""");
}

[Fact]
public void IfStatementAfterIfBeforeElseIf()
{
ParseDocumentTest("""
@if (true)
{
}
#if true
else if (false)
#endif
{
}
""");
}

[Fact]
public void IfStatementAfterElseIf()
{
ParseDocumentTest("""
@if (true)
{
}
else if (false)
#if true
{
#endif
}
""");
}

[Fact]
public void IfStatementAfterElseIfBeforeElse()
{
ParseDocumentTest("""
@if (true)
{
}
else if (false)
{
}
#if true
else
#endif
{
}
""");
}

[Fact]
public void IfStatementAfterElse()
{
ParseDocumentTest("""
@if (true)
{
}
else if (false)
{
}
else
#if true
{
#endif
}
""");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Markup span at (0:0,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [115] )
Transition span at (0:0,0 [1] ) (Accepts:None) - Parent: Statement block at (0:0,0 [115] )
MetaCode span at (1:0,1 [1] ) (Accepts:None) - Parent: Statement block at (0:0,0 [115] )
Code span at (2:0,2 [112] ) (Accepts:Any) - Parent: Statement block at (0:0,0 [115] )
MetaCode span at (114:10,0 [1] ) (Accepts:None) - Parent: Statement block at (0:0,0 [115] )
Markup span at (115:10,1 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [115] )
Loading

0 comments on commit 07b168a

Please sign in to comment.