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

Add Support for cs:identifier Metadata #3445

Merged

Conversation

InsertCreativityHere
Copy link
Member

@InsertCreativityHere InsertCreativityHere commented Jan 29, 2025

This PR adds support for cs:identifier in the same was as cpp:identifier.
And removes all the logic for escaping keywords/name collisions.

@@ -2612,22 +2612,6 @@ Slice::ClassDef::classDataMembers() const
return result;
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The allClassDataMembers functions were only called by unused variables.
So all dead code.

@@ -3,7 +3,6 @@
#include "CsUtil.h"
#include "../Slice/MetadataValidation.h"
#include "../Slice/Util.h"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DotNetNames files were only about escaping conflicts with 'built-in' C# methods and properties like ToString, HResult, etc. It was deleted.

@@ -22,38 +21,6 @@ using namespace std;
using namespace Slice;
using namespace IceInternal;

namespace
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's up to users to escape their identifiers if they're using a reserved keyword now.
Just like we did in slice2cpp.

@@ -85,52 +52,42 @@ Slice::CsGenerator::getNamespacePrefix(const ContainedPtr& cont)
string
Slice::CsGenerator::getNamespace(const ContainedPtr& cont)
{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixId used to convert :: into . in addition to escaping keywords and such.
Now that it's been delete, the conversion of :: to . has to be done here instead.

}
else
{
return prefix;
}
}

return scope;
return fixedScope;
}

string
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had 2 versions of getUnqualified which I combined into 1 simpler version.

The cost of doing this is that we no remove the Ice. prefix from builtin types like Ice::Object when we're building Ice itself. I can add it back if this is a problem. But removing this simplifies the code and is only useful in the very niche case when you're building Ice itself.

@@ -44,12 +44,10 @@ namespace Slice

static std::string getParamAttributes(const ParameterPtr&);

std::string writeValue(const TypePtr&, const std::string&);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead function.

void writeConstantValue(const TypePtr&, const SyntaxTreeBasePtr&, const std::string&);

// Generates "= null!" for non-nullable fields (Slice class and exception only).
void writeDataMemberInitializers(const DataMemberList&, unsigned int);
void writeDataMemberInitializers(const DataMemberList&);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unsigned int was a bit-flag we used to determine which category of names to escape.

@@ -1618,9 +1618,6 @@ Slice::JavaVisitor::writeMarshaling(Output& out, const ClassDefPtr& p)
}
out << eb;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these variables were unused.
I found them when checking allClassDataMembers.

@@ -2,7 +2,7 @@

public class Client : Test.TestHelper
{
public sealed class caseI : @abstract.caseDisp_
public sealed class caseI : cs_abstract.caseDisp_
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose cs_abstract for now until we decide on how to escape module names.

@@ -45,19 +44,20 @@ namespace
string result = "<see cref=\"";
if (!identifier.empty())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the comment-link-callback.
It should be calling mappedName but cannot.
My other PR fixes this, so don't worry about it.

@InsertCreativityHere InsertCreativityHere marked this pull request as ready for review January 29, 2025 17:03
@@ -605,69 +601,13 @@ Slice::CsVisitor::emitNonBrowsableAttribute()
<< "[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]";
}

string
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uncalled function.

Comment on lines 742 to 743
string fixedParamName = addPrefixToIdentifier("", param->mappedName()); // Strip off any leading '@'s.
_out << nl << "/// <param name=\"" << fixedParamName << "\">";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and doc-comment links to parameters cannot start with @.
The link should just be to the actual identifier, without any escaping prefixes.

string prefix = getNamespacePrefix(cont);
if (!prefix.empty())
{
if (!scope.empty())
if (!fixedScope.empty())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can fixedScope be empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoritically it could if you passed a top-level module into this function.
But after reviewing it, we never pass a module in here.

I added an assert(!dynamic_pointer_cast<Module>(p)) to the top of the function and simplified the code.

string
Slice::CsGenerator::fixId(const string& name, unsigned int baseTypes, bool mangleCasts)
Slice::CsGenerator::getMappedScoped(const ContainedPtr& p)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems confusing to have getMappedScoped and mappedScoped in the parser.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree.

The mappedScope and mappedScoped in the Parser don't make much sense since they use ::.

The scope is not :: in all languages and we don't have a mechanism to pass the language scope separator from the Slice compiler to the Parser.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good point.

I fixed mappedScoped (the parser function) to accept a separator argument.
Now C# can just pass "." and let the parser handle it. Much cleaner.

"Ice.Object", // not used anymore
"Ice.ObjectPrx?",
"Ice.Value?"};
"global::Ice.Object", // not used anymore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we decided to not use global:: for Ice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay, I removed the global:: prefixes.

