protostuff / protostuff-compiler

Protobuf parser, java code and documentation generator
Apache License 2.0
56 stars 30 forks source link

Proto.getNamespace() returns double point string which lead element in no package declared proto can not be resolved #79

Open timeaftertime opened 2 years ago

timeaftertime commented 2 years ago

When I use protostuff-parser to parse proto file, A io.protostuff.compiler.parser.ParserException raised. I read the code and found the reason:

The io.protostuff.compiler.model.Proto.getNamespace() implements as this:

@Override
public String getNamespace() {
    if (pkg == null) {
        return ".";
    }
    return "." + pkg.getValue() + ".";
}

And the default value of pkg is protected Package pkg = Package.DEFAULT;, meanwhile the implements of getValue in Package.DEFAULT return ""(empty string).

So when use ImportImpl to parse a proto file without a "package" declared, the Message and other elements in the proto file will be registered as a fullyQualifiedName starts with double point(".."):

// io.protostuff.compiler.parser.TypeRegistratorPostProcessor
private void registerUserTypes(ProtoContext context) {
    final Proto proto = context.getProto();
    List<Message> messages = new ArrayList<>();
    messages.addAll(proto.getMessages());
    // ......
    for (Message type : messages) {
        type.setProto(proto);
        String fullyQualifiedName = proto.getNamespace() + type.getName();
        type.setFullyQualifiedName(fullyQualifiedName);
        context.register(fullyQualifiedName, type);
    }
    // ......
}

As that, When proto file "A" which declares "package" as "aaaa.bbb", imports another proto file "B" which no "package" declared, it will not be able to found Message XXX declared in B, because the XXX is registered as "..XXX", and the look up for XXX is only for ".aaa.bbb.XXX", ".aaa.XXX", ".XXX":

// io.protostuff.compiler.parser.TypeResolverPostProcessor

public static Deque<String> createScopeLookupList(UserTypeContainer container) {
    String namespace = container.getNamespace();
    Deque<String> scopeLookupList = new ArrayDeque<>();
    int end = 0;
    while (end >= 0) {
        end = namespace.indexOf('.', end);
        if (end >= 0) {
            end++;
            String scope = namespace.substring(0, end);
            scopeLookupList.addFirst(scope);
        }
    }
    return scopeLookupList;
}

private UserType resolveUserType(Element source, ProtoContext context, Deque<String> scopeLookupList, String typeName) {
    UserType fieldType = null;
    // A leading '.' (for example, .foo.bar.Baz) means to start from the outermost scope
    if (typeName.startsWith(".")) {
        UserType type = (UserType) context.resolve(typeName);
        if (type != null) {
            fieldType = type;
        }
    } else {
        for (String scope : scopeLookupList) {
            String fullTypeName = scope + typeName;
            UserType type = (UserType) context.resolve(fullTypeName);
            if (type != null) {
                fieldType = type;
                break;
            }
        }
    }
    if (fieldType == null) {
        String format = "Unresolved reference: '%s'";
        throw new ParserException(source, format, typeName);
    }
    return fieldType;
}

I think it is legal to create a proto file without "package". So I wonder:

kshchepanovskyi commented 2 years ago

Sorry for late reply. It's definitely valid to have proto file with unspecified package. I'll check this case.