mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Patch for Devin Steffler.
This patch updates the following for the K&R C support in GNUCSourceParser: - updated the existing K&R C tests to test CTranslationUnit#getDeclarations(IBinding) and CVisitor.clearBindings(IASTTranslationUnit) - GNUCSourceParser now uses simpleDeclaration instead of parseDeclarationOrExpression when parsing the K&R C Parameter Declarations - added a couple more tests - fixed a couple K&R C bugs related to CVisitor/CStructure
This commit is contained in:
parent
91b5d1731d
commit
f8c0954d72
5 changed files with 388 additions and 37 deletions
|
@ -10,9 +10,18 @@
|
|||
***********************************************************************/
|
||||
package org.eclipse.cdt.core.parser.tests.ast2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
|
||||
|
@ -20,14 +29,25 @@ import org.eclipse.cdt.core.dom.ast.IASTName;
|
|||
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTProblemDeclaration;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
|
||||
import org.eclipse.cdt.core.dom.ast.IBasicType;
|
||||
import org.eclipse.cdt.core.dom.ast.ICompositeType;
|
||||
import org.eclipse.cdt.core.dom.ast.IField;
|
||||
import org.eclipse.cdt.core.dom.ast.IFunction;
|
||||
import org.eclipse.cdt.core.dom.ast.IParameter;
|
||||
import org.eclipse.cdt.core.dom.ast.IPointerType;
|
||||
import org.eclipse.cdt.core.dom.ast.ITypedef;
|
||||
import org.eclipse.cdt.core.dom.ast.c.ICASTTypedefNameSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.c.ICBasicType;
|
||||
import org.eclipse.cdt.core.dom.ast.c.ICScope;
|
||||
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
|
||||
import org.eclipse.cdt.core.parser.ParserLanguage;
|
||||
import org.eclipse.cdt.internal.core.dom.parser.c.CKnRParameter;
|
||||
import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor;
|
||||
|
||||
/**
|
||||
* @author dsteffle
|
||||
|
@ -57,6 +77,19 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertEquals( x1, x2 );
|
||||
assertEquals( x2, x3 );
|
||||
assertEquals( x3, x4 );
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(x1);
|
||||
assertEquals( decls.length, 2 );
|
||||
assertEquals( decls[0], ((IASTStandardFunctionDeclarator)f1.getDeclarators()[0]).getParameters()[0].getDeclarator().getName() );
|
||||
assertEquals( decls[1], ((IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)f2.getDeclarator()).getParameterDeclarations()[0]).getDeclarators()[0].getName() );
|
||||
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)((IASTCompoundStatement)f2.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
CVisitor.clearBindings(tu);
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)((IASTCompoundStatement)f2.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
|
||||
}
|
||||
|
||||
public void testSimpleKRCTest2() throws Exception {
|
||||
|
@ -80,6 +113,18 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertNotNull( x4 );
|
||||
assertEquals( x2, x3 );
|
||||
assertEquals( x3, x4 );
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(x2);
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], ((IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)f2.getDeclarator()).getParameterDeclarations()[0]).getDeclarators()[0].getName() );
|
||||
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)((IASTCompoundStatement)f2.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
CVisitor.clearBindings(tu);
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)((IASTCompoundStatement)f2.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
|
||||
}
|
||||
|
||||
public void testSimpleKRCTest3() throws Exception {
|
||||
|
@ -140,7 +185,20 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertEquals( x_parm, x_parm2 );
|
||||
assertEquals( y_parm, y_parm2 );
|
||||
assertEquals( ret_x.resolveBinding(), x_parm );
|
||||
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(ret_x.resolveBinding());
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], x1.getName() );
|
||||
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("isroot").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)((IASTCompoundStatement)isroot_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)((IASTCompoundStatement)isroot_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("y").toCharArray()) ); //$NON-NLS-1$
|
||||
CVisitor.clearBindings(tu);
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("isroot").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)((IASTCompoundStatement)isroot_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)((IASTCompoundStatement)isroot_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("y").toCharArray()) ); //$NON-NLS-1$
|
||||
|
||||
}
|
||||
|
||||
public void testKRCWithTypes() throws Exception {
|
||||
|
@ -186,6 +244,18 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertTrue(c1_t.getType() instanceof IBasicType);
|
||||
assertEquals(((IBasicType)c1_t.getType()).getType(), IBasicType.t_char);
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(x3.resolveBinding());
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], x2 );
|
||||
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("c").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("isroot").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)((IASTCompoundStatement)isroot_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
CVisitor.clearBindings(tu);
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("c").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("isroot").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)((IASTCompoundStatement)isroot_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public void testKRCProblem1() throws Exception {
|
||||
|
@ -212,6 +282,10 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
// f_kr.getParameterNames()[0].resolveBinding();
|
||||
// ((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().resolveBinding();
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().resolveBinding());
|
||||
assertEquals( decls.length, 0 );
|
||||
|
||||
}
|
||||
|
||||
public void testKRCProblem2() throws Exception {
|
||||
|
@ -234,8 +308,17 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertEquals(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().toString(), "x"); //$NON-NLS-1$
|
||||
assertTrue(((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand2() instanceof IASTLiteralExpression);
|
||||
assertEquals(((IASTLiteralExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand2()).toString(), "0"); //$NON-NLS-1$
|
||||
|
||||
}
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().resolveBinding());
|
||||
assertEquals( decls.length, 0 );
|
||||
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("i").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
CVisitor.clearBindings(tu);
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("i").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public void testKRCProblem3() throws Exception {
|
||||
StringBuffer buffer = new StringBuffer(); //$NON-NLS-1$
|
||||
|
@ -257,6 +340,9 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertTrue(((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand2() instanceof IASTLiteralExpression);
|
||||
assertEquals(((IASTLiteralExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand2()).toString(), "0"); //$NON-NLS-1$
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().resolveBinding());
|
||||
assertEquals( decls.length, 0 );
|
||||
}
|
||||
|
||||
public void testKRCProblem4() throws Exception {
|
||||
|
@ -301,6 +387,12 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertEquals(x2_parm, x3_parm);
|
||||
assertEquals(y1_parm, y2_parm);
|
||||
assertEquals(z1_parm, z2_parm);
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().resolveBinding());
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], x2 );
|
||||
|
||||
}
|
||||
|
||||
public void testKRCProblem5() throws Exception {
|
||||
|
@ -322,6 +414,240 @@ public class AST2KnRTests extends AST2BaseTest {
|
|||
assertEquals(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().toString(), "x"); //$NON-NLS-1$
|
||||
assertTrue(((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand2() instanceof IASTLiteralExpression);
|
||||
assertEquals(((IASTLiteralExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand2()).toString(), "0"); //$NON-NLS-1$
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(((IASTIdExpression)((IASTBinaryExpression)((IASTReturnStatement)((IASTCompoundStatement)f.getBody()).getStatements()[0]).getReturnValue()).getOperand1()).getName().resolveBinding());
|
||||
assertEquals( decls.length, 0 );
|
||||
}
|
||||
|
||||
|
||||
public void testKRC_monop_cards1() throws Exception {
|
||||
StringBuffer buffer = new StringBuffer(); //$NON-NLS-1$
|
||||
buffer.append( "#ifdef __STDC__\n" ); //$NON-NLS-1$
|
||||
buffer.append( "#define __P(x) x\n" ); //$NON-NLS-1$
|
||||
buffer.append( "#else\n" ); //$NON-NLS-1$
|
||||
buffer.append( "#define __P(x) ()\n" ); //$NON-NLS-1$
|
||||
buffer.append( "#endif\n" ); //$NON-NLS-1$
|
||||
buffer.append( "struct A_struct {\n" ); //$NON-NLS-1$
|
||||
buffer.append( "int a;\n" ); //$NON-NLS-1$
|
||||
buffer.append( "long *c;\n" ); //$NON-NLS-1$
|
||||
buffer.append( "};\n" ); //$NON-NLS-1$
|
||||
buffer.append( "typedef struct A_struct A;\n" ); //$NON-NLS-1$
|
||||
buffer.append( "static void f __P((A *));\n" ); //$NON-NLS-1$
|
||||
buffer.append( "static void\n" ); //$NON-NLS-1$
|
||||
buffer.append( "f(x)\n" ); //$NON-NLS-1$
|
||||
buffer.append( "A *x; {\n" ); //$NON-NLS-1$
|
||||
buffer.append( "x->a = 0;\n" ); //$NON-NLS-1$
|
||||
buffer.append( "x->c[1]=x->c[2];\n" ); //$NON-NLS-1$
|
||||
buffer.append( "}\n" ); //$NON-NLS-1$
|
||||
IASTTranslationUnit tu = parse( buffer.toString(), ParserLanguage.C, true );
|
||||
|
||||
IASTSimpleDeclaration A_struct = (IASTSimpleDeclaration)tu.getDeclarations()[0];
|
||||
IASTSimpleDeclaration A = (IASTSimpleDeclaration)tu.getDeclarations()[1];
|
||||
IASTSimpleDeclaration f_decl = (IASTSimpleDeclaration)tu.getDeclarations()[2];
|
||||
IASTFunctionDefinition f_def = (IASTFunctionDefinition)tu.getDeclarations()[3];
|
||||
|
||||
// check A_struct
|
||||
assertTrue( A_struct.getDeclSpecifier() instanceof IASTCompositeTypeSpecifier );
|
||||
assertEquals( A_struct.getDeclarators().length, 0 );
|
||||
IASTName A_struct_name1 = ((IASTCompositeTypeSpecifier)A_struct.getDeclSpecifier()).getName();
|
||||
assertEquals( A_struct_name1.toString(), "A_struct" ); //$NON-NLS-1$
|
||||
ICompositeType A_struct_type1 = (ICompositeType)A_struct_name1.resolveBinding();
|
||||
assertEquals( A_struct_type1.getPhysicalNode(), A_struct.getDeclSpecifier() );
|
||||
List fields = A_struct_type1.getFields();
|
||||
IField a1 = (IField)fields.get(0);
|
||||
IField c1 = (IField)fields.get(1);
|
||||
assertEquals( a1.getName().toString(), "a" ); //$NON-NLS-1$
|
||||
assertEquals( c1.getName().toString(), "c" ); //$NON-NLS-1$
|
||||
IBasicType a1_t = (IBasicType)a1.getType();
|
||||
IPointerType c1_t = (IPointerType)c1.getType();
|
||||
assertEquals( a1_t.getType(), IBasicType.t_int );
|
||||
assertTrue( c1_t.getType() instanceof IBasicType );
|
||||
assertTrue( ((IBasicType)c1_t.getType()).isLong() );
|
||||
|
||||
// check A
|
||||
IASTName A_name1 = A.getDeclarators()[0].getName();
|
||||
assertEquals( A_name1.toString(), "A" ); //$NON-NLS-1$
|
||||
ITypedef A_var1 = (ITypedef)A_name1.resolveBinding();
|
||||
assertTrue( A.getDeclSpecifier() instanceof IASTElaboratedTypeSpecifier );
|
||||
IASTName A_struct_name_2 = ((IASTElaboratedTypeSpecifier)A.getDeclSpecifier()).getName();
|
||||
assertEquals( A_struct_name_2.toString(), "A_struct" ); //$NON-NLS-1$
|
||||
assertEquals( ((IASTElaboratedTypeSpecifier)A.getDeclSpecifier()).getStorageClass(), IASTDeclSpecifier.sc_typedef );
|
||||
ICompositeType A_struct_type2 = (ICompositeType)A_struct_name_2.resolveBinding();
|
||||
assertEquals( A_struct_type2, A_struct_type1 );
|
||||
|
||||
// check f_decl
|
||||
assertTrue( f_decl.getDeclarators()[0] instanceof IASTStandardFunctionDeclarator );
|
||||
IASTStandardFunctionDeclarator f_decltor1 = ((IASTStandardFunctionDeclarator)f_decl.getDeclarators()[0]);
|
||||
IASTName f_name1 = f_decltor1.getName();
|
||||
IFunction f_fun1 = (IFunction)f_name1.resolveBinding();
|
||||
assertEquals( f_name1.toString(), "f" ); //$NON-NLS-1$
|
||||
assertEquals( f_decltor1.getParameters().length, 1 );
|
||||
IASTName A_name2 = ((ICASTTypedefNameSpecifier)f_decltor1.getParameters()[0].getDeclSpecifier()).getName();
|
||||
assertEquals( A_name2.toString(), "A" ); //$NON-NLS-1$
|
||||
ITypedef A_var2 = (ITypedef)A_name2.resolveBinding();
|
||||
assertEquals( A_var1, A_var2 );
|
||||
|
||||
// check f_def
|
||||
assertTrue( f_def.getDeclarator() instanceof ICASTKnRFunctionDeclarator );
|
||||
|
||||
ICASTKnRFunctionDeclarator f_decltor2 = (ICASTKnRFunctionDeclarator)f_def.getDeclarator();
|
||||
assertEquals( f_decltor2.getName().toString(), "f" ); //$NON-NLS-1$
|
||||
IFunction f_fun2 = (IFunction)f_decltor2.getName().resolveBinding();
|
||||
assertEquals( f_fun1, f_fun2 );
|
||||
ICBasicType f_ret_t = (ICBasicType)f_fun2.getType().getReturnType();
|
||||
assertEquals( f_ret_t.getType(), IBasicType.t_void );
|
||||
IASTName x1 = f_decltor2.getParameterNames()[0];
|
||||
assertEquals( x1.toString(), "x" ); //$NON-NLS-1$
|
||||
IASTSimpleDeclaration x_parm = (IASTSimpleDeclaration)f_decltor2.getParameterDeclarations()[0];
|
||||
IASTName x2 = x_parm.getDeclarators()[0].getName();
|
||||
assertEquals( x2.toString(), "x" ); //$NON-NLS-1$
|
||||
assertEquals( x_parm.getDeclarators()[0].getPointerOperators().length, 1 );
|
||||
IASTName A3 = ((IASTNamedTypeSpecifier)x_parm.getDeclSpecifier()).getName();
|
||||
ITypedef A_var3 = (ITypedef)A3.resolveBinding();
|
||||
assertEquals( A_var2, A_var3 );
|
||||
assertEquals( A3.toString(), "A" ); //$NON-NLS-1$;
|
||||
assertEquals( x1.resolveBinding(), x2.resolveBinding() );
|
||||
|
||||
// check f_def body
|
||||
assertTrue( f_def.getBody() instanceof IASTCompoundStatement );
|
||||
IASTCompoundStatement f_def_body = (IASTCompoundStatement)f_def.getBody();
|
||||
IASTExpressionStatement stmt1 = (IASTExpressionStatement)f_def_body.getStatements()[0];
|
||||
IASTExpressionStatement stmt2 = (IASTExpressionStatement)f_def_body.getStatements()[1];
|
||||
IASTName a2 = ((IASTFieldReference)((IASTBinaryExpression)stmt1.getExpression()).getOperand1()).getFieldName();
|
||||
assertEquals( ((IASTName)a1.getPhysicalNode()).resolveBinding(), a2.resolveBinding() );
|
||||
IASTName x3 = ((IASTIdExpression)((IASTFieldReference)((IASTBinaryExpression)stmt1.getExpression()).getOperand1()).getFieldOwner()).getName();
|
||||
assertEquals( x2.resolveBinding(), x3.resolveBinding() );
|
||||
assertEquals( ((IASTBinaryExpression)stmt1.getExpression()).getOperand2().toString(), "0" ); //$NON-NLS-1$
|
||||
assertTrue( ((IASTBinaryExpression)stmt2.getExpression()).getOperand1() instanceof IASTArraySubscriptExpression );
|
||||
assertTrue( ((IASTBinaryExpression)stmt2.getExpression()).getOperand2() instanceof IASTArraySubscriptExpression );
|
||||
IASTName c2 = ((IASTFieldReference)((IASTArraySubscriptExpression)((IASTBinaryExpression)stmt2.getExpression()).getOperand1()).getArrayExpression()).getFieldName();
|
||||
IASTName x4 = ((IASTIdExpression)((IASTFieldReference)((IASTArraySubscriptExpression)((IASTBinaryExpression)stmt2.getExpression()).getOperand1()).getArrayExpression()).getFieldOwner()).getName();
|
||||
IASTName c3 = ((IASTFieldReference)((IASTArraySubscriptExpression)((IASTBinaryExpression)stmt2.getExpression()).getOperand2()).getArrayExpression()).getFieldName();
|
||||
IASTName x5 = ((IASTIdExpression)((IASTFieldReference)((IASTArraySubscriptExpression)((IASTBinaryExpression)stmt2.getExpression()).getOperand1()).getArrayExpression()).getFieldOwner()).getName();
|
||||
assertEquals( ((IASTName)c1.getPhysicalNode()).resolveBinding(), c2.resolveBinding() );
|
||||
assertEquals( ((IASTName)c1.getPhysicalNode()).resolveBinding(), c3.resolveBinding() );
|
||||
assertEquals( x3.resolveBinding(), x4.resolveBinding() );
|
||||
assertEquals( x4.resolveBinding(), x5.resolveBinding() );
|
||||
|
||||
// test CFunction.getParameters size
|
||||
List f1_parms = f_fun1.getParameters();
|
||||
assertEquals( f1_parms.size(), 1 );
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(x2.resolveBinding());
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], x2 );
|
||||
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_TAG, new String("A_struct").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("A").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNotNull( ((ICScope)((IASTCompoundStatement)f_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
CVisitor.clearBindings(tu);
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_TAG, new String("A_struct").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)tu.getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("f").toCharArray()) ); //$NON-NLS-1$
|
||||
assertNull( ((ICScope)((IASTCompoundStatement)f_def.getBody()).getScope()).getBinding(ICScope.NAMESPACE_TYPE_OTHER, new String("x").toCharArray()) ); //$NON-NLS-1$
|
||||
|
||||
}
|
||||
|
||||
public void testKRC_monop_cards2() throws Exception {
|
||||
StringBuffer buffer = new StringBuffer(); //$NON-NLS-1$
|
||||
buffer.append( "int\n" ); //$NON-NLS-1$
|
||||
buffer.append( "getinp(prompt, list)\n" ); //$NON-NLS-1$
|
||||
buffer.append( " const char *prompt, *const list[];\n" ); //$NON-NLS-1$
|
||||
buffer.append( "{\n *list[1] = 'a';\n}\n" ); //$NON-NLS-1$
|
||||
IASTTranslationUnit tu = parse( buffer.toString(), ParserLanguage.C, true );
|
||||
|
||||
IASTFunctionDefinition getinp = (IASTFunctionDefinition)tu.getDeclarations()[0];
|
||||
|
||||
IASTName prompt1 = ((ICASTKnRFunctionDeclarator)getinp.getDeclarator()).getParameterNames()[0];
|
||||
IASTName list1 = ((ICASTKnRFunctionDeclarator)getinp.getDeclarator()).getParameterNames()[1];
|
||||
IASTName prompt2 = ((IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)getinp.getDeclarator()).getParameterDeclarations()[0]).getDeclarators()[0].getName();
|
||||
IASTName list2 = ((IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)getinp.getDeclarator()).getParameterDeclarations()[0]).getDeclarators()[1].getName();
|
||||
IASTName list3 = ((IASTIdExpression)((IASTArraySubscriptExpression)((IASTUnaryExpression)((IASTBinaryExpression)((IASTExpressionStatement)((IASTCompoundStatement)getinp.getBody()).getStatements()[0]).getExpression()).getOperand1()).getOperand()).getArrayExpression()).getName();
|
||||
|
||||
assertEquals( prompt1.resolveBinding(), prompt2.resolveBinding() );
|
||||
assertEquals( list1.resolveBinding(), list2.resolveBinding() );
|
||||
assertEquals( list2.resolveBinding(), list3.resolveBinding() );
|
||||
|
||||
IASTSimpleDeclaration parm_decl = (IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)getinp.getDeclarator()).getParameterDeclarations()[0];
|
||||
assertTrue( ((IASTSimpleDeclSpecifier)parm_decl.getDeclSpecifier()).isConst() );
|
||||
assertEquals( ((IASTSimpleDeclSpecifier)parm_decl.getDeclSpecifier()).getType(), IASTSimpleDeclSpecifier.t_char );
|
||||
IASTDeclarator prompt = parm_decl.getDeclarators()[0];
|
||||
IASTArrayDeclarator list = (IASTArrayDeclarator)parm_decl.getDeclarators()[1];
|
||||
assertEquals( prompt.getName().toString(), "prompt" ); //$NON-NLS-1$
|
||||
assertEquals( prompt.getPointerOperators().length, 1 );
|
||||
assertEquals( list.getName().toString(), "list" ); //$NON-NLS-1$
|
||||
assertEquals( list.getArrayModifiers().length, 1 );
|
||||
assertNull( list.getArrayModifiers()[0].getConstantExpression() );
|
||||
assertEquals( list.getPointerOperators().length, 1 );
|
||||
|
||||
// test tu.getDeclarations(IBinding)
|
||||
IASTName[] decls = tu.getDeclarations(list3.resolveBinding());
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], list2 );
|
||||
|
||||
decls = tu.getDeclarations(prompt1.resolveBinding());
|
||||
assertEquals( decls.length, 1 );
|
||||
assertEquals( decls[0], prompt2 );
|
||||
}
|
||||
|
||||
public void testKRC_getParametersOrder() throws Exception {
|
||||
StringBuffer buffer = new StringBuffer(); //$NON-NLS-1$
|
||||
buffer.append( "int f(a, b) int b,a;{}\n" ); //$NON-NLS-1$
|
||||
IASTTranslationUnit tu = parse( buffer.toString(), ParserLanguage.C, true );
|
||||
|
||||
IASTFunctionDefinition f = (IASTFunctionDefinition)tu.getDeclarations()[0];
|
||||
ICASTKnRFunctionDeclarator f_decltor = (ICASTKnRFunctionDeclarator)f.getDeclarator();
|
||||
IFunction f_fun = (IFunction)f_decltor.getName().resolveBinding();
|
||||
List f_parms = f_fun.getParameters();
|
||||
assertEquals( f_parms.size(), 2 );
|
||||
assertEquals( ((CKnRParameter)f_parms.get(0)).getName(), "a" ); //$NON-NLS-1$
|
||||
assertEquals( ((CKnRParameter)f_parms.get(1)).getName(), "b" ); //$NON-NLS-1$
|
||||
|
||||
}
|
||||
|
||||
public void testKRC_Ethereal_1() throws Exception {
|
||||
StringBuffer buffer = new StringBuffer(); //$NON-NLS-1$
|
||||
buffer.append( "struct symbol {\n" ); //$NON-NLS-1$
|
||||
buffer.append( "int lambda;\n};\n" ); //$NON-NLS-1$
|
||||
buffer.append( "struct lemon {\n" ); //$NON-NLS-1$
|
||||
buffer.append( "struct symbol **symbols;\n" ); //$NON-NLS-1$
|
||||
buffer.append( "int errorcnt;\n};\n" ); //$NON-NLS-1$
|
||||
buffer.append( "void f(lemp)\n" ); //$NON-NLS-1$
|
||||
buffer.append( "struct lemon *lemp;\n{\n" ); //$NON-NLS-1$
|
||||
buffer.append( "lemp->symbols[1]->lambda = 1;\n" ); //$NON-NLS-1$
|
||||
buffer.append( "lemp->errorcnt++;}\n" ); //$NON-NLS-1$
|
||||
IASTTranslationUnit tu = parse( buffer.toString(), ParserLanguage.C, true );
|
||||
|
||||
IASTSimpleDeclaration symbol_decl = (IASTSimpleDeclaration)tu.getDeclarations()[0];
|
||||
IASTSimpleDeclaration lemon_decl = (IASTSimpleDeclaration)tu.getDeclarations()[1];
|
||||
IASTFunctionDefinition f_def = (IASTFunctionDefinition)tu.getDeclarations()[2];
|
||||
|
||||
IASTName symbol_name1 = ((IASTCompositeTypeSpecifier)symbol_decl.getDeclSpecifier()).getName();
|
||||
IASTName lambda_name1 = ((IASTSimpleDeclaration)((IASTCompositeTypeSpecifier)symbol_decl.getDeclSpecifier()).getMembers()[0]).getDeclarators()[0].getName();
|
||||
IASTName lemon_name1 = ((IASTCompositeTypeSpecifier)lemon_decl.getDeclSpecifier()).getName();
|
||||
IASTName symbol_name2 = ((IASTElaboratedTypeSpecifier)((IASTSimpleDeclaration)((IASTCompositeTypeSpecifier)lemon_decl.getDeclSpecifier()).getMembers()[0]).getDeclSpecifier()).getName();
|
||||
IASTName symbols_name1 = ((IASTSimpleDeclaration)((IASTCompositeTypeSpecifier)lemon_decl.getDeclSpecifier()).getMembers()[0]).getDeclarators()[0].getName();
|
||||
IASTName errorcnt_name1 = ((IASTSimpleDeclaration)((IASTCompositeTypeSpecifier)lemon_decl.getDeclSpecifier()).getMembers()[1]).getDeclarators()[0].getName();
|
||||
IASTName lemp_name1 = ((ICASTKnRFunctionDeclarator)f_def.getDeclarator()).getParameterNames()[0];
|
||||
IASTName lemon_name2 = ((IASTElaboratedTypeSpecifier)((IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)f_def.getDeclarator()).getParameterDeclarations()[0]).getDeclSpecifier()).getName();
|
||||
IASTName lemp_name2 = ((IASTSimpleDeclaration)((ICASTKnRFunctionDeclarator)f_def.getDeclarator()).getParameterDeclarations()[0]).getDeclarators()[0].getName();
|
||||
IASTName lemp_name3 = ((IASTIdExpression)((IASTFieldReference)((IASTArraySubscriptExpression)((IASTFieldReference)((IASTBinaryExpression)((IASTExpressionStatement)((IASTCompoundStatement)f_def.getBody()).getStatements()[0]).getExpression()).getOperand1()).getFieldOwner()).getArrayExpression()).getFieldOwner()).getName();
|
||||
IASTName symbols_name2 = ((IASTFieldReference)((IASTArraySubscriptExpression)((IASTFieldReference)((IASTBinaryExpression)((IASTExpressionStatement)((IASTCompoundStatement)f_def.getBody()).getStatements()[0]).getExpression()).getOperand1()).getFieldOwner()).getArrayExpression()).getFieldName();
|
||||
IASTName lambda_name2 = ((IASTFieldReference)((IASTBinaryExpression)((IASTExpressionStatement)((IASTCompoundStatement)f_def.getBody()).getStatements()[0]).getExpression()).getOperand1()).getFieldName();
|
||||
|
||||
IASTName lemp_name4 = ((IASTIdExpression)((IASTFieldReference)((IASTUnaryExpression)((IASTExpressionStatement)((IASTCompoundStatement)f_def.getBody()).getStatements()[1]).getExpression()).getOperand()).getFieldOwner()).getName();
|
||||
IASTName errorcnt_name2 = ((IASTFieldReference)((IASTUnaryExpression)((IASTExpressionStatement)((IASTCompoundStatement)f_def.getBody()).getStatements()[1]).getExpression()).getOperand()).getFieldName();
|
||||
|
||||
assertEquals( symbol_name1.resolveBinding(), symbol_name2.resolveBinding() );
|
||||
assertEquals( lambda_name1.resolveBinding(), lambda_name2.resolveBinding() );
|
||||
assertEquals( lemon_name1.resolveBinding(), lemon_name2.resolveBinding() );
|
||||
assertEquals( symbols_name1.resolveBinding(), symbols_name2.resolveBinding() );
|
||||
assertEquals( errorcnt_name1.resolveBinding(), errorcnt_name2.resolveBinding() );
|
||||
assertEquals( lemp_name1.resolveBinding(), lemp_name2.resolveBinding() );
|
||||
assertEquals( lemp_name2.resolveBinding(), lemp_name3.resolveBinding() );
|
||||
assertEquals( lemp_name3.resolveBinding(), lemp_name4.resolveBinding() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
|
|||
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
|
||||
|
@ -26,6 +27,7 @@ import org.eclipse.cdt.core.dom.ast.IFunctionType;
|
|||
import org.eclipse.cdt.core.dom.ast.IScope;
|
||||
import org.eclipse.cdt.core.dom.ast.IType;
|
||||
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
|
||||
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
|
||||
|
||||
/**
|
||||
* Created on Nov 5, 2004
|
||||
|
@ -98,15 +100,19 @@ public class CFunction implements IFunction {
|
|||
}
|
||||
} else if (dtor instanceof ICASTKnRFunctionDeclarator) {
|
||||
IASTDeclaration[] params = ((ICASTKnRFunctionDeclarator)dtor).getParameterDeclarations();
|
||||
int size = params.length;
|
||||
result = new ArrayList( size );
|
||||
if( size > 0 ){
|
||||
for( int i = 0; i < size; i++ ){
|
||||
IASTDeclaration p = params[i];
|
||||
if ( p instanceof IASTSimpleDeclaration ) {
|
||||
IASTDeclarator[] decltors = ((IASTSimpleDeclaration)p).getDeclarators();
|
||||
for( int j=0; j<decltors.length; j++ ){
|
||||
result.add( decltors[j].getName().resolveBinding() );
|
||||
IASTName[] names = ((ICASTKnRFunctionDeclarator)dtor).getParameterNames();
|
||||
result = new ArrayList( names.length );
|
||||
if( names.length > 0 ){
|
||||
// ensures that the List of parameters is created in the same order as the K&R C parameter names
|
||||
for( int i=0; i<names.length; i++ ) {
|
||||
for( int j=0; j<params.length; j++) {
|
||||
if ( params[j] instanceof IASTSimpleDeclaration ) {
|
||||
IASTDeclarator[] decltors = ((IASTSimpleDeclaration)params[j]).getDeclarators();
|
||||
for (int k=0; k<decltors.length; k++) {
|
||||
if ( CharArrayUtils.equals(names[i].toCharArray(), decltors[k].getName().toCharArray()) ) {
|
||||
result.add( decltors[k].getName().resolveBinding() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ public class CStructure implements ICompositeType {
|
|||
if( node instanceof IASTSimpleDeclaration ){
|
||||
IASTDeclarator[] declarators = ((IASTSimpleDeclaration)node).getDeclarators();
|
||||
for( int j = 0; j < declarators.length; j++ ){
|
||||
IASTDeclarator declarator = declarators[i];
|
||||
IASTDeclarator declarator = declarators[j];
|
||||
IBinding binding = declarator.getName().resolveBinding();
|
||||
if( binding != null )
|
||||
fields.add( binding );
|
||||
|
|
|
@ -298,6 +298,10 @@ public class CVisitor {
|
|||
addName(declarator.getName());
|
||||
}
|
||||
} else if ( parent instanceof IASTSimpleDeclaration ) {
|
||||
// prototype parameter with no identifier isn't a declaration of the K&R C parameter
|
||||
if ( binding instanceof CKnRParameter && declarator.getName().toString() == null)
|
||||
return true;
|
||||
|
||||
if ( (declarator.getName() != null && declarator.getName().resolveBinding() == binding) ) {
|
||||
if ( declarator instanceof IASTStandardFunctionDeclarator ||
|
||||
declarator instanceof ICASTKnRFunctionDeclarator ) {
|
||||
|
@ -490,6 +494,12 @@ public class CVisitor {
|
|||
if( binding != null && binding instanceof IType ){
|
||||
return (IType) binding;
|
||||
}
|
||||
} else if( expression instanceof IASTFieldReference ){
|
||||
IBinding binding = ((IASTFieldReference)expression).getFieldName().resolveBinding();
|
||||
|
||||
if( binding instanceof IVariable ){
|
||||
return ((IVariable)binding).getType();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1129,6 +1139,18 @@ public class CVisitor {
|
|||
if( !visitDeclSpecifier( param.getDeclSpecifier(), action ) ) return false;
|
||||
if( !visitDeclarator( param.getDeclarator(), action ) ) return false;
|
||||
}
|
||||
} else if ( declarator instanceof ICASTKnRFunctionDeclarator ) {
|
||||
IASTDeclaration[] parmDeclarations = ((ICASTKnRFunctionDeclarator)declarator).getParameterDeclarations();
|
||||
for( int i = 0; i < parmDeclarations.length; i++ ){
|
||||
if ( parmDeclarations[i] instanceof IASTSimpleDeclaration ) {
|
||||
IASTSimpleDeclaration parmDeclaration = (IASTSimpleDeclaration)parmDeclarations[i];
|
||||
if( !visitDeclSpecifier( parmDeclaration.getDeclSpecifier(), action ) ) return false;
|
||||
IASTDeclarator[] decltors = parmDeclaration.getDeclarators();
|
||||
for ( int j = 0; j < decltors.length; j++ ) {
|
||||
if( !visitDeclarator( decltors[j], action ) ) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1792,41 +1792,38 @@ public class GNUCSourceParser extends AbstractGNUSourceCodeParser {
|
|||
// less)
|
||||
try {
|
||||
boolean hasValidDecltors = true;
|
||||
IASTStatement stmt = parseDeclarationOrExpressionStatement();
|
||||
if (stmt instanceof IASTDeclarationStatement) {
|
||||
IASTDeclaration declaration = ((IASTDeclarationStatement) stmt)
|
||||
.getDeclaration();
|
||||
|
||||
if (declaration instanceof IASTSimpleDeclaration) {
|
||||
IASTDeclarator[] decltors = ((IASTSimpleDeclaration) declaration)
|
||||
.getDeclarators();
|
||||
for (int k = 0; k < decltors.length; k++) {
|
||||
boolean decltorOk = false;
|
||||
for (int j = 0; j < parmNames.length; j++) {
|
||||
if (CharArrayUtils.equals(
|
||||
decltors[k].getName()
|
||||
.toCharArray(),
|
||||
parmNames[j]
|
||||
.toCharArray())) {
|
||||
decltorOk = true;
|
||||
break;
|
||||
}
|
||||
IASTDeclaration decl = simpleDeclaration();
|
||||
IASTSimpleDeclaration declaration = null;
|
||||
if (decl instanceof IASTSimpleDeclaration) {
|
||||
declaration = ((IASTSimpleDeclaration) decl);
|
||||
|
||||
IASTDeclarator[] decltors = declaration.getDeclarators();
|
||||
for (int k = 0; k < decltors.length; k++) {
|
||||
boolean decltorOk = false;
|
||||
for (int j = 0; j < parmNames.length; j++) {
|
||||
if (CharArrayUtils.equals(
|
||||
decltors[k].getName()
|
||||
.toCharArray(),
|
||||
parmNames[j]
|
||||
.toCharArray())) {
|
||||
decltorOk = true;
|
||||
break;
|
||||
}
|
||||
if (!decltorOk)
|
||||
hasValidDecltors = false;
|
||||
}
|
||||
if (!decltorOk)
|
||||
hasValidDecltors = false;
|
||||
}
|
||||
} else {
|
||||
hasValidDecltors = false;
|
||||
}
|
||||
|
||||
if (hasValidDecltors) {
|
||||
parmDeclarations[i] = ((IASTDeclarationStatement) stmt)
|
||||
.getDeclaration();
|
||||
parmDeclarations[i] = declaration;
|
||||
} else {
|
||||
parmDeclarations[i] = createKnRCProblemDeclaration(
|
||||
((ASTNode) stmt).getLength(),
|
||||
((ASTNode) stmt).getOffset());
|
||||
((ASTNode) declaration).getLength(),
|
||||
((ASTNode) declaration).getOffset());
|
||||
}
|
||||
} catch (BacktrackException b) {
|
||||
parmDeclarations[i] = createKnRCProblemDeclaration(
|
||||
|
|
Loading…
Add table
Reference in a new issue