How to match a struct member access using clang AST matcher? - clang

I am trying to write a clang-tidy check to rename struct members.
For this, I need to match MemberExpr nodes that access a certain member, from certain structs.
In the following code, it should only match all uses of member on types S1 and S2 (8 matches in total).
typedef struct S1 { int member; int v; } S1; typedef struct MS1 {S1 s1; } MS1;
typedef struct S2 { int member; int v; } S2; typedef struct MS2 {S2 s2; } MS2;
typedef struct S3 { int member; int v; } S3; typedef struct MS3 {S2 s3; } MS3;
void f() {
S1 *s1a, s1b; MS1 *ms1a, ms1b;
S2 *s2a, s2b; MS2 *ms2a, ms2b;
S3 *s3a, s3b; MS3 *ms3a, ms3b;
(void)s1a->member; (void)s1b.member; (void)ms1a->s1.member; (void)ms1b.s1.member;
(void)s2a->member; (void)s2b.member; (void)ms2a->s2.member; (void)ms2b.s2.member;
(void)s3a->member; (void)s3b.member; (void)ms3a->s3.member; (void)ms3b.s3.member;
(void)s1a->v; (void)s1b.v; (void)ms1a->s1.v; (void)ms1b.s1.v;
(void)s2a->v; (void)s2b.v; (void)ms2a->s2.v; (void)ms2b.s2.v;
(void)s3a->v; (void)s3b.v; (void)ms3a->s3.v; (void)ms3b.s3.v;
}
The matcher memberExpr(member(hasName("member"))) is too broad and also includes type S3.
How can I limit the matcher to only return those member accesses of S1 and S2?
Thanks.

Related

C++ How to write a friend function binary operator which lives in a namespace in a separate compilation unit

I can't seem to figure out how to get the following to compile.
I am trying to write a binary operator which:
Is defined in a separate compilation unit (file)
Lives in a nested namespace
Here's some code:
// header.h
namespace ns1
{
namespace ns2
{
class myClass
{
friend bool operator==(const myClass& l, const myClass& r);
protected:
int a;
};
}
}
// header.cpp
#include "header.h"
using namespace ns1;
using namespace ns1::ns2;
bool operator==(const myClass& l, const myClass& r)
{
if(l.a != r.a) return false;
return true;
}
// main.cpp
#include "header.h"
using namespace ns1::ns2;
using namespace std;
int main()
{
myClass class1;
myClass class2;
if(class1 == class2)
{
cout << "hello world" << endl;
}
return 0;
}
This is the output from the compiler:
In function ‘bool operator==(const ns1::ns2::myClass&, const ns1::ns2::myClass&)’:
error: ‘int ns1::ns2::myClass::a’ is protected within this context
I have a suspicion that this is related to the compiler not understanding which namespace operator== should be in. I have tried explicitly declaring this, but that didn't help either.
The question is what is going on here? What is the compiler thinking?
Edit
Note: I posted this edit in response to an answer which was then deleted. The suggestion was to put the operator inside ns1::ns2, however this did not work. See the below output.
New compiler output:
error: ‘bool ns1::ns2::operator==(const ns1::ns2::myClass&, const ns1::ns2::myClass&)’ has not been declared within ‘ns1::ns2’ [-Werror]
bool ns1::ns2::operator==(const myClass& l, const myClass& r)
note: only here as a ‘friend’
friend bool operator==(const myClass& l, const myClass& r);
The problem here is when you declare a friend function inside your class, this function belongs to the innermost enclosing namespace, you have to define
bool ns1::ns2::operator==(const myClass& l, const myClass& r)
It should be defined inside namespace ns1::ns2 but not just introduced it with using directives,
// header.cpp
#include "header.h"
namespace ns1 {
namespace ns2 {
bool operator==(const myClass& l, const myClass& r)
{
if(l.a != r.a) return false;
return true;
}
}
}
Demo
or
// header.h
namespace ns1
{
namespace ns2
{
class myClass
{
...
};
bool operator==(const myClass& l, const myClass& r);
}
}
// header.cpp
bool ns1::ns2::operator==(const myClass& l, const myClass& r)
Demo
Another way is to declare your friend function as a global one,
// header.h
#pragma once
namespace ns1
{
namespace ns2
{
class myClass;
}
}
bool operator==(const ns1::ns2::myClass& l, const ns1::ns2::myClass& r);
namespace ns1
{
namespace ns2
{
class myClass
{
friend bool ::operator==(const myClass& l, const myClass& r);
protected:
int a;
};
}
}
// header.cpp
#include "header.h"
using namespace ns1::ns2;
bool operator==(const myClass& l, const myClass& r)
{
if(l.a != r.a) return false;
return true;
}
Demo

