Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

OptionReader fails to parse a parenthesized extension after a regular option path component

Aperta
#3,672 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
76/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Tranquilla
Stack tecnologico
kotlin
Ambito
compilers

Direzione di ricerca

Inizia con wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/internal/parser/OptionReader.kt e SyntaxReader.kt, concentrandoti sul modo in cui i componenti del percorso dell’opzione regolari e tra parentesi consumano i punti. Esamina i test esistenti per le opzioni di estensione annidate, quindi aggiungi la copertura per un’estensione tra parentesi dopo un componente regolare. Il lavoro è completato quando (foo.field).string.(foo.datetime) = true viene analizzato correttamente senza richiedere spazi bianchi.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Description

Wire's ProtoParser fails to parse a parenthesized extension when it follows a regular field component in a custom option path.

This fails:

(foo.field).string.(foo.datetime) = true

Adding whitespace before the second dot makes the same option parse successfully:

(foo.field).string .(foo.datetime) = true

This occurs in real-world Protovalidate predefined rules, for example:

(buf.validate.field).string.(company.validate.datetime) = true

Protovalidate documents this form of nested extension syntax:

(buf.validate.field).float.(foo.bar.required_with_max)

https://protovalidate.com/schemas/predefined-rules/#applying-predefined-rules

Environment

  • com.squareup.wire:wire-schema-jvm:5.4.0
  • Java 21.0.9
  • Reproduced by invoking ProtoParser directly
  • Initially encountered with confluentinc/cp-schema-registry:8.0.3, which bundles Wire 5.4.0

The failure does not require Schema Registry and can be reproduced by calling Wire directly.

Minimal reproduction

import com.squareup.wire.schema.Location;
import com.squareup.wire.schema.internal.parser.ProtoParser;

public final class WireParseTest {
    public static void main(String[] args) {
        String schema =
            "syntax = \"proto3\";\n" +
            "\n" +
            "message TestEvent {\n" +
            "  string started_at = 1 [\n" +
            "    (foo.field).string.(foo.datetime) = true\n" +
            "  ];\n" +
            "}\n";

        var parsed = ProtoParser.Companion.parse(
            Location.get("test.proto"),
            schema
        );

        System.out.println(parsed);
    }
}

Compile and execute it against Wire 5.4.0:

javac \
  -proc:none \
  -cp 'wire-schema-jvm-5.4.0.jar:<required-runtime-dependencies>' \
  WireParseTest.java

java \
  -cp '.:wire-schema-jvm-5.4.0.jar:<required-runtime-dependencies>' \
  WireParseTest

It can also be reproduced using the Schema Registry image that contains Wire 5.4.0:

docker run --rm \
  --volume "$PWD:/work" \
  --workdir /work \
  --entrypoint sh \
  confluentinc/cp-schema-registry:8.0.3 \
  -c '
    javac \
      -proc:none \
      -cp "/usr/share/java/schema-registry/*" \
      WireParseTest.java &&
    java \
      -cp "/work:/usr/share/java/schema-registry/*" \
      WireParseTest
  '

Actual result

Exception in thread "main" java.lang.IllegalStateException:
Syntax error in test.proto:5:34: expected '=' in option
    at com.squareup.wire.schema.internal.parser.SyntaxReader.unexpected(SyntaxReader.kt:425)
    at com.squareup.wire.schema.internal.parser.OptionReader.readOption(OptionReader.kt:215)
    at com.squareup.wire.schema.internal.parser.OptionReader.readOptions(OptionReader.kt:38)
    at com.squareup.wire.schema.internal.parser.ProtoParser.readField(ProtoParser.kt:369)
    at com.squareup.wire.schema.internal.parser.ProtoParser.readField(ProtoParser.kt:353)
    at com.squareup.wire.schema.internal.parser.ProtoParser.readDeclaration(ProtoParser.kt:168)
    at com.squareup.wire.schema.internal.parser.ProtoParser.readMessage(ProtoParser.kt:206)
    at com.squareup.wire.schema.internal.parser.ProtoParser.readDeclaration(ProtoParser.kt:150)
    at com.squareup.wire.schema.internal.parser.ProtoParser.readProtoFile(ProtoParser.kt:71)
    at com.squareup.wire.schema.internal.parser.ProtoParser$Companion.parse(ProtoParser.kt:680)
    at WireParseTest.main(WireParseTest.java:15)

Expected result

The option should parse successfully without requiring whitespace before the second dot:

(foo.field).string.(foo.datetime) = true

Whitespace around the dot should not change the meaning or validity of the option path.

Whitespace workaround

Changing only this:

(foo.field).string.(foo.datetime) = true

to this:

(foo.field).string .(foo.datetime) = true

makes Wire parse the schema successfully.

The equivalent Protovalidate message-literal syntax also works:

(buf.validate.field).string = {
  [company.validate.datetime]: true
}

Suspected cause

OptionReader.readOption() appears to support nested parenthesized option components:

val subName = reader.readName(retainWrap = true)

if (subName.startsWith("(")) {
  subNames.add(subName)
} else {
  subNames.addAll(subName.split("."))
}

However, SyntaxReader.readWord() treats . as part of a word:

when (data[pos]) {
  in 'a'..'z', in 'A'..'Z', in '0'..'9', '_', '-', '.' -> pos++
  else -> break@loop
}

When parsing:

(foo.field).string.(foo.datetime)

the call that should read string appears to consume string. instead. The separator dot before (foo.datetime) therefore never reaches the loop in OptionReader.

OptionReader subsequently encounters ( where it expects =, producing:

expected '=' in option

The successful whitespace workaround supports this analysis: whitespace prevents readWord() from consuming the separator dot as part of string..

Relevant sources:

Wire already contains support and tests for some nested extension options, such as:

option (my_message_option_six).(More.more_string) = "foobar";

However, that form does not contain a regular path component between two parenthesized extensions. The failing structure is specifically:

(parenthesized extension).regular field.(parenthesized extension)
Lingua principale
Kotlin
Stelle
4.4k
Fork
633
Merge medio
1g 16h
PR unite (30g)
10

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di square/wire

Tutte le issue di square/wire

Issue simili

Altre issue su Kotlin

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.