Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.4.0] Allow unquoted canonical repository names with query #23675

Merged
merged 1 commit into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion site/en/query/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ tokens:
hyphen, underscore, colon, dollar sign, tilde, left square brace, right square
brace). However, unquoted words may not start with a hyphen `-` or asterisk `*`
even though relative [target names](/concepts/labels#target-names) may start
with those characters.
with those characters. As a special rule meant to simplify the handling of
labels referring to external repositories, unquoted words that start with
`@@` may contain `+` characters.

Unquoted words also may not include the characters plus sign `+` or equals
sign `=`, even though those characters are permitted in target names. When
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,10 @@ private TokenKind getTokenKindForWord(String word) {
return kind == null ? TokenKind.WORD : kind;
}

private String scanWord() {
private String scanWord(char firstChar) {
int oldPos = pos - 1;
boolean startsWithDoubleAt =
firstChar == '@' && pos < input.length() && input.charAt(pos) == '@';
while (pos < input.length()) {
switch (input.charAt(pos)) {
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
Expand All @@ -192,6 +194,17 @@ private String scanWord() {
case ']':
pos++;
break;
case '+':
if (startsWithDoubleAt) {
// Allow unquoted canonical labels such as
// @@rules_jvm_external++maven+maven//:bar, but still parse @foo+@bar as two separate
// labels (here @foo refers to the @foo//:foo target).
// If @@foo+bar is intended to mean @@foo + bar, it can be written as such with spaces.
pos++;
} else {
return bufferSlice(oldPos, pos);
}
break;
default:
return bufferSlice(oldPos, pos);
}
Expand All @@ -202,13 +215,13 @@ private String scanWord() {
/**
* Scans a word or keyword.
*
* ON ENTRY: 'pos' is 1 + the index of the first char in the word.
* ON EXIT: 'pos' is 1 + the index of the last char in the word.
* <p>ON ENTRY: 'pos' is 1 + the index of the first char in the word. ON EXIT: 'pos' is 1 + the
* index of the last char in the word.
*
* @return the word or keyword token.
*/
private Token wordOrKeyword() {
String word = scanWord();
private Token wordOrKeyword(char firstChar) {
String word = scanWord(firstChar);
TokenKind kind = getTokenKindForWord(word);
return kind == TokenKind.WORD ? new Token(word) : new Token(kind);
}
Expand Down Expand Up @@ -260,7 +273,7 @@ private void tokenize() throws QuerySyntaxException {
break;
}
default: {
addToken(wordOrKeyword());
addToken(wordOrKeyword(c));
break;
} // default
} // switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,28 @@ public void testOperatorWithUnquotedExprWithSpecialCharacters() throws QuerySynt
assertThat(tokens[6].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[7].kind).isEqualTo(Lexer.TokenKind.RPAREN);
}

@Test
public void testUnquotedCanonicalLabels() throws QuerySyntaxException {
Lexer.Token[] tokens =
scan("somepath(@foo+@bar+//baz+@@foo +bar, @@rules_jvm_external++maven+maven//:bar)");
assertThat(asString(tokens))
.isEqualTo(
"somepath ( @foo + @bar + //baz + @@foo + bar , @@rules_jvm_external++maven+maven//:bar"
+ " ) EOF");
assertThat(tokens[0].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[1].kind).isEqualTo(Lexer.TokenKind.LPAREN);
assertThat(tokens[2].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[3].kind).isEqualTo(Lexer.TokenKind.PLUS);
assertThat(tokens[4].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[5].kind).isEqualTo(Lexer.TokenKind.PLUS);
assertThat(tokens[6].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[7].kind).isEqualTo(Lexer.TokenKind.PLUS);
assertThat(tokens[8].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[9].kind).isEqualTo(Lexer.TokenKind.PLUS);
assertThat(tokens[10].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[11].kind).isEqualTo(Lexer.TokenKind.COMMA);
assertThat(tokens[12].kind).isEqualTo(Lexer.TokenKind.WORD);
assertThat(tokens[13].kind).isEqualTo(Lexer.TokenKind.RPAREN);
}
}
Loading