C++ set with customized comparator crashes on insert

STL set can have customized comparator. It can be implemented in several ways, such as define an operator(), use decltype on lambda, etc. I was trying to use a static method of a class and encountered a weird crash. The crash can be demonstrated by the following code
#include <string>
#include <set>
struct Foo {
static bool Compare(std::string const& s1, std::string const& s2)
{
return s1 < s2;
}
};
std::set<std::string, decltype(&Foo::Compare)> S;
int main()
{
S.insert("hello");
S.insert("world");
return 0;
}
The crash happened on the second insert. Thank you.
You have to pass pointer to compare function to set constructor, otherwise it is null and it is why the code fails.
std::set<std::string, decltype(&Foo::Compare)> S{&Foo::Compare};
By decltype(&Foo::Compare) you only specify the type of comparator, it has to be provided because its default value is null.
Change your code to below will solve the problem.
struct Foo {
bool operator()(std::string const& s1, std::string const& s2)
{
return s1 < s2;
}
};
std::set<std::string, Foo> S;
The original program crushes because the constructor of set will try to call the constructor of decltype(&Foo::Compare) which is actually not constructible.

Modifying Anonymous Structs and Unions in Clang's AST

I'm attempting to write an external Clang AST modifier that translates anonymous struct and union definitions to a different form. For example, I have:
typedef struct test_case_t {
struct {
int value;
} first[1];
int second;
int third[1];
} test_case_t;
I would like to transform this to:
struct test_case_t {
struct first{
int value;
};
struct first first[1];
int second;
int third[1];
};
typedef struct test_case_t test_case_t;
However, the transform seems to drop the struct first declaration, so this is what I get instead:
struct test_case_t {
struct{ // this should be "struct first"
int value;
};
struct first first[1];
int second;
int third[1];
};
typedef struct test_case_t test_case_t;
How do I go about modifying the struct definition in place and add the first declaration name? I have the RecordDecl of the first variable definition, but I can't seem to figure out how to transform the struct definition.
Ultimately, this is a rather interesting idiosyncrasy of Clang's AST. When representing nested sets of structs and unions, the nested declarations and definitions are split into unique FieldDecls. The struct declaration is lexically parsed first then the variable definition is parsed. This requires that you save off a reference to the struct/union declaration and match it downstream to subsequent variable definitions. This process can be rather tricky, but mimics the following. Specifically, we're looking for struct declarations that match are not isFreeStanding()
for(RecordDecl::decl_iterator iter = RD->decls_begin(), end =
RD->decls_end(); iter != end; ++iter) {
Decl *StructDecl = nullptr;
if(FieldDecl *FD = dyn_cast_or_null<FieldDecl>(*iter)) {
// create new FD
if(const ElaboratedType * NET =
dyn_cast<ElaboratedType>(SemaRef.Context.getBaseElementType(NewFD->getType())))
{
if( StructDecl ){
RecordDecl *MyDecl = dyn_cast<RecordDecl>(StructDecl);
if( MyDecl )
MyDecl->setDeclName(FD->getDeclName());
}
}
}
}else if(TagDecl *TD = dyn_cast<TagDecl>(*iter)){
if(TD->isThisDeclarationADefinition() && !TD->isFreeStanding()){
// save StructDecl
}
}
}

Implement opApply with nogc and inferred parameters

