Modification of a StringLiteral (SemaConsumer - RecursiveASTVisitor - TreeTransform)

Hello,

We are currently working on a clang plugin to check some StringLiteral (In-class initializer of field).
So far we are able to find the fields, analyze the strings and make the replacement in the source file when needed with :

PluginASTAction::CreateASTConsumer => SemaConsumer::HandleTopLevelDecl => RecursiveASTVisitor::TraverseFieldDecl => Rewriter::ReplaceText

What we would like now, is to make the change ‘online’. So when we compile our sources, apply the modification in the source file but also in the AST (to avoid a second compilation step).

We tried the StringLiteral::setString method to change the string, but it partially worked. The string gets changed but the size of the const char [] doesn’t, which result in the string being truncated.

Here is the ouput of the dump before and after the setString :

CXXConstructExpr 0xb8eec90 ‘class Toto’ ‘void (const char *, double)’

-ImplicitCastExpr 0xb8eec80 ‘const char *’
**| -StringLiteral 0xb8eec14 'const char [2]' lvalue "-"** -FloatingLiteral 0xb8eec38 ‘double’ 4.250000e+01

CXXConstructExpr 0xb8eec90 ‘class Toto’ ‘void (const char *, double)’

-ImplicitCastExpr 0xb8eec80 ‘const char *’
**| -StringLiteral 0xb8eec14 'const char [2]' lvalue "modified"** -FloatingLiteral 0xb8eec38 ‘double’ 4.250000e+01

We also tried to use the TreeTransform class by implementing the TransformStringLiteral method and invoking the TreeTransform::TransformInitializer method.
The result was the same, it seems the node doesn’t get rebuild.

Are we missing something obvious? Any suggestion of how we could replace that string in the AST?

Thanks for your help,
Tim

The AST is neither designed nor intended to support in-place modification
-- it's supposed to be essentially immutable once created. TreeTransform is
a better approach (since it creates new AST rather than updating existing
AST nodes in-place).

In your overridden TransformStringLiteral, how are you creating the new
StringLiteral? We don't seem to have any relevant functions on Sema for
this, so I think you'd need to compute the relevant type yourself and call
StringLiteral::Create.

Hello Richard,

Hello Richard,

*The AST is neither designed nor intended to support in-place

modification -- it's supposed to be essentially immutable once created.
TreeTransform is a better approach (since it creates new AST rather than
updating existing AST nodes in-place).*

This is what I have been reading here on previous messages, hence our
attempt to use TreeTransform.

*In your overridden TransformStringLiteral, how are you creating the new
StringLiteral? We don't seem to have any relevant functions on Sema for
this, so I think you'd need to compute the relevant type yourself and call
StringLiteral::Create.*

This is what we tried, here is the code snippet of our TreeTransform class
:

*class HmIdTransform : public TreeTransform<HmIdTransform>*
*{*
* Sema& m_Sema;*
* ASTContext& m_Context;*
* std::string m_NewString;*

*public:*
* HmIdTransform(Sema& semaRef, std::string newString)*
* : TreeTransform(semaRef)*
* , m_Sema(semaRef)*
* , m_Context(semaRef.getASTContext())*
* , m_NewString(newString)*
* {*

* }*

* bool AlwaysRebuild() { return true; }*

* ExprResult TransformStringLiteral(StringLiteral* strLiteral)*
* {*
* QualType StrType = m_Context.getConstantArrayType(m_Context.CharTy,
llvm::APInt(32, m_NewString.size() + 1), ArrayType::Normal, 0);*

* return m_Sema.Owned(StringLiteral::Create(m_Context, m_NewString,
StringLiteral::Ascii, false, StrType, strLiteral->getLocStart()));*
* }*
*};*

Here is an extract of our RecursiveASTVisitor::TraverseFieldDecl method :

*bool TraverseFieldDecl(FieldDecl* decl)*
*{*
* if(decl->hasInClassInitializer())*
* {*
* Expr* initializer = decl->getInClassInitializer();*

* if(initializer && initializer->children())*
* {*
* HmIdTransform initTransform(m_CI.getSema(), "ModifiedString");*

* initTransform.TransformInitializer(initializer, true);*

This returns the transformed initializer; you need to do something with the
result. (This, too, will require modifying the AST, so it's not a
particularly safe operation.)

I'm not sure if this will be sufficient, but you could try:

decl->removeInClassInitializer();
SemaRef.AddInitializerToDecl(decl, /*result of transform*/, false, false);

Hello Richard,

I tried your suggestion but I get the following error when the method AddInitializerToDecl is invoked on the FieldDecl :

test3.cpp:32:9: error: illegal initializer (only variables can be initialized)

My code does the following :

class MyClass
{
MyOtherClass myVar {“StringToModify”};
}

In the mean time, I found a solution without a TreeTransform. My initial problem (wrong const char array length) is solved by setting a new type on the StringLiteral.

However, if you have any idea on how to get this working with a TreeTransform, I am willing to try.

Thanks