Skip to content

Commit

Permalink
Allow unquoted canonical repository names with query
Browse files Browse the repository at this point in the history
Unquoted `query` words that start with `@@` are no longer broken on `+`, which allows the usage of unquoted canonical labels in expressions. In the very rare case of a shorthand form of a canonical label referring to a WORKSPACE repo (which contains no `+`), the `+` can be separated by a space to get the original behavior (e.g. `@@foo+bar` no longer means `@@foo//:foo + //bar:bar`,  but `@@foo +bar` does.

Closes bazelbuild#23600.

PiperOrigin-RevId: 673763980
Change-Id: Ie5509647b4e04e2f72b908d609781c79cf7b06d4
  • Loading branch information
fmeum authored and copybara-github committed Sep 12, 2024
1 parent 31a0b13 commit 9ec53a6
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 7 deletions.
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 @@ -167,8 +167,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',
Expand Down Expand Up @@ -245,6 +247,17 @@ private String scanWord() {
'[',
']' ->
pos++;
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);
}
}
default -> {
return bufferSlice(oldPos, pos);
}
Expand All @@ -256,13 +269,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 All @@ -284,7 +297,7 @@ private void tokenize() throws QuerySyntaxException {
/* ignore */
}
case '\'', '\"' -> addToken(quotedWord(c));
default -> addToken(wordOrKeyword());
default -> addToken(wordOrKeyword(c));
}
}

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);
}
}

0 comments on commit 9ec53a6

Please sign in to comment.