@@ -298,7 +243,8 @@ string
Slice::CsGenerator::resultStructName(const string& className, const string& opName, bool marshaledResult)
{
ostringstream s;
s << className << "_" << IceInternal::toUpper(opName.substr(0, 1)) << opName.substr(1)
string fixedOpName = addPrefixToIdentifier("", opName); // Strip any leading '@' from the name.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like using empty prefix as a mean of removing @

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use my helper function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used your helper function.
But named it removeEscapePrefix instead of removeAtPrefix because the latter sounds like it's removing something at a position, which it isn't.

@@ -1874,7 +1762,7 @@ Slice::Gen::ProxyVisitor::visitOperation(const OperationPtr& p)
true);
emitObsoleteAttribute(p, _out);
_out << nl << taskResultType(p, ns);
_out << " " << p->name() << "Async" << spar << inParams
_out << " " << name << "Async" << spar << inParams
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we nee to remove @ if present in name?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can but it's optional. @ creates a syntax error only when it's in the middle of an identifier. It's ignored as prefix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach that I took was to only remove the @ when absolutely necessary and for internal code that users won't be seeing.

I think it's desirable for cs:identifier to be respected as much as possible. And if you put cs:identifier:@throw we will use @throw everywhere, even if the escape character isn't strictly necessary there.

The less magical cs:identifier is the better IMO.

cpp/src/slice2cs/Gen.cpp Show resolved Hide resolved
string
Slice::CsGenerator::fixId(const string& name, unsigned int baseTypes, bool mangleCasts)
Slice::CsGenerator::getMappedScoped(const ContainedPtr& p)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree.

The mappedScope and mappedScoped in the Parser don't make much sense since they use ::.

The scope is not :: in all languages and we don't have a mechanism to pass the language scope separator from the Slice compiler to the Parser.

stringstream result;
for (auto j = newIds.begin(); j != newIds.end(); ++j)

string name = p->mappedScoped();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this code rather confusing: it operates on a ::-separated name with the mapped C# name at the end.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this function.
Now you can tell the parser which scope-separator you want when you call mappedScoped.

Comment on lines 122 to 83
string
Slice::CsGenerator::addPrefixToIdentifier(const string& prefix, const string& ident)
{
return prefix + (ident.find('@') == 0 ? ident.substr(1) : ident);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a helper function to remove the @, like:

string removeAtPrefix(const string&)

@@ -298,7 +243,8 @@ string
Slice::CsGenerator::resultStructName(const string& className, const string& opName, bool marshaledResult)
{
ostringstream s;
s << className << "_" << IceInternal::toUpper(opName.substr(0, 1)) << opName.substr(1)
string fixedOpName = addPrefixToIdentifier("", opName); // Strip any leading '@' from the name.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use my helper function.

@InsertCreativityHere
Copy link
Member Author

I changed the signature of mappedScope to accept a const string& separator argument.

This way the languages don't need to replace all the :: with their separator of choice.
Instead they tell the parser what they want, and let it handle that.

@bernardnormier
Copy link
Member

I changed the signature of mappedScope to accept a const string& separator argument.

This way the languages don't need to replace all the :: with their separator of choice. Instead they tell the parser what they want, and let it handle that.

Should this be instead a parameter of createUnit?

The implementation queries xxx:identifier based on a fixed xxx provided to createUnit, but then requires slice2xxx to provide its scope separator every time it wants a mapped scope? This doesn't sound very consistent.

@InsertCreativityHere
Copy link
Member Author

InsertCreativityHere commented Jan 29, 2025

  1. It's not something we call often, in slice2cs we only call it from 4 places, so it's not a huge burden.
  2. It's rare, but we don't always use the same separator. There's two places in slice2cpp where we actually want _ as our separator (instead of '::') because we want a 'flattened' name.

So, I'm inclined to leave it this way until we're passing it many times and it becomes a hassle to deal with.
Which might happen as I do the other language mappings, but so far, it hasn't IMO.

{
return "global::" + type;
}
string scope = cont->mappedScope(".").substr(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So mapped scope returns ".Foo.Bar.", and that is why we need substr(1) here?

I wonder if we can have a scope prefix, and C# can pass either "" or "global::" and use it for the initial occurrence.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct, and also not a bad idea.

But I want to check how often we actually use this. Because the other option is just to remove this global scope piece all together and let callers add it by hand. Because I'm pretty sure we almost always just remove it except for niche things like type-ids.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make the intent clear, otherwise each time you see this code have to wonder why is substr(1) necessary.

Copy link
Member

@bernardnormier bernardnormier Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should either:

  • don't start with the scope separator in the string returned by mappedScope (and update mappedScope doc-comment)
    or
  • add a prefix parameter

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully agree with @bernardnormier's comment.

All I'm saying is that I lean towards not starting with the scope separator, but I want to wait and see how we're actually using this. I suspect the leading separator is more of a nuisance than a help, but I won't know until I look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants