changeset 34775:e0059b2d4e2b

runtime(java): Improve the matching of contextual keywords Commit: https://github.com/vim/vim/commit/5ccdcc482e299609ae8852a75b22190e38b9b5df Author: Aliaksei Budavei <0x000c70@gmail.com> Date: Thu Apr 4 21:51:18 2024 +0200 runtime(java): Improve the matching of contextual keywords - Recognise a _record_ contextual keyword. - Recognise _non-sealed_, _sealed_, and _permits_ contextual keywords. - Admit _$_ to keyword characters. - Group _abstract_, _final_, _default_, _(non-)sealed_ (apart from _(non-)sealed_, the incompossibility of these modifiers calls for attention). - Remove another _synchronized_ keyword redefinition. I have also replaced a function with an expression. Before patch 8.1.0515, it should have been declared :function! to work with repeatable script sourcing; there is less to worry about with an expression. References: https://openjdk.org/jeps/395 (Records) https://openjdk.org/jeps/409 (Sealed Classes) https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.8 closes: #14403 Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 04 Apr 2024 22:00:05 +0200
parents 6f294e4d5b71
children 19e038a1139a
files runtime/syntax/java.vim runtime/syntax/testdir/dumps/java_contextual_keywords_00.dump runtime/syntax/testdir/dumps/java_contextual_keywords_01.dump runtime/syntax/testdir/dumps/java_contextual_keywords_02.dump runtime/syntax/testdir/dumps/java_contextual_keywords_99.dump runtime/syntax/testdir/input/java_contextual_keywords.java
diffstat 6 files changed, 172 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/syntax/java.vim
+++ b/runtime/syntax/java.vim
@@ -2,7 +2,7 @@
 " Language:	Java
 " Maintainer:	Claudio Fleiner <claudio@fleiner.com>
 " URL:          https://github.com/fleiner/vim/blob/master/runtime/syntax/java.vim
-" Last Change:	2024 Mar 25
+" Last Change:	2024 Apr 04
 
 " Please check :help java.vim for comments on some of the options available.
 
@@ -19,6 +19,9 @@ endif
 let s:cpo_save = &cpo
 set cpo&vim
 
+" Admit the ASCII dollar sign to keyword characters (JLS-17, §3.8):
+execute printf('syntax iskeyword %s,$', &l:iskeyword)
+
 " some characters that cannot be in a java program (outside a string)
 syn match javaError "[\\@`]"
 syn match javaError "<<<\|\.\.\|=>\|||=\|&&=\|\*\/"
@@ -27,16 +30,17 @@ syn match javaError "<<<\|\.\.\|=>\|||=\
 syn match   javaError2 "#\|=<"
 hi def link javaError2 javaError
 
-" keyword definitions
+" Keywords (JLS-17, §3.9):
 syn keyword javaExternal	native package
-syn match javaExternal		"\<import\>\(\s\+static\>\)\?"
+syn match   javaExternal	"\<import\>\%(\s\+static\>\)\="
 syn keyword javaError		goto const
 syn keyword javaConditional	if else switch
 syn keyword javaRepeat		while for do
 syn keyword javaBoolean		true false
 syn keyword javaConstant	null
 syn keyword javaTypedef		this super
-syn keyword javaOperator	var new instanceof
+syn keyword javaOperator	new instanceof
+syn match   javaOperator	"\<var\>\%(\s*(\)\@!"
 " Since the yield statement, which could take a parenthesised operand,
 " and _qualified_ yield methods get along within the switch block
 " (JLS-17, §3.8), it seems futile to make a region definition for this
@@ -48,14 +52,15 @@ syn match   javaOperator	"\%(\%(::\|\.\)
 syn keyword javaType		boolean char byte short int long float double
 syn keyword javaType		void
 syn keyword javaStatement	return
-syn keyword javaStorageClass	static synchronized transient volatile final strictfp serializable
+syn keyword javaStorageClass	static synchronized transient volatile strictfp serializable
 syn keyword javaExceptions	throw try catch finally
 syn keyword javaAssert		assert
-syn keyword javaMethodDecl	synchronized throws
-syn keyword javaClassDecl	extends implements interface
-" to differentiate the keyword class from MyClass.class we use a match here
+syn keyword javaMethodDecl	throws
+" Differentiate a "MyClass.class" literal from the keyword "class".
 syn match   javaTypedef		"\.\s*\<class\>"ms=s+1
-syn keyword javaClassDecl	enum
+syn keyword javaClassDecl	enum extends implements interface
+syn match   javaClassDecl	"\<permits\>\%(\s*(\)\@!"
+syn match   javaClassDecl	"\<record\>\%(\s*(\)\@!"
 syn match   javaClassDecl	"^class\>"
 syn match   javaClassDecl	"[^.]\s*\<class\>"ms=s+1
 syn match   javaAnnotation	"@\([_$a-zA-Z][_$a-zA-Z0-9]*\.\)*[_$a-zA-Z][_$a-zA-Z0-9]*\>" contains=javaString
@@ -63,19 +68,27 @@ syn match   javaClassDecl	"@interface\>"
 syn keyword javaBranch		break continue nextgroup=javaUserLabelRef skipwhite
 syn match   javaUserLabelRef	"\k\+" contained
 syn match   javaVarArg		"\.\.\."
-syn keyword javaScopeDecl	public protected private abstract
+syn keyword javaScopeDecl	public protected private
+syn keyword javaConceptKind	abstract final
+syn match   javaConceptKind	"\<non-sealed\>"
+syn match   javaConceptKind	"\<sealed\>\%(\s*(\)\@!"
 syn match   javaConceptKind	"\<default\>\%(\s*\%(:\|->\)\)\@!"
 
-function s:isModuleInfoDeclarationCurrentBuffer() abort
-    return fnamemodify(bufname("%"), ":t") =~ '^module-info\%(\.class\>\)\@!'
-endfunction
+" Note that a "module-info" file will be recognised with an arbitrary
+" file extension (or no extension at all) so that more than one such
+" declaration for the same Java module can be maintained for modular
+" testing in a project without attendant confusion for IDEs, with the
+" ".java\=" extension used for a production version and an arbitrary
+" extension used for a testing version.
+let s:module_info_cur_buf = fnamemodify(bufname("%"), ":t") =~ '^module-info\%(\.class\>\)\@!'
+lockvar s:module_info_cur_buf
 
-" Java Modules(Since Java 9, for "module-info.java" file)
-if s:isModuleInfoDeclarationCurrentBuffer()
-    syn keyword javaModuleStorageClass	module transitive
-    syn keyword javaModuleStmt		open requires exports opens uses provides
-    syn keyword javaModuleExternal	to with
-    syn cluster javaTop add=javaModuleStorageClass,javaModuleStmt,javaModuleExternal
+" Java modules (since Java 9, for "module-info.java" file).
+if s:module_info_cur_buf
+  syn keyword javaModuleStorageClass	module transitive
+  syn keyword javaModuleStmt		open requires exports opens uses provides
+  syn keyword javaModuleExternal	to with
+  syn cluster javaTop add=javaModuleStorageClass,javaModuleStmt,javaModuleExternal
 endif
 
 if exists("java_highlight_java_lang_ids")
@@ -111,7 +124,7 @@ if exists("java_highlight_all")  || exis
   syn match   javaC_JavaLang "\%(\<Enum\.\)\@<=\<EnumDesc\>"
   syn keyword javaC_JavaLang Boolean Character Class ClassLoader Compiler Double Float Integer Long Math Number Object Process Runtime SecurityManager String StringBuffer Thread ThreadGroup Byte Short Void InheritableThreadLocal Package RuntimePermission ThreadLocal StrictMath StackTraceElement Enum ProcessBuilder StringBuilder ClassValue Module ModuleLayer StackWalker Record
   syn match   javaC_JavaLang "\<System\>"	" See javaDebug.
-  " As of Java 21, java.lang.Compiler is no more (deprecated in Java 9).
+  " As of JDK 21, java.lang.Compiler is no more (deprecated in JDK 9).
   syn keyword javaLangDeprecated Compiler
   syn cluster javaTop add=javaC_JavaLang
   syn cluster javaClasses add=javaC_JavaLang
@@ -275,17 +288,17 @@ syn cluster javaTop add=javaString,javaS
 
 if exists("java_highlight_functions")
   if java_highlight_functions == "indent"
