generator: handle invalid identifier(s) in filename

The generation was broken for proto filenames, which where
invalid identifier, since the filename is used at several places in the
generation process.

This became evident for the protobuftyperegistrations and traditional
filename header guards.

Solve this by transforming the basename into a valid identifier.
Also add a testcase on the highest point of abstraction for the
generators (qtgrpcgen).

Fixes: QTBUG-131417
Pick-to: 6.8
Change-Id: I492907881913f8b43ebf365a9e1fe38062113c3c
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
This commit is contained in:
Dennis Oberst 2024-12-05 17:08:34 +01:00
parent aa70a1099c
commit 4a3fc66b89
13 changed files with 317 additions and 3 deletions

View File

@ -176,6 +176,7 @@ bool QGrpcGenerator::GenerateClientServices(const FileDescriptor *file,
const std::string basename = utils::extractFileBasename(file->name()) +
GrpcTemplates::GrpcClientFileSuffix() + CommonTemplates::ProtoFileSuffix();
std::string identifier = utils::toValidIdentifier(basename);
const std::string realtivePath = common::generateRelativeFilePath(file, basename);
// Generate QML class
@ -195,7 +196,7 @@ bool QGrpcGenerator::GenerateClientServices(const FileDescriptor *file,
printDisclaimer(clientSourcePrinter.get());
const std::string
headerGuard = common::headerGuardFromFilename(basename + CommonTemplates::HeaderSuffix());
headerGuard = common::headerGuardFromFilename(identifier + CommonTemplates::HeaderSuffix());
QGrpcGenerator::printHeaderGuardBegin(clientHeaderPrinter.get(), headerGuard);
clientSourcePrinter->Print(

View File

@ -48,6 +48,7 @@ void QProtobufGenerator::GenerateSources(const FileDescriptor *file,
assert(generatorContext != nullptr);
std::string basename = utils::extractFileBasename(file->name());
std::string identifier = utils::toValidIdentifier(basename);
std::string relativePath = common::generateRelativeFilePath(file, basename);
std::unique_ptr<io::ZeroCopyOutputStream> sourceStream(
generatorContext->Open(relativePath + CommonTemplates::ProtoFileSuffix() + ".cpp"));
@ -92,7 +93,7 @@ void QProtobufGenerator::GenerateSources(const FileDescriptor *file,
messageDef.printClassRegistration(registrationPrinter.get());
});
registrationPrinter->Print({{"proto_name", utils::capitalizeAsciiName(basename)}},
registrationPrinter->Print({{"proto_name", utils::capitalizeAsciiName(identifier)}},
CommonTemplates::ProtobufTypeRegistrarTemplate());
CloseFileNamespaces(file, registrationPrinter.get());
@ -113,6 +114,7 @@ void QProtobufGenerator::GenerateHeader(const FileDescriptor *file,
const std::string basename = utils::extractFileBasename(file->name()) +
CommonTemplates::ProtoFileSuffix();
std::string identifier = utils::toValidIdentifier(basename);
std::string relativePath = common::generateRelativeFilePath(file, basename);
std::unique_ptr<io::ZeroCopyOutputStream>
@ -126,7 +128,7 @@ void QProtobufGenerator::GenerateHeader(const FileDescriptor *file,
std::set<std::string> systemIncludes;
const std::string
headerGuard = common::headerGuardFromFilename(basename + CommonTemplates::HeaderSuffix());
headerGuard = common::headerGuardFromFilename(identifier + CommonTemplates::HeaderSuffix());
QProtobufGenerator::printHeaderGuardBegin(headerPrinter.get(), headerGuard);
if (!Options::instance().exportMacroFilename().empty()) {
std::string exportMacroFilename = Options::instance().exportMacroFilename();

View File

@ -21,6 +21,11 @@ bool isAsciiAlpha(char c)
return (unsigned char)c <= 127 && ::isalpha(c);
}
bool isAsciiAlnum(char c)
{
return (unsigned char)c <= 127 && std::isalnum(c);
}
std::vector<std::string> split(std::string_view s, std::string_view c, bool keepEmpty)
{
assert(!c.empty());
@ -89,6 +94,25 @@ std::string extractFileBasename(std::string fileName)
return slash != std::string::npos ? fileName.substr(slash + 1) : fileName;
}
std::string toValidIdentifier(std::string_view name)
{
assert(!name.empty() && "empty names are not supported as identifier");
std::string out;
out.reserve(name.size() + 1);
if (!isAsciiAlpha(name[0]) && name[0] != '_') // omitted Unicode with XID_Start
out += '_';
for (const auto c : name) {
if (isAsciiAlnum(c) || c == '_') // omitted Unicode with XID_Continue
out += c;
else
out += '_'; // TODO: a deterministic hex - ASCII mapping algorithm would be better
}
return out;
}
std::string capitalizeAsciiName(std::string name)
{
if (name.empty() || !isAsciiAlpha(name[0]))

View File

@ -29,6 +29,7 @@ void asciiToLower(std::string &str);
void asciiToUpper(std::string &str);
std::string removeFileSuffix(std::string fileName);
std::string extractFileBasename(std::string fileName);
std::string toValidIdentifier(std::string_view name);
std::string capitalizeAsciiName(std::string name);
std::string deCapitalizeAsciiName(std::string name);
std::string &rtrim(std::string &s);

View File

@ -57,6 +57,24 @@ target_include_directories(tst_qtgrpcgen_no_options PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}/${cmake_generated_dir}/protobuf_common/qtgrpc/tests")
list(APPEND cmake_generator_tests no_options)
qt_add_protobuf(tst_qtgrpcgen_protobuf_invalid-identifier
PROTO_FILES
data/666-invalid-identifier.proto
HEADER_GUARD filename
OUTPUT_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}/${cmake_generated_dir}/invalid-identifier"
)
qt_add_grpc(tst_qtgrpcgen_invalid-identifier CLIENT
PROTO_FILES
data/666-invalid-identifier.proto
HEADER_GUARD filename
OUTPUT_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}/${cmake_generated_dir}/invalid-identifier"
)
target_link_libraries(tst_qtgrpcgen_invalid-identifier
PRIVATE tst_qtgrpcgen_protobuf_invalid-identifier)
list(APPEND cmake_generator_tests invalid-identifier)
if(TARGET Qt6::GrpcQuick)
qt_internal_extend_target(tst_qtgrpcgen
DEFINES

View File

@ -0,0 +1,9 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
syntax = "proto3";
message Empty {}
service InvalidService {
rpc Get(Empty) returns (Empty) {}
}

View File

@ -0,0 +1,94 @@
/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */
#include "666-invalid-identifier.qpb.h"
#include <QtProtobuf/qprotobufregistration.h>
#include <cmath>
class Empty_QtProtobufData : public QSharedData
{
public:
Empty_QtProtobufData()
: QSharedData()
{
}
Empty_QtProtobufData(const Empty_QtProtobufData &other)
: QSharedData(other)
{
}
};
Empty::~Empty() = default;
static constexpr struct {
QtProtobufPrivate::QProtobufPropertyOrdering::Data data;
const std::array<uint, 1> qt_protobuf_Empty_uint_data;
const char qt_protobuf_Empty_char_data[7];
} qt_protobuf_Empty_metadata {
// data
{
0, /* = version */
0, /* = num fields */
1, /* = field number offset */
1, /* = property index offset */
1, /* = field flags offset */
5, /* = message full name length */
},
// uint_data
{
// JSON name offsets:
6, /* = end-of-string-marker */
// Field numbers:
// Property indices:
// Field flags:
},
// char_data
/* metadata char_data: */
"Empty\0" /* = full message name */
/* field char_data: */
""
};
const QtProtobufPrivate::QProtobufPropertyOrdering Empty::staticPropertyOrdering = {
&qt_protobuf_Empty_metadata.data
};
void Empty::registerTypes()
{
qRegisterMetaType<Empty>();
qRegisterMetaType<EmptyRepeated>();
}
Empty::Empty()
: QProtobufMessage(&Empty::staticMetaObject, &Empty::staticPropertyOrdering),
dptr(new Empty_QtProtobufData)
{
}
Empty::Empty(const Empty &other)
= default;
Empty &Empty::operator =(const Empty &other)
{
Empty temp(other);
swap(temp);
return *this;
}
Empty::Empty(Empty &&other) noexcept
= default;
Empty::operator QVariant() const
{
return QVariant::fromValue(*this);
}
bool comparesEqual(const Empty &lhs, const Empty &rhs) noexcept
{
return operator ==(static_cast<const QProtobufMessage&>(lhs),
static_cast<const QProtobufMessage&>(rhs));
}
#include "moc_666-invalid-identifier.qpb.cpp"

View File

@ -0,0 +1,68 @@
/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */
#ifndef _666_INVALID_IDENTIFIER_QPB_H
#define _666_INVALID_IDENTIFIER_QPB_H
#include "tst_qtgrpcgen_protobuf_invalid-identifier_exports.qpb.h"
#include <QtProtobuf/qprotobuflazymessagepointer.h>
#include <QtProtobuf/qprotobufmessage.h>
#include <QtProtobuf/qprotobufobject.h>
#include <QtProtobuf/qtprotobuftypes.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qlist.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
class Empty;
using EmptyRepeated = QList<Empty>;
namespace Empty_QtProtobufNested {
enum class QtProtobufFieldEnum;
} // namespace Empty_QtProtobufNested
class Empty_QtProtobufData;
class Empty : public QProtobufMessage
{
Q_PROTOBUF_OBJECT_EXPORT(QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT)
public:
using QtProtobufFieldEnum = Empty_QtProtobufNested::QtProtobufFieldEnum;
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Empty();
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT ~Empty();
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Empty(const Empty &other);
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Empty &operator =(const Empty &other);
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Empty(Empty &&other) noexcept;
Empty &operator =(Empty &&other) noexcept
{
swap(other);
return *this;
}
void swap(Empty &other) noexcept
{
QProtobufMessage::swap(other);
dptr.swap(other.dptr);
}
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Q_IMPLICIT operator QVariant() const;
QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT static void registerTypes();
private:
friend QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT bool comparesEqual(const Empty &lhs, const Empty &rhs) noexcept;
friend bool operator==(const Empty &lhs, const Empty &rhs) noexcept
{
return comparesEqual(lhs, rhs);
}
friend bool operator!=(const Empty &lhs, const Empty &rhs) noexcept
{
return !comparesEqual(lhs, rhs);
}
QExplicitlySharedDataPointer<Empty_QtProtobufData> dptr;
};
namespace Empty_QtProtobufNested {
Q_NAMESPACE_EXPORT(QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT)
} // namespace Empty_QtProtobufNested
#endif // _666_INVALID_IDENTIFIER_QPB_H

View File

@ -0,0 +1,31 @@
/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */
#include "666-invalid-identifier_client.grpc.qpb.h"
namespace InvalidService {
using namespace Qt::StringLiterals;
Client::Client(QObject *parent)
: QGrpcClientBase("InvalidService"_L1, parent)
{
}
Client::~Client() = default;
std::unique_ptr<QGrpcCallReply> Client::Get(const Empty &arg)
{
return Get(arg, {});
}
std::unique_ptr<QGrpcCallReply> Client::Get(const Empty &arg, const QGrpcCallOptions &options)
{
auto reply = call("Get"_L1, arg, options);
if (auto *replyPtr = reply.get(); replyPtr != nullptr) {
setOperationResponseMetaType(replyPtr, QMetaType::fromType<Empty>());
}
return reply;
}
} // namespace InvalidService

View File

@ -0,0 +1,34 @@
/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */
#ifndef _666_INVALID_IDENTIFIER_CLIENT_GRPC_QPB_H
#define _666_INVALID_IDENTIFIER_CLIENT_GRPC_QPB_H
#include "666-invalid-identifier.qpb.h"
#include "tst_qtgrpcgen_invalid-identifier_exports.qpb.h"
#include <QtGrpc/qgrpccallreply.h>
#include <QtGrpc/qgrpcclientbase.h>
#include <QtGrpc/qgrpcstream.h>
#include <memory>
namespace InvalidService {
class QPB_TST_QTGRPCGEN_INVALID_IDENTIFIER_EXPORT Client : public QGrpcClientBase
{
Q_OBJECT
public:
explicit Client(QObject *parent = nullptr);
~Client() override;
[[nodiscard]]
std::unique_ptr<QGrpcCallReply> Get(const Empty &arg);
[[nodiscard]]
std::unique_ptr<QGrpcCallReply> Get(const Empty &arg, const QGrpcCallOptions &options);
};
} // namespace InvalidService
#endif // _666_INVALID_IDENTIFIER_CLIENT_GRPC_QPB_H

View File

@ -0,0 +1,8 @@
#include "666-invalid-identifier.qpb.h"
#include <QtProtobuf/qprotobufregistration.h>
static QtProtobuf::ProtoTypeRegistrar ProtoTypeRegistrarEmpty(qRegisterProtobufType<Empty>);
static bool Register_666_invalid_identifierProtobufTypes = [](){ qRegisterProtobufTypes(); return true; }();

View File

@ -0,0 +1,12 @@
/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */
#if defined(QT_SHARED) || !defined(QT_STATIC)
# if defined(QT_BUILD_TST_QTGRPCGEN_INVALID_IDENTIFIER_LIB)
# define QPB_TST_QTGRPCGEN_INVALID_IDENTIFIER_EXPORT Q_DECL_EXPORT
# else
# define QPB_TST_QTGRPCGEN_INVALID_IDENTIFIER_EXPORT Q_DECL_IMPORT
# endif
#else
# define QPB_TST_QTGRPCGEN_INVALID_IDENTIFIER_EXPORT
#endif

View File

@ -0,0 +1,12 @@
/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */
#if defined(QT_SHARED) || !defined(QT_STATIC)
# if defined(QT_BUILD_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_LIB)
# define QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Q_DECL_EXPORT
# else
# define QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT Q_DECL_IMPORT
# endif
#else
# define QPB_TST_QTGRPCGEN_PROTOBUF_INVALID_IDENTIFIER_EXPORT
#endif