@@ -1018,26 +1018,55 @@ class Scanner {
10181018 }
10191019
10201020 /// Scans a tag handle.
1021- String _scanTagHandle ({bool directive = false }) {
1021+ ///
1022+ /// If [isPrefix] is `true` , the handle can never be a secondary or named
1023+ /// tag handle. Such handles cannot be used in a global tag's local tag
1024+ /// prefix.
1025+ ///
1026+ /// See: https://yaml.org/spec/1.2/spec.html#id2783273
1027+ String _scanTagHandle ({bool directive = false , bool isPrefix = false }) {
1028+ final start = _scanner.state;
10221029 _scanner.expect ('!' );
10231030
1024- var buffer = StringBuffer ('!' );
1025-
1026- // libyaml only allows word characters in tags, but the spec disagrees:
1027- // http://yaml.org/spec/1.2/spec.html#ns-tag-char.
1028- var start = _scanner.position;
1029- while (_isTagChar) {
1030- _scanner.readChar ();
1031- }
1032- buffer.write (_scanner.substring (start));
1031+ final buffer = StringBuffer ('!' );
10331032
10341033 if (_scanner.peekChar () == EXCLAMATION ) {
1035- buffer.writeCharCode (_scanner.readCodePoint ());
1034+ // TODO: Do we really need this highlighted?
1035+ final char = _scanner.readCodePoint ();
1036+
1037+ if (isPrefix) {
1038+ throw YamlException (
1039+ 'A local tag used as a global tag prefix cannot have a secondary tag'
1040+ ' handle' ,
1041+ _scanner.spanFrom (start),
1042+ );
1043+ }
1044+
1045+ buffer.writeCharCode (char);
10361046 } else {
1037- // It's either the '!' tag or not really a tag handle. If it's a %TAG
1038- // directive, it's an error. If it's a tag token, it must be part of a
1039- // URI.
1040- if (directive && buffer.toString () != '!' ) _scanner.expect ('!' );
1047+ // Both %TAG and tag shorthands can have named handles.
1048+ buffer.write (_scanTagUri (flowSeparators: false ));
1049+
1050+ /// For directives, expect the "!" for a named tag. No other handle can
1051+ /// have a tag uri here. For a tag shorthand anywhere else, this needs to
1052+ /// be a separation space (tab included) or line break or nothing.
1053+ if (buffer.length > 1 && ! _isBlankOrEnd) {
1054+ _scanner.expect ('!' );
1055+
1056+ /// A tag directive doesn't allow a local tag with a named handle as a
1057+ /// local tag prefix.
1058+ ///
1059+ /// See: https://yaml.org/spec/1.2/spec.html#id2783273
1060+ if (directive && isPrefix) {
1061+ throw YamlException (
1062+ 'A local tag used as a global tag prefix cannot have a named tag'
1063+ ' handle' ,
1064+ _scanner.spanFrom (start),
1065+ );
1066+ }
1067+
1068+ buffer.write ('!' );
1069+ }
10411070 }
10421071
10431072 return buffer.toString ();
0 commit comments