55
66using System ;
77using System . Collections . Generic ;
8+ using System . Collections . Immutable ;
89using System . Diagnostics ;
910using System . Linq ;
1011using Microsoft . AspNetCore . Razor . Language . Syntax . InternalSyntax ;
@@ -48,8 +49,7 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
4849 builder . Description = Resources . TagHelperPrefixDirective_Description ;
4950 } ) ;
5051
51- internal static ISet < string > DefaultKeywords = new HashSet < string > ( )
52- {
52+ internal static ImmutableHashSet < string > DefaultKeywords = ImmutableHashSet . Create (
5353 SyntaxConstants . CSharp . TagHelperPrefixKeyword ,
5454 SyntaxConstants . CSharp . AddTagHelperKeyword ,
5555 SyntaxConstants . CSharp . RemoveTagHelperKeyword ,
@@ -65,12 +65,12 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
6565 "namespace" ,
6666 "class" ,
6767 "where"
68- } ;
68+ ) ;
6969
70- private readonly ISet < string > CurrentKeywords = new HashSet < string > ( DefaultKeywords ) ;
70+ private readonly ImmutableHashSet < string > CurrentKeywords ;
7171
72- private readonly Dictionary < CSharpKeyword , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > _keywordParserMap = new Dictionary < CSharpKeyword , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > ( ) ;
73- private readonly Dictionary < string , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > _directiveParserMap = new Dictionary < string , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > ( StringComparer . Ordinal ) ;
72+ private readonly ImmutableDictionary < CSharpKeyword , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > _keywordParserMap ;
73+ private readonly ImmutableDictionary < string , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > _directiveParserMap ;
7474
7575 public CSharpCodeParser ( ParserContext context )
7676 : this ( directives : Enumerable . Empty < DirectiveDescriptor > ( ) , context : context )
@@ -90,15 +90,103 @@ public CSharpCodeParser(IEnumerable<DirectiveDescriptor> directives, ParserConte
9090 throw new ArgumentNullException ( nameof ( context ) ) ;
9191 }
9292
93- Keywords = new HashSet < string > ( ) ;
93+ var keywordsBuilder = ImmutableHashSet < string > . Empty . ToBuilder ( ) ;
94+ var keywordParserMapBuilder = ImmutableDictionary < CSharpKeyword , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > . Empty . ToBuilder ( ) ;
95+ var currentKeywordsBuilder = DefaultKeywords . ToBuilder ( ) ;
96+ var directiveParserMapBuilder = ImmutableDictionary . CreateBuilder < string , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > ( StringComparer . Ordinal ) ;
97+
9498 SetupKeywordParsers ( ) ;
9599 SetupExpressionParsers ( ) ;
96100 SetupDirectiveParsers ( directives ) ;
101+
102+ Keywords = keywordsBuilder . ToImmutable ( ) ;
103+ CurrentKeywords = currentKeywordsBuilder . ToImmutable ( ) ;
104+ _keywordParserMap = keywordParserMapBuilder . ToImmutable ( ) ;
105+ _directiveParserMap = directiveParserMapBuilder . ToImmutable ( ) ;
106+
107+ void SetupKeywordParsers ( )
108+ {
109+ MapKeywords ( ParseConditionalBlock , topLevel : true , CSharpKeyword . For , CSharpKeyword . Foreach , CSharpKeyword . While , CSharpKeyword . Switch , CSharpKeyword . Lock ) ;
110+ MapKeywords ( ParseCaseStatement , topLevel : false , CSharpKeyword . Case , CSharpKeyword . Default ) ;
111+ MapKeywords ( ParseIfStatement , topLevel : true , CSharpKeyword . If ) ;
112+ MapKeywords ( ParseTryStatement , topLevel : true , CSharpKeyword . Try ) ;
113+ MapKeywords ( ParseDoStatement , topLevel : true , CSharpKeyword . Do ) ;
114+ MapKeywords ( ParseUsingKeyword , topLevel : true , CSharpKeyword . Using ) ;
115+ }
116+
117+ void MapKeywords (
118+ Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > handler ,
119+ bool topLevel ,
120+ params CSharpKeyword [ ] keywords )
121+ {
122+ foreach ( var keyword in keywords )
123+ {
124+ keywordParserMapBuilder . Add ( keyword , handler ) ;
125+ if ( topLevel )
126+ {
127+ keywordsBuilder . Add ( CSharpLanguageCharacteristics . GetKeyword ( keyword ) ) ;
128+ }
129+ }
130+ }
131+
132+ void SetupExpressionParsers ( )
133+ {
134+ keywordParserMapBuilder . Add ( CSharpKeyword . Await , ParseAwaitExpression ) ;
135+ }
136+
137+ void SetupDirectiveParsers ( IEnumerable < DirectiveDescriptor > directiveDescriptors )
138+ {
139+ foreach ( var directiveDescriptor in directiveDescriptors )
140+ {
141+ currentKeywordsBuilder . Add ( directiveDescriptor . Directive ) ;
142+ MapDirectives ( ( builder , transition ) => ParseExtensibleDirective ( builder , transition , directiveDescriptor ) , directiveParserMapBuilder , keywordsBuilder , context , directiveDescriptor . Directive ) ;
143+ }
144+
145+ MapDirectives ( ParseTagHelperPrefixDirective , directiveParserMapBuilder , keywordsBuilder , context , SyntaxConstants . CSharp . TagHelperPrefixKeyword ) ;
146+ MapDirectives ( ParseAddTagHelperDirective , directiveParserMapBuilder , keywordsBuilder , context , SyntaxConstants . CSharp . AddTagHelperKeyword ) ;
147+ MapDirectives ( ParseRemoveTagHelperDirective , directiveParserMapBuilder , keywordsBuilder , context , SyntaxConstants . CSharp . RemoveTagHelperKeyword ) ;
148+
149+ // If there wasn't any extensible directives relating to the reserved directives then map them.
150+ if ( ! directiveParserMapBuilder . ContainsKey ( "class" ) )
151+ {
152+ MapDirectives ( ParseReservedDirective , directiveParserMapBuilder , keywordsBuilder , context , "class" ) ;
153+ }
154+
155+ if ( ! directiveParserMapBuilder . ContainsKey ( "namespace" ) )
156+ {
157+ MapDirectives ( ParseReservedDirective , directiveParserMapBuilder , keywordsBuilder , context , "namespace" ) ;
158+ }
159+ }
160+
161+ static void MapDirectives (
162+ Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > handler ,
163+ ImmutableDictionary < string , Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > > . Builder directiveParserMap ,
164+ ImmutableHashSet < string > . Builder keywords ,
165+ ParserContext context ,
166+ params string [ ] directives )
167+ {
168+ foreach ( var directive in directives )
169+ {
170+ if ( directiveParserMap . ContainsKey ( directive ) )
171+ {
172+ // It is possible for the list to contain duplicates in cases when the project is misconfigured.
173+ // In those cases, we shouldn't register multiple handlers per keyword.
174+ continue ;
175+ }
176+
177+ directiveParserMap . Add ( directive , ( builder , transition ) =>
178+ {
179+ handler ( builder , transition ) ;
180+ context . SeenDirectives . Add ( directive ) ;
181+ } ) ;
182+ keywords . Add ( directive ) ;
183+ }
184+ }
97185 }
98186
99187 public HtmlMarkupParser HtmlParser { get ; set ; }
100188
101- protected internal ISet < string > Keywords { get ; private set ; }
189+ protected internal ImmutableHashSet < string > Keywords { get ; private set ; }
102190
103191 public bool IsNested { get ; set ; }
104192
@@ -908,30 +996,6 @@ protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder,
908996 return false ;
909997 }
910998
911- private void SetupDirectiveParsers ( IEnumerable < DirectiveDescriptor > directiveDescriptors )
912- {
913- foreach ( var directiveDescriptor in directiveDescriptors )
914- {
915- CurrentKeywords . Add ( directiveDescriptor . Directive ) ;
916- MapDirectives ( ( builder , transition ) => ParseExtensibleDirective ( builder , transition , directiveDescriptor ) , directiveDescriptor . Directive ) ;
917- }
918-
919- MapDirectives ( ParseTagHelperPrefixDirective , SyntaxConstants . CSharp . TagHelperPrefixKeyword ) ;
920- MapDirectives ( ParseAddTagHelperDirective , SyntaxConstants . CSharp . AddTagHelperKeyword ) ;
921- MapDirectives ( ParseRemoveTagHelperDirective , SyntaxConstants . CSharp . RemoveTagHelperKeyword ) ;
922-
923- // If there wasn't any extensible directives relating to the reserved directives then map them.
924- if ( ! _directiveParserMap . ContainsKey ( "class" ) )
925- {
926- MapDirectives ( ParseReservedDirective , "class" ) ;
927- }
928-
929- if ( ! _directiveParserMap . ContainsKey ( "namespace" ) )
930- {
931- MapDirectives ( ParseReservedDirective , "namespace" ) ;
932- }
933- }
934-
935999 private void EnsureDirectiveIsAtStartOfLine ( )
9361000 {
9371001 // 1 is the offset of the @ transition for the directive.
@@ -955,28 +1019,6 @@ private void EnsureDirectiveIsAtStartOfLine()
9551019 }
9561020 }
9571021
958- // Internal for testing.
959- internal void MapDirectives ( Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > handler , params string [ ] directives )
960- {
961- foreach ( var directive in directives )
962- {
963- if ( _directiveParserMap . ContainsKey ( directive ) )
964- {
965- // It is possible for the list to contain duplicates in cases when the project is misconfigured.
966- // In those cases, we shouldn't register multiple handlers per keyword.
967- continue ;
968- }
969-
970- _directiveParserMap . Add ( directive , ( builder , transition ) =>
971- {
972- handler ( builder , transition ) ;
973- Context . SeenDirectives . Add ( directive ) ;
974- } ) ;
975-
976- Keywords . Add ( directive ) ;
977- }
978- }
979-
9801022 private void ParseTagHelperPrefixDirective ( SyntaxListBuilder < RazorSyntaxNode > builder , CSharpTransitionSyntax transition )
9811023 {
9821024 RazorDiagnostic duplicateDiagnostic = null ;
@@ -1791,51 +1833,6 @@ private bool AtBooleanLiteral()
17911833 return result . HasValue && ( result . Value == CSharpKeyword . True || result . Value == CSharpKeyword . False ) ;
17921834 }
17931835
1794- private void SetupExpressionParsers ( )
1795- {
1796- MapExpressionKeyword ( ParseAwaitExpression , CSharpKeyword . Await ) ;
1797- }
1798-
1799- private void SetupKeywordParsers ( )
1800- {
1801- MapKeywords (
1802- ParseConditionalBlock ,
1803- CSharpKeyword . For ,
1804- CSharpKeyword . Foreach ,
1805- CSharpKeyword . While ,
1806- CSharpKeyword . Switch ,
1807- CSharpKeyword . Lock ) ;
1808- MapKeywords ( ParseCaseStatement , false , CSharpKeyword . Case , CSharpKeyword . Default ) ;
1809- MapKeywords ( ParseIfStatement , CSharpKeyword . If ) ;
1810- MapKeywords ( ParseTryStatement , CSharpKeyword . Try ) ;
1811- MapKeywords ( ParseDoStatement , CSharpKeyword . Do ) ;
1812- MapKeywords ( ParseUsingKeyword , CSharpKeyword . Using ) ;
1813- }
1814-
1815- private void MapExpressionKeyword ( Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > handler , CSharpKeyword keyword )
1816- {
1817- _keywordParserMap . Add ( keyword , handler ) ;
1818-
1819- // Expression keywords don't belong in the regular keyword list
1820- }
1821-
1822- private void MapKeywords ( Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > handler , params CSharpKeyword [ ] keywords )
1823- {
1824- MapKeywords ( handler , topLevel : true , keywords : keywords ) ;
1825- }
1826-
1827- private void MapKeywords ( Action < SyntaxListBuilder < RazorSyntaxNode > , CSharpTransitionSyntax > handler , bool topLevel , params CSharpKeyword [ ] keywords )
1828- {
1829- foreach ( var keyword in keywords )
1830- {
1831- _keywordParserMap . Add ( keyword , handler ) ;
1832- if ( topLevel )
1833- {
1834- Keywords . Add ( CSharpLanguageCharacteristics . GetKeyword ( keyword ) ) ;
1835- }
1836- }
1837- }
1838-
18391836 private void ParseAwaitExpression ( SyntaxListBuilder < RazorSyntaxNode > builder , CSharpTransitionSyntax transition )
18401837 {
18411838 // Ensure that we're on the await statement (only runs in debug)
0 commit comments