-    syn match  javaFuncDef "^\(\t\| \{8\}\)[_$a-zA-Z][_$a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation
-    syn region javaFuncDef start=+^\(\t\| \{8\}\)[$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation
-    syn match  javaFuncDef "^  [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation
-    syn region javaFuncDef start=+^  [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation
+    syn match  javaFuncDef "^\(\t\| \{8\}\)[_$a-zA-Z][_$a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaConceptKind,javaType,javaStorageClass,@javaClasses,javaAnnotation
+    syn region javaFuncDef start=+^\(\t\| \{8\}\)[$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaConceptKind,javaType,javaStorageClass,@javaClasses,javaAnnotation
+    syn match  javaFuncDef "^  [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaConceptKind,javaType,javaStorageClass,@javaClasses,javaAnnotation
+    syn region javaFuncDef start=+^  [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaConceptKind,javaType,javaStorageClass,@javaClasses,javaAnnotation
   else
     " This line catches method declarations at any indentation>0, but it assumes
     " two things:
     "	1. class names are always capitalized (ie: Button)
     "	2. method names are never capitalized (except constructors, of course)
     "syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^>]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*([^0-9]+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses
-    syn region javaFuncDef start=+^\s\+\%(\%(public\|protected\|private\|static\|\%(abstract\|default\)\|final\|native\|synchronized\)\s\+\)*\%(<.*>\s\+\)\?\%(\%(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\%([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\%(<[^(){}]*>\)\=\%(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*(+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses,javaAnnotation
+    syn region javaFuncDef start=+^\s\+\%(\%(public\|protected\|private\|static\|\%(abstract\|default\)\|final\|native\|synchronized\)\s\+\)*\%(<.*>\s\+\)\?\%(\%(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\%([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\%(<[^(){}]*>\)\=\%(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*(+ end=+)+ contains=javaScopeDecl,javaConceptKind,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses,javaAnnotation
   endif
   syn match   javaLambdaDef "\<\K\k*\>\%(\<default\>\)\@<!\s*->"
   syn match  javaBraces  "[{}]"
@@ -440,10 +453,10 @@ hi def link htmlArg			Type
 hi def link htmlString			String
 hi def link javaSpaceError		Error
 
-if s:isModuleInfoDeclarationCurrentBuffer()
-    hi def link javaModuleStorageClass	StorageClass
-    hi def link javaModuleStmt		Statement
-    hi def link javaModuleExternal	Include
+if s:module_info_cur_buf
+  hi def link javaModuleStorageClass	StorageClass
+  hi def link javaModuleStmt		Statement
+  hi def link javaModuleExternal	Include
 endif
 
 let b:current_syntax = "java"
@@ -452,9 +465,8 @@ if main_syntax == 'java'
   unlet main_syntax
 endif
 
-delfunction! s:isModuleInfoDeclarationCurrentBuffer
-let b:spell_options="contained"
+let b:spell_options = "contained"
 let &cpo = s:cpo_save
-unlet s:cpo_save
+unlet s:module_info_cur_buf s:cpo_save
 
 " vim: ts=8
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_contextual_keywords_00.dump
@@ -0,0 +1,20 @@
+>c+0#00e0003#ffffff0|l|a|s@1| +0#0000000&|C|o|n|t|e|x|t|u|a|l|K|e|y|w|o|r|d|s|T|e|s|t|s| @6|/+0#0000e05&@1| |S|e@1| |J|L|S|,| |§|3|.|9| |K|e|y|w|o|r|d|s|.| +0#0000000&@12
+|{| @73
+@4|p+0#00e0003&|r|i|v|a|t|e| +0#0000000&|C|o|n|t|e|x|t|u|a|l|K|e|y|w|o|r|d|s|T|e|s|t|s|(|)| |{| |t+0#af5f00255&|h|r|o|w| +0#0000000&|n+0#af5f00255&|e|w| +0#0000000&|E|r@1|o|r|(|)|;| |}| @14
+@75
+@4|/+0#0000e05&@1| |M|o|d|u|l|e|D|e|c|l|a|r|a|t|i|o|n|:| |m|o|d|u|l|e| |o|p|e|n|.| +0#0000000&@36
+@4|v+0#00e0003&|o|i|d| +0#0000000&|m|o|d|u|l|e|(|)| @2|{| |O|b|j|e|c|t| |m|o|d|u|l|e| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @4|w|h|e|n|(|)|;| |}| @17
+@4|v+0#00e0003&|o|i|d| +0#0000000&|o|p|e|n|(|)| |{| |O|b|j|e|c|t| |o|p|e|n| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @6|m|o|d|u|l|e|(|)|;| |}| @19
+@4|/+0#0000e05&@1| |M|o|d|u|l|e|D|i|r|e|c|t|i|v|e|:| |e|x|p|o|r|t|s| |o|p|e|n|s| |p|r|o|v|i|d|e|s| |r|e|q|u|i|r|e|s| |t|o| |u|s|e|s| |w|i|t|h|.| +0#0000000&@5
+@4|v+0#00e0003&|o|i|d| +0#0000000&|e|x|p|o|r|t|s|(|)| @1|{| |O|b|j|e|c|t| |e|x|p|o|r|t|s| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @3|o|p|e|n|(|)|;| |}| @17
+@4|v+0#00e0003&|o|i|d| +0#0000000&|o|p|e|n|s|(|)| @3|{| |O|b|j|e|c|t| |o|p|e|n|s| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @5|e|x|p|o|r|t|s|(|)|;| |}| @14
+@4|v+0#00e0003&|o|i|d| +0#0000000&|p|r|o|v|i|d|e|s|(|)| |{| |O|b|j|e|c|t| |p|r|o|v|i|d|e|s| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @2|o|p|e|n|s|(|)|;| |}| @16
+@4|v+0#00e0003&|o|i|d| +0#0000000&|r|e|q|u|i|r|e|s|(|)| |{| |O|b|j|e|c|t| |r|e|q|u|i|r|e|s| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @2|p|r|o|v|i|d|e|s|(|)|;| |}| @13
+@4|v+0#00e0003&|o|i|d| +0#0000000&|t|o|(|)| @2|{| |O|b|j|e|c|t| |t|o| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @4|r|e|q|u|i|r|e|s|(|)|;| |}| @21
+@4|v+0#00e0003&|o|i|d| +0#0000000&|u|s|e|s|(|)| |{| |O|b|j|e|c|t| |u|s|e|s| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @6|t|o|(|)|;| |}| @23
+@4|v+0#00e0003&|o|i|d| +0#0000000&|w|i|t|h|(|)| |{| |O|b|j|e|c|t| |w|i|t|h| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @6|u|s|e|s|(|)|;| |}| @21
+@4|/+0#0000e05&@1| |R|e|q|u|i|r|e|s|M|o|d|i|f|i|e|r|:| |t|r|a|n|s|i|t|i|v|e|.| +0#0000000&@38
+@4|v+0#00e0003&|o|i|d| +0#0000000&|t|r|a|n|s|i|t|i|v|e|(|)| |{| |O|b|j|e|c|t| |t|r|a|n|s|i|t|i|v|e| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @2|w|i|t|h|(|)|;| |}| @13
+@4|/+0#0000e05&@1| |L|o|c|a|l|V|a|r|i|a|b|l|e|T|y|p|e| ||| |L|a|m|b|d|a|P|a|r|a|m|e|t|e|r|T|y|p|e|:| |v|a|r|.| +0#0000000&@22
+@4|v+0#00e0003&|o|i|d| +0#0000000&|v|a|r|(|)| @1|{| |v+0#af5f00255&|a|r| +0#0000000&|v+0#af5f00255&|a|r| +0#0000000&|=| |n+0#af5f00255&|e|w| +0#0000000&|O|b|j|e|c|t|(|)|;| @2|t|r|a|n|s|i|t|i|v|e|(|)|;| |}| @15
+@57|1|,|1| @10|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_contextual_keywords_01.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@3|v+0#00e0003&|o|i|d| +0#0000000&|u|s|e|s|(|)| |{| |O|b|j|e|c|t| |u|s|e|s| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @6|t|o|(|)|;| |}| @23
+@4|v+0#00e0003&|o|i|d| +0#0000000&|w|i|t|h|(|)| |{| |O|b|j|e|c|t| |w|i|t|h| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @6|u|s|e|s|(|)|;| |}| @21
+@4|/+0#0000e05&@1| |R|e|q|u|i|r|e|s|M|o|d|i|f|i|e|r|:| |t|r|a|n|s|i|t|i|v|e|.| +0#0000000&@38
+@4|v+0#00e0003&|o|i|d| +0#0000000&|t|r|a|n|s|i|t|i|v|e|(|)| |{| |O|b|j|e|c|t| |t|r|a|n|s|i|t|i|v|e| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @2|w|i|t|h|(|)|;| |}| @13
+@4|/+0#0000e05&@1| |L|o|c|a|l|V|a|r|i|a|b|l|e|T|y|p|e| ||| |L|a|m|b|d|a|P|a|r|a|m|e|t|e|r|T|y|p|e|:| |v|a|r|.| +0#0000000&@22
+@4>v+0#00e0003&|o|i|d| +0#0000000&|v|a|r|(|)| @1|{| |v+0#af5f00255&|a|r| +0#0000000&|v+0#af5f00255&|a|r| +0#0000000&|=| |n+0#af5f00255&|e|w| +0#0000000&|O|b|j|e|c|t|(|)|;| @2|t|r|a|n|s|i|t|i|v|e|(|)|;| |}| @15
+@4|/+0#0000e05&@1| |Y|i|e|l|d|S|t|a|t|e|m|e|n|t|:| |y|i|e|l|d| |(|s|e@1| |j|a|v|a|_|s|w|i|t|c|h|.|j|a|v|a|)|.| +0#0000000&@22
+@4|v+0#00e0003&|o|i|d| +0#0000000&|y+0#af5f00255&|i|e|l|d|(+0#0000000&|)| @3|{| |O|b|j|e|c|t| |y+0#af5f00255&|i|e|l|d| +0#0000000&|=| |n+0#e000002&|u|l@1|;+0#0000000&| @5|v|a|r|(|)|;| |}| @18
+@4|/+0#0000e05&@1| |R|e|c|o|r|d|D|e|c|l|a|r|a|t|i|o|n|:| |r|e|c|o|r|d|.| +0#0000000&@41
+@4|v+0#00e0003&|o|i|d| +0#0000000&|r|e|c|o|r|d|(|)| @2|{| |O|b|j|e|c|t| |r+0#00e0003&|e|c|o|r|d| +0#0000000&|=| |n+0#e000002&|u|l@1|;+0#0000000&| @4|t+0#00e0003&|h|i|s|.+0#0000000&|y|i|e|l|d|(|)|;| |}| @11
+@4|/+0#0000e05&@1| |N|o|r|m|a|l|{|C|l|a|s@1|,|I|n|t|e|r|f|a|c|e|}|D|e|c|l|a|r|a|t|i|o|n|:| |n|o|n|-|s|e|a|l|e|d| |p|e|r|m|i|t|s| |s|e|a|l|e|d|.| +0#0000000&@5
+@4|v+0#00e0003&|o|i|d| +0#0000000&|p|e|r|m|i|t|s|(|)| @1|{| |O|b|j|e|c|t| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|=| |n+0#e000002&|u|l@1|;+0#0000000&| @3|r|e|c|o|r|d|(|)|;| |}| @15
+@4|v+0#00e0003&|o|i|d| +0#0000000&|s|e|a|l|e|d|(|)| @2|{| |O|b|j|e|c|t| |s+0#4040ff13&|e|a|l|e|d| +0#0000000&|=| |n+0#e000002&|u|l@1|;+0#0000000&| @4|p|e|r|m|i|t|s|(|)|;| |}| @14
+@4|/+0#0000e05&@1| |G|u|a|r|d|:| |w|h|e|n| |(|s|e@1| |j|a|v|a|_|s|w|i|t|c|h|.|j|a|v|a|)|.| +0#0000000&@32
+@4|v+0#00e0003&|o|i|d| +0#0000000&|w|h|e|n|(|)| |{| |O|b|j|e|c|t| |w|h|e|n| |=| |n+0#e000002&|u|l@1|;+0#0000000&| @6|s|e|a|l|e|d|(|)|;| |}| @19
+@75
+@4|s+0#4040ff13&|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|1| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|C|1|,| |I|3| |{| |}| @32
+@4|s+0#4040ff13&|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|2| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|C|1|,| |I|3| |{| |}| @32
+@4|n+0#4040ff13&|o|n|-|s|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|3| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|I|1|,| |I|2| |{| |}| @28
+@57|1|9|,|2|-|5| @7|4@1|%| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_contextual_keywords_02.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@3|n+0#4040ff13&|o|n|-|s|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|3| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|I|1|,| |I|2| |{| |}| @28
+@4|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|4| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|I|3| |{| |}| @43
+@75
+@4|a+0#4040ff13&|b|s|t|r|a|c|t| +0#0000000&|s+0#4040ff13&|e|a|l|e|d| +0#0000000&|c+0#00e0003&|l|a|s@1| +0#0000000&|C|1| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|1|,| |I|2| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|C|2|,| |C|3| |{| |}| @9
+@4|a+0#4040ff13&|b|s|t|r|a|c|t| +0#0000000&|n+0#4040ff13&|o|n|-|s|e|a|l|e|d| +0#0000000&|c+0#00e0003&|l|a|s@1| +0#0000000&|C|2| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|C|1| |{| |}| @27
+@4>f+0#4040ff13&|i|n|a|l| +0#0000000&|c+0#00e0003&|l|a|s@1| +0#0000000&|C|3| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|C|1| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|3| |{| |}| @27
+@4|c+0#00e0003&|l|a|s@1| +0#0000000&|C|4| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|C|2| |{| |}| @47
+@75
+@4|r+0#00e0003&|e|c|o|r|d| +0#0000000&|R|(|)| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|3| |{| |}| @42
+@4|e+0#00e0003&|n|u|m| +0#0000000&|E| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|3| |{| |I|N|S|T|A|N|C|E| |}| @37
+@75
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|1|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|(|v+0#af5f00255&|a|r| +0#0000000&|v+0#af5f00255&|a|r|)+0#0000000&| |-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @21
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|2|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|(|T| |v+0#af5f00255&|a|r|)+0#0000000&| |-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @23
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|3|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|(|v+0#af5f00255&|a|r|)+0#0000000&| |-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @25
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|4|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|v+0#af5f00255&|a|r| +0#0000000&|-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @27
+@4|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|<|T|>| |{| |T| |i|(|T| |i|)|;| |d+0#4040ff13&|e|f|a|u|l|t| +0#0000000&|I|<|T|>| |s|e|l|f|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|t+0#00e0003&|h|i|s|;+0#0000000&| |}| |}| @5
+|}| @73
+|~+0#4040ff13&| @73
+|~| @73
+| +0#0000000&@56|3|7|,|2|-|5| @7|B|o|t| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/java_contextual_keywords_99.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@3|s+0#4040ff13&|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|1| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|C|1|,| |I|3| |{| |}| @32
+@4|s+0#4040ff13&|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|2| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|C|1|,| |I|3| |{| |}| @32
+@4|n+0#4040ff13&|o|n|-|s|e|a|l|e|d| +0#0000000&|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|3| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|I|1|,| |I|2| |{| |}| @28
+@4|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|4| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|I|3| |{| |}| @43
+@75
+@4|a+0#4040ff13&|b|s|t|r|a|c|t| +0#0000000&|s+0#4040ff13&|e|a|l|e|d| +0#0000000&|c+0#00e0003&|l|a|s@1| +0#0000000&|C|1| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|1|,| |I|2| |p+0#00e0003&|e|r|m|i|t|s| +0#0000000&|C|2|,| |C|3| |{| |}| @9
+@4|a+0#4040ff13&|b|s|t|r|a|c|t| +0#0000000&|n+0#4040ff13&|o|n|-|s|e|a|l|e|d| +0#0000000&|c+0#00e0003&|l|a|s@1| +0#0000000&|C|2| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|C|1| |{| |}| @27
+@4|f+0#4040ff13&|i|n|a|l| +0#0000000&|c+0#00e0003&|l|a|s@1| +0#0000000&|C|3| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|C|1| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|3| |{| |}| @27
+@4|c+0#00e0003&|l|a|s@1| +0#0000000&|C|4| |e+0#00e0003&|x|t|e|n|d|s| +0#0000000&|C|2| |{| |}| @47
+@75
+@4|r+0#00e0003&|e|c|o|r|d| +0#0000000&|R|(|)| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|3| |{| |}| @42
+@4|e+0#00e0003&|n|u|m| +0#0000000&|E| |i+0#00e0003&|m|p|l|e|m|e|n|t|s| +0#0000000&|I|3| |{| |I|N|S|T|A|N|C|E| |}| @37
+@75
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|1|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|(|v+0#af5f00255&|a|r| +0#0000000&|v+0#af5f00255&|a|r|)+0#0000000&| |-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @21
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|2|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|(|T| |v+0#af5f00255&|a|r|)+0#0000000&| |-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @23
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|3|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|(|v+0#af5f00255&|a|r|)+0#0000000&| |-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @25
+@4|s+0#00e0003&|t|a|t|i|c| +0#0000000&|<|T|>| |I|<|T|>| |i|4|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|v+0#af5f00255&|a|r| +0#0000000&|-|>| |v+0#af5f00255&|a|r|;+0#0000000&| |}| @27
+@4|i+0#00e0003&|n|t|e|r|f|a|c|e| +0#0000000&|I|<|T|>| |{| |T| |i|(|T| |i|)|;| |d+0#4040ff13&|e|f|a|u|l|t| +0#0000000&|I|<|T|>| |s|e|l|f|(|)| |{| |r+0#af5f00255&|e|t|u|r|n| +0#0000000&|t+0#00e0003&|h|i|s|;+0#0000000&| |}| |}| @5
+>}| @73
+@57|4|8|,|1| @9|B|o|t| 
new file mode 100644
--- /dev/null
+++ b/runtime/syntax/testdir/input/java_contextual_keywords.java
@@ -0,0 +1,48 @@
+class ContextualKeywordsTests		// See JLS, §3.9 Keywords.
+{
+	private ContextualKeywordsTests() { throw new Error(); }
+
+	// ModuleDeclaration: module open.
+	void module()	{ Object module = null;		when(); }
+	void open()	{ Object open = null;		module(); }
+	// ModuleDirective: exports opens provides requires to uses with.
+	void exports()	{ Object exports = null;	open(); }
+	void opens()	{ Object opens = null;		exports(); }
+	void provides()	{ Object provides = null;	opens(); }
+	void requires()	{ Object requires = null;	provides(); }
+	void to()	{ Object to = null;		requires(); }
+	void uses()	{ Object uses = null;		to(); }
+	void with()	{ Object with = null;		uses(); }
+	// RequiresModifier: transitive.
+	void transitive() { Object transitive = null;	with(); }
+	// LocalVariableType | LambdaParameterType: var.
+	void var()	{ var var = new Object();	transitive(); }
+	// YieldStatement: yield (see java_switch.java).
+	void yield()	{ Object yield = null;		var(); }
+	// RecordDeclaration: record.
+	void record()	{ Object record = null;		this.yield(); }
+	// Normal{Class,Interface}Declaration: non-sealed permits sealed.
+	void permits()	{ Object permits = null;	record(); }
+	void sealed()	{ Object sealed = null;		permits(); }
+	// Guard: when (see java_switch.java).
+	void when()	{ Object when = null;		sealed(); }
+
+	sealed interface I1 permits C1, I3 { }
+	sealed interface I2 permits C1, I3 { }
+	non-sealed interface I3 extends I1, I2 { }
+	interface I4 extends I3 { }
+
+	abstract sealed class C1 implements I1, I2 permits C2, C3 { }
+	abstract non-sealed class C2 extends C1 { }
+	final class C3 extends C1 implements I3 { }
+	class C4 extends C2 { }
+
+	record R() implements I3 { }
+	enum E implements I3 { INSTANCE }
+
+	static <T> I<T> i1() { return (var var) -> var; }
+	static <T> I<T> i2() { return (T var) -> var; }
+	static <T> I<T> i3() { return (var) -> var; }
+	static <T> I<T> i4() { return var -> var; }
+	interface I<T> { T i(T i); default I<T> self() { return this; } }
+}