logic: index and store Java annotations
* add annotation symbol kind * add annotation_usage edge kind * increment storage version * record java annotation decls * record java annotation member decls (as fields) * update expected output of java sample project indexing tests * add tests for recording annotations * add annotation icon, annotation colors * add new annotation node and edge types to legend
This commit is contained in:
@@ -2377,6 +2377,7 @@ void GraphController::createLegendGraph()
|
||||
incompleteFile->addComponent(std::make_shared<TokenComponentFilePath>(FilePath(), false));
|
||||
|
||||
addNode(NodeType::NODE_MACRO, L"Macro", Vec2i(x, y + dy * ++i));
|
||||
addNode(NodeType::NODE_ANNOTATION, L"Annotation", Vec2i(x, y + dy * ++i));
|
||||
|
||||
addNode(NodeType::NODE_NAMESPACE, L"namespace", Vec2i(x, y + dy * ++i));
|
||||
y -= 15;
|
||||
@@ -2488,6 +2489,13 @@ void GraphController::createLegendGraph()
|
||||
addEdge(Edge::EDGE_MACRO_USAGE, file, macro);
|
||||
}
|
||||
|
||||
{
|
||||
addText(L"annotation use", 0, Vec2i(x, y + dy * ++i));
|
||||
Node* type = addNode(NodeType::NODE_TYPE, L"Type", Vec2i(x, y + dy * ++i));
|
||||
Node* macro = addNode(NodeType::NODE_ANNOTATION, L"Annotation", Vec2i(x + dx, y + dy * i));
|
||||
addEdge(Edge::EDGE_ANNOTATION_USAGE, type, macro);
|
||||
}
|
||||
|
||||
{
|
||||
addText(L"aggregation", 0, Vec2i(x, y + dy * ++i));
|
||||
Node* typeA = addNode(NodeType::NODE_TYPE, L"Type A", Vec2i(x, y + dy * ++i));
|
||||
|
||||
@@ -8,6 +8,7 @@ std::vector<NodeType> NodeType::getOverviewBundleNodeTypesOrdered()
|
||||
return {
|
||||
NodeType(NodeType::NODE_FILE),
|
||||
NodeType(NodeType::NODE_MACRO),
|
||||
NodeType(NodeType::NODE_ANNOTATION),
|
||||
NodeType(NodeType::NODE_NAMESPACE),
|
||||
NodeType(NodeType::NODE_PACKAGE),
|
||||
NodeType(NodeType::NODE_CLASS),
|
||||
@@ -45,6 +46,8 @@ NodeType::Type NodeType::intToType(int value)
|
||||
return NodeType::NODE_CLASS;
|
||||
case NodeType::NODE_INTERFACE:
|
||||
return NodeType::NODE_INTERFACE;
|
||||
case NodeType::NODE_ANNOTATION:
|
||||
return NodeType::NODE_ANNOTATION;
|
||||
case NodeType::NODE_GLOBAL_VARIABLE:
|
||||
return NodeType::NODE_GLOBAL_VARIABLE;
|
||||
case NodeType::NODE_FIELD:
|
||||
@@ -94,6 +97,8 @@ std::string NodeType::getReadableTypeString(NodeType::Type type)
|
||||
return "class";
|
||||
case NodeType::NODE_INTERFACE:
|
||||
return "interface";
|
||||
case NodeType::NODE_ANNOTATION:
|
||||
return "annotation";
|
||||
case NodeType::NODE_GLOBAL_VARIABLE:
|
||||
return "global variable";
|
||||
case NodeType::NODE_FIELD:
|
||||
@@ -237,6 +242,7 @@ bool NodeType::isUsable() const
|
||||
NodeType::NODE_ENUM |
|
||||
NodeType::NODE_UNION |
|
||||
NodeType::NODE_INTERFACE |
|
||||
NodeType::NODE_ANNOTATION |
|
||||
NodeType::NODE_TYPEDEF;
|
||||
return ((m_type & mask) > 0);
|
||||
}
|
||||
@@ -248,6 +254,7 @@ bool NodeType::isPotentialMember() const
|
||||
NodeType::NODE_FIELD |
|
||||
NodeType::NODE_CLASS |
|
||||
NodeType::NODE_INTERFACE |
|
||||
NodeType::NODE_ANNOTATION |
|
||||
NodeType::NODE_STRUCT |
|
||||
NodeType::NODE_UNION |
|
||||
NodeType::NODE_TYPEDEF |
|
||||
@@ -265,6 +272,7 @@ bool NodeType::isCollapsible() const
|
||||
NodeType::NODE_STRUCT |
|
||||
NodeType::NODE_CLASS |
|
||||
NodeType::NODE_INTERFACE |
|
||||
NodeType::NODE_ANNOTATION |
|
||||
NodeType::NODE_ENUM |
|
||||
NodeType::NODE_UNION |
|
||||
NodeType::NODE_FILE;
|
||||
@@ -285,6 +293,7 @@ bool NodeType::hasSearchFilter() const
|
||||
NodeType::NODE_STRUCT |
|
||||
NodeType::NODE_CLASS |
|
||||
NodeType::NODE_INTERFACE |
|
||||
NodeType::NODE_ANNOTATION |
|
||||
NodeType::NODE_GLOBAL_VARIABLE |
|
||||
NodeType::NODE_FIELD |
|
||||
NodeType::NODE_FUNCTION |
|
||||
@@ -324,6 +333,8 @@ Tree<NodeType::BundleInfo> NodeType::getOverviewBundleTree() const
|
||||
return Tree<BundleInfo>(BundleInfo(L"Classes"));
|
||||
case NodeType::NODE_INTERFACE:
|
||||
return Tree<BundleInfo>(BundleInfo(L"Interfaces"));
|
||||
case NodeType::NODE_ANNOTATION:
|
||||
return Tree<BundleInfo>(BundleInfo(L"Annotations"));
|
||||
case NodeType::NODE_STRUCT:
|
||||
return Tree<BundleInfo>(BundleInfo(L"Structs"));
|
||||
case NodeType::NODE_FUNCTION:
|
||||
@@ -355,6 +366,8 @@ FilePath NodeType::getIconPath() const
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case NodeType::NODE_ANNOTATION:
|
||||
return ResourcePaths::getGuiPath().concatenate(L"graph_view/images/annotation.png");
|
||||
case NodeType::NODE_ENUM:
|
||||
return ResourcePaths::getGuiPath().concatenate(L"graph_view/images/enum.png");
|
||||
case NodeType::NODE_TYPEDEF:
|
||||
@@ -376,6 +389,7 @@ bool NodeType::hasIcon() const
|
||||
}
|
||||
|
||||
const NodeType::TypeMask mask =
|
||||
NODE_ANNOTATION |
|
||||
NodeType::NODE_ENUM |
|
||||
NodeType::NODE_TYPEDEF |
|
||||
NodeType::NODE_FILE |
|
||||
@@ -397,6 +411,7 @@ NodeType::StyleType NodeType::getNodeStyle() const
|
||||
case NodeType::NODE_CLASS:
|
||||
case NodeType::NODE_UNION:
|
||||
case NodeType::NODE_INTERFACE:
|
||||
case NodeType::NODE_ANNOTATION:
|
||||
case NodeType::NODE_ENUM:
|
||||
case NodeType::NODE_TYPEDEF:
|
||||
case NodeType::NODE_TEMPLATE_PARAMETER_TYPE:
|
||||
|
||||
+13
-13
@@ -26,20 +26,20 @@ public:
|
||||
NODE_STRUCT = 1 << 5,
|
||||
NODE_CLASS = 1 << 6,
|
||||
NODE_INTERFACE = 1 << 7,
|
||||
NODE_GLOBAL_VARIABLE = 1 << 8,
|
||||
NODE_FIELD = 1 << 9,
|
||||
NODE_FUNCTION = 1 << 10,
|
||||
NODE_METHOD = 1 << 11,
|
||||
NODE_ANNOTATION = 1 << 8,
|
||||
NODE_GLOBAL_VARIABLE = 1 << 9,
|
||||
NODE_FIELD = 1 << 10,
|
||||
NODE_FUNCTION = 1 << 11,
|
||||
NODE_METHOD = 1 << 12,
|
||||
NODE_ENUM = 1 << 13,
|
||||
NODE_ENUM_CONSTANT = 1 << 14,
|
||||
NODE_TYPEDEF = 1 << 15,
|
||||
NODE_TEMPLATE_PARAMETER_TYPE = 1 << 16,
|
||||
NODE_TYPE_PARAMETER = 1 << 17,
|
||||
|
||||
NODE_ENUM = 1 << 12,
|
||||
NODE_ENUM_CONSTANT = 1 << 13,
|
||||
NODE_TYPEDEF = 1 << 14,
|
||||
NODE_TEMPLATE_PARAMETER_TYPE = 1 << 15,
|
||||
NODE_TYPE_PARAMETER = 1 << 16,
|
||||
|
||||
NODE_FILE = 1 << 17,
|
||||
NODE_MACRO = 1 << 18,
|
||||
NODE_UNION = 1 << 19,
|
||||
NODE_FILE = 1 << 18,
|
||||
NODE_MACRO = 1 << 19,
|
||||
NODE_UNION = 1 << 20,
|
||||
|
||||
NODE_MAX_VALUE = NODE_UNION
|
||||
};
|
||||
|
||||
@@ -46,6 +46,8 @@ Edge::EdgeType Edge::intToType(int value)
|
||||
return EDGE_AGGREGATION;
|
||||
case EDGE_MACRO_USAGE:
|
||||
return EDGE_MACRO_USAGE;
|
||||
case EDGE_ANNOTATION_USAGE:
|
||||
return EDGE_ANNOTATION_USAGE;
|
||||
}
|
||||
|
||||
return EDGE_UNDEFINED;
|
||||
@@ -159,6 +161,8 @@ std::wstring Edge::getReadableTypeString(EdgeType type)
|
||||
return L"aggregation";
|
||||
case EDGE_MACRO_USAGE:
|
||||
return L"macro use";
|
||||
case EDGE_ANNOTATION_USAGE:
|
||||
return L"annotation use";
|
||||
}
|
||||
|
||||
return L"";
|
||||
|
||||
+17
-16
@@ -15,22 +15,23 @@ public:
|
||||
typedef int TypeMask;
|
||||
enum EdgeType : TypeMask
|
||||
{
|
||||
EDGE_UNDEFINED = 0x0,
|
||||
EDGE_MEMBER = 0x1,
|
||||
EDGE_TYPE_USAGE = 0x2,
|
||||
EDGE_USAGE = 0x4,
|
||||
EDGE_CALL = 0x8,
|
||||
EDGE_INHERITANCE = 0x10,
|
||||
EDGE_OVERRIDE = 0x20,
|
||||
EDGE_TEMPLATE_ARGUMENT = 0x40,
|
||||
EDGE_TYPE_ARGUMENT = 0x80,
|
||||
EDGE_TEMPLATE_DEFAULT_ARGUMENT = 0x100,
|
||||
EDGE_TEMPLATE_SPECIALIZATION = 0x200,
|
||||
EDGE_TEMPLATE_MEMBER_SPECIALIZATION = 0x400,
|
||||
EDGE_INCLUDE = 0x800,
|
||||
EDGE_IMPORT = 0x1000,
|
||||
EDGE_AGGREGATION = 0x2000,
|
||||
EDGE_MACRO_USAGE = 0x4000
|
||||
EDGE_UNDEFINED = 0,
|
||||
EDGE_MEMBER = 1 << 0,
|
||||
EDGE_TYPE_USAGE = 1 << 1,
|
||||
EDGE_USAGE = 1 << 2,
|
||||
EDGE_CALL = 1 << 3,
|
||||
EDGE_INHERITANCE = 1 << 4,
|
||||
EDGE_OVERRIDE = 1 << 5,
|
||||
EDGE_TEMPLATE_ARGUMENT = 1 << 6,
|
||||
EDGE_TYPE_ARGUMENT = 1 << 7,
|
||||
EDGE_TEMPLATE_DEFAULT_ARGUMENT = 1 << 8,
|
||||
EDGE_TEMPLATE_SPECIALIZATION = 1 << 9,
|
||||
EDGE_TEMPLATE_MEMBER_SPECIALIZATION = 1 << 10,
|
||||
EDGE_INCLUDE = 1 << 11,
|
||||
EDGE_IMPORT = 1 << 12,
|
||||
EDGE_AGGREGATION = 1 << 13,
|
||||
EDGE_MACRO_USAGE = 1 << 14,
|
||||
EDGE_ANNOTATION_USAGE = 1 << 15
|
||||
};
|
||||
|
||||
static int typeToInt(EdgeType type);
|
||||
|
||||
@@ -109,6 +109,8 @@ NodeType ParserClientImpl::symbolKindToNodeType(SymbolKind symbolKind) const
|
||||
{
|
||||
switch (symbolKind)
|
||||
{
|
||||
case SYMBOL_ANNOTATION:
|
||||
return NodeType::NODE_ANNOTATION;
|
||||
case SYMBOL_BUILTIN_TYPE:
|
||||
return NodeType::NODE_BUILTIN_TYPE;
|
||||
case SYMBOL_CLASS:
|
||||
@@ -179,6 +181,8 @@ Edge::EdgeType ParserClientImpl::referenceKindToEdgeType(ReferenceKind reference
|
||||
return Edge::EDGE_IMPORT;
|
||||
case REFERENCE_MACRO_USAGE:
|
||||
return Edge::EDGE_MACRO_USAGE;
|
||||
case REFERENCE_ANNOTATION_USAGE:
|
||||
return Edge::EDGE_ANNOTATION_USAGE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ ReferenceKind intToReferenceKind(int v)
|
||||
return REFERENCE_IMPORT;
|
||||
case REFERENCE_MACRO_USAGE:
|
||||
return REFERENCE_MACRO_USAGE;
|
||||
case REFERENCE_ANNOTATION_USAGE:
|
||||
return REFERENCE_ANNOTATION_USAGE;
|
||||
}
|
||||
return REFERENCE_UNDEFINED;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ enum ReferenceKind
|
||||
REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION = 10,
|
||||
REFERENCE_INCLUDE = 11,
|
||||
REFERENCE_IMPORT = 12,
|
||||
REFERENCE_MACRO_USAGE = 13
|
||||
REFERENCE_MACRO_USAGE = 13,
|
||||
REFERENCE_ANNOTATION_USAGE = 14
|
||||
};
|
||||
|
||||
ReferenceKind intToReferenceKind(int v);
|
||||
|
||||
@@ -4,6 +4,8 @@ SymbolKind intToSymbolKind(int v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case SYMBOL_ANNOTATION:
|
||||
return SYMBOL_ANNOTATION;
|
||||
case SYMBOL_BUILTIN_TYPE:
|
||||
return SYMBOL_BUILTIN_TYPE;
|
||||
case SYMBOL_CLASS:
|
||||
|
||||
@@ -3,26 +3,27 @@
|
||||
|
||||
enum SymbolKind
|
||||
{ // these values need to be the same as SymbolKind in Java code
|
||||
SYMBOL_BUILTIN_TYPE = 1,
|
||||
SYMBOL_CLASS = 2,
|
||||
SYMBOL_ENUM = 3,
|
||||
SYMBOL_ENUM_CONSTANT = 4,
|
||||
SYMBOL_FIELD = 5,
|
||||
SYMBOL_FUNCTION = 6,
|
||||
SYMBOL_GLOBAL_VARIABLE = 7,
|
||||
SYMBOL_INTERFACE = 8,
|
||||
SYMBOL_LOCAL_VARIABLE = 9,
|
||||
SYMBOL_MACRO = 10,
|
||||
SYMBOL_METHOD = 11,
|
||||
SYMBOL_NAMESPACE = 12,
|
||||
SYMBOL_PACKAGE = 13,
|
||||
SYMBOL_PARAMETER = 14,
|
||||
SYMBOL_STRUCT = 15,
|
||||
SYMBOL_TEMPLATE_PARAMETER = 16,
|
||||
SYMBOL_TYPEDEF = 17,
|
||||
SYMBOL_TYPE_PARAMETER = 18,
|
||||
SYMBOL_UNION = 19,
|
||||
SYMBOL_KIND_MAX = 20
|
||||
SYMBOL_ANNOTATION = 1,
|
||||
SYMBOL_BUILTIN_TYPE = 2,
|
||||
SYMBOL_CLASS = 3,
|
||||
SYMBOL_ENUM = 4,
|
||||
SYMBOL_ENUM_CONSTANT = 5,
|
||||
SYMBOL_FIELD = 6,
|
||||
SYMBOL_FUNCTION = 7,
|
||||
SYMBOL_GLOBAL_VARIABLE = 8,
|
||||
SYMBOL_INTERFACE = 9,
|
||||
SYMBOL_LOCAL_VARIABLE = 10,
|
||||
SYMBOL_MACRO = 11,
|
||||
SYMBOL_METHOD = 12,
|
||||
SYMBOL_NAMESPACE = 13,
|
||||
SYMBOL_PACKAGE = 14,
|
||||
SYMBOL_PARAMETER = 15,
|
||||
SYMBOL_STRUCT = 16,
|
||||
SYMBOL_TEMPLATE_PARAMETER = 17,
|
||||
SYMBOL_TYPEDEF = 18,
|
||||
SYMBOL_TYPE_PARAMETER = 19,
|
||||
SYMBOL_UNION = 20,
|
||||
SYMBOL_KIND_MAX = 21
|
||||
};
|
||||
|
||||
SymbolKind intToSymbolKind(int v);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
const size_t SqliteIndexStorage::s_storageVersion = 18;
|
||||
const size_t SqliteIndexStorage::s_storageVersion = 19;
|
||||
|
||||
SqliteIndexStorage::SqliteIndexStorage(const FilePath& dbFilePath)
|
||||
: SqliteStorage(dbFilePath.getCanonical())
|
||||
|
||||
@@ -53,6 +53,33 @@ public:
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_anotation_declaration_in_defaut_package()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"public @interface SampleAnnotation\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
TS_ASSERT(utility::containsElement<std::wstring>(
|
||||
client->annotations, L"public SampleAnnotation <1:1 <1:19 1:34> 3:1>"
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_anotation_member_declaration()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"public @interface SampleAnnotation\n"
|
||||
"{\n"
|
||||
" public int value() default 0;\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
TS_ASSERT(utility::containsElement<std::wstring>(
|
||||
client->fields, L"public int SampleAnnotation.value <3:13 3:17>"
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_class_declaration_in_defaut_package()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
@@ -781,6 +808,83 @@ public:
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// test finding usages of symbols
|
||||
|
||||
void test_java_parser_finds_usage_of_marker_annotation()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"public @interface SampleAnnotation\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"@SampleAnnotation\n"
|
||||
"class Foo\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
TS_ASSERT(utility::containsElement<std::wstring>(
|
||||
client->annotationUses, L"Foo -> SampleAnnotation <5:2 5:17>"
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_usage_of_single_member_annotation()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"public @interface SampleAnnotation\n"
|
||||
"{\n"
|
||||
" public int value() default 0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"@SampleAnnotation(33)\n"
|
||||
"class Foo\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
TS_ASSERT(utility::containsElement<std::wstring>(
|
||||
client->annotationUses, L"Foo -> SampleAnnotation <6:2 6:17>"
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_usage_of_normal_annotation()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"public @interface SampleAnnotation\n"
|
||||
"{\n"
|
||||
" public int a() default 0;\n"
|
||||
" public int b() default 0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"@SampleAnnotation()\n"
|
||||
"class Foo\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
TS_ASSERT(utility::containsElement<std::wstring>(
|
||||
client->annotationUses, L"Foo -> SampleAnnotation <7:2 7:17>"
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_usage_of_normal_annotation_member_in_initialization()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
"public @interface SampleAnnotation\n"
|
||||
"{\n"
|
||||
" public int a() default 0;\n"
|
||||
" public int b() default 0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"@SampleAnnotation(a = 9)\n"
|
||||
"class Foo\n"
|
||||
"{\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
TS_ASSERT(utility::containsElement<std::wstring>(
|
||||
client->usages, L"Foo -> int SampleAnnotation.a <7:19 7:19>"
|
||||
));
|
||||
}
|
||||
|
||||
void test_java_parser_finds_inheritance_using_extends_keyword()
|
||||
{
|
||||
std::shared_ptr<TestParserClient> client = parseCode(
|
||||
|
||||
@@ -194,6 +194,8 @@ private:
|
||||
return L"REFERENCE_IMPORT";
|
||||
case REFERENCE_MACRO_USAGE:
|
||||
return L"REFERENCE_MACRO_USAGE";
|
||||
case REFERENCE_ANNOTATION_USAGE:
|
||||
return L"REFERENCE_ANNOTATION_USAGE";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,9 @@ public:
|
||||
case REFERENCE_MACRO_USAGE:
|
||||
referenceContainer = ¯oUses;
|
||||
break;
|
||||
case REFERENCE_ANNOTATION_USAGE:
|
||||
referenceContainer = &annotationUses;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -145,6 +148,7 @@ public:
|
||||
std::vector<std::wstring> classes;
|
||||
std::vector<std::wstring> unions;
|
||||
std::vector<std::wstring> interfaces;
|
||||
std::vector<std::wstring> annotations;
|
||||
std::vector<std::wstring> enums;
|
||||
std::vector<std::wstring> enumConstants;
|
||||
std::vector<std::wstring> functions;
|
||||
@@ -166,6 +170,7 @@ public:
|
||||
std::vector<std::wstring> usages; // for variables
|
||||
std::vector<std::wstring> typeUses; // for types
|
||||
std::vector<std::wstring> macroUses;
|
||||
std::vector<std::wstring> annotationUses;
|
||||
std::vector<std::wstring> templateArgumentTypes;
|
||||
std::vector<std::wstring> typeArguments;
|
||||
std::vector<std::wstring> templateDefaultArgumentTypes;
|
||||
@@ -204,6 +209,8 @@ private:
|
||||
return &unions;
|
||||
case SYMBOL_INTERFACE:
|
||||
return &interfaces;
|
||||
case SYMBOL_ANNOTATION:
|
||||
return &annotations;
|
||||
case SYMBOL_ENUM:
|
||||
return &enums;
|
||||
case SYMBOL_ENUM_CONSTANT:
|
||||
|
||||
Reference in New Issue
Block a user