Note: I initially posted an over-simplified version of my problem. A more
accurate description follows:
I have the following struct:
struct Thing(T) {
T[3] values;
int opApply(scope int delegate(size_t, ref T) dg) {
int res = 0;
foreach(idx, ref val; values) {
res = dg(idx, val);
if (res) break;
}
return res;
}
}
Foreach can be used like so:
unittest {
Thing!(size_t[]) thing;
foreach(i, ref val ; thing) val ~= i;
}
However, it is not #nogc friendly:
#nogc unittest {
Thing!size_t thing;
foreach(i, ref val ; thing) val = i;
}
If I change the signature to
int opApply(scope int delegate(size_t, ref T) #nogc dg) { ... }
It works for the #nogc case, but fails to compile for non-#nogc cases.
The solutions I have tried are:
Cast the delegate
int opApply(scope int delegate(size_t, ref T) dg) {
auto callme = cast(int delegate(size_t, ref T) #nogc) dg;
// use callme instead of dg to support nogc
This seems wrong as I am willfully casting a #nogc attribute even onto
functions that do may not support it.
Use opSlice instead of opApply:
I'm not sure how to return an (index, ref value) tuple from my range. Even if
I could, I think it would have to contain a pointer to my static array, which
could have a shorter lifetime than the returned range.
Use a templated opApply:
All attempts to work with this have failed to automatically determine the
foreach argument types. For example, I needed to specify:
foreach(size_t idx, ref int value ; thing)
Which I see as a significant hindrance to the API.
Sorry for underspecifying my problem before. For total transparency,
Enumap is the "real-world" example. It
currently uses opSlice, which does not support ref access to values. My
attempts to support 'foreach with ref' while maintaining #nogc support is what
prompted this question.
Instead of overloading the opApplyoperator you can implement an input range for your type. Input ranges work automatically as the agregate argument in foreach statements:
struct Thing(K,V) {
import std.typecons;
#nogc bool empty(){return true;}
#nogc auto front(){return tuple(K.init, V.init);}
#nogc void popFront(){}
}
unittest {
Thing!(int, int) w;
foreach(val ; w) {
int[] i = [1,2,3]; // spurious allocation
}
}
#nogc unittest {
Thing!(int, int) w;
foreach(idx, val ; w) { assert(idx == val); }
}
This solves the problem caused by the allocation of the delegate used in foreach.
Note that the example is shitty (the range doesn't work at all, and usually ranges are provided via opSlice, etc) but you should get the idea.

Return int reference in vala

I have a class that has fields and I want call a method of this class and get the reference to one of the fields (not the value!!). Something like this:
class Test : Object{
uint8 x;
uint8 y;
uint8 z;
uint8 method(){
if (x == 1){
return y;
}else if (x == 2){
return z;
}
}
public static void main(string[] args){
uint8 i = method(); // get reference to y or z
i++; //this must update y or z
}
}
In C would be:
int& method()
{
if (x == 1){
return y;
}else if (x == 2){
return z;
}
}
How can I achieve this in vala?
Edit: I'm trying use pointers, I have the following
public class Test : Object {
private Struct1 stru;
struct Struct1{
uint8 _a;
public uint8 a{
get{ return _a; }
set{ _a = value; }
}
public Struct1(Struct1? copy = null){
if (copy != null){
this._a = copy.a;
}else{
this._a = 0;
}
}
public uint8* get_aa(){
return (uint8*)a;
}
}
public void get_pointer(){
uint8* dst = stru.get_aa();
}
public static int main (string[] args){
Test t = new Test();
return 0;
}
}
but when I compile I get
/home/angelluis/Documentos/vala/test.vala.c: In function ‘test_struct1_get_aa’:
/home/angelluis/Documentos/vala/test.vala.c:130:11: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
result = (guint8*) _tmp1_;
^
Compilation succeeded - 2 warning(s)
Why? I am returning an uint8* type and I attempt to store it in an uint8* pointer.
C doesn't have references (C++ does). Keep in mind that Vala compiles to C as an intermediate language.
I think that there are only two ways to do this in Vala:
Use a box type to encapsulate your uint8 values and return a reference to that box type.
Use a pointer. (Which opens the obvious pointer can of worms)
Edit: Answer to your updated example code problem:
You must be very careful with casting something to some pointer type. In this case the C compiler caught your spurious cast and emited a warning.
uint8 _a;
// This property will get and set the *value* of _a
public uint8 a{
get{ return _a; }
set{ _a = value; }
}
public uint8* get_aa(){
// Here you are casting a *value* of type uint8 to a pointer
// Which doesn't make any sense, hence the compiler warning
return (uint8*)a;
}
Note that you can't get a pointer or a reference to a property, because properties have no memory location on their own.
You can however get a pointer to the field _a in this case:
public uint8* get_aa(){
return &_a;
}
If you insist to go through the property, you have to make your property operate on the pointer as well:
uint8 _a;
public uint8* a{
get{ return &_a; }
}
Notice that in this version I have removed the get_aa () method which is now equivalent to the getter for a.
Also since in this code the property is returning a pointer there is no need for a setter, you can just dereference the pointer to set the value.

Resources