Skip to content

Commit

Permalink
finished basic support for access restrictions on constants, variable…
Browse files Browse the repository at this point in the history
…s and functions
  • Loading branch information
Lazy-Rabbit-2001 committed Oct 13, 2024
1 parent fb50a95 commit 89c1749
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 18 deletions.
4 changes: 2 additions & 2 deletions modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@
<annotation name="@private">
<return type="void" />
<description>
Modifies the accessibility of the following property to [b]private[/b], which means that it can only be accessed from within the same script.
Modifies the accessibility of the following member(constants, variables and functions) to [b]private[/b], which means that it can only be accessed from within the same script.
[codeblock]
# In Class A:
@private static var a = 10
Expand All @@ -745,7 +745,7 @@
<annotation name="@protected">
<return type="void" />
<description>
Modifies the accessibility of the following property to [b]protected[/b], which means that it can only be accessed from within the same script or a child of the same script.
Modifies the accessibility of the following member(constants, variables and functions) to [b]protected[/b], which means that it can only be accessed from within the same script or a child of the same script.
[codeblock]
# In Class A:
@private static var a = 10
Expand Down
12 changes: 12 additions & 0 deletions modules/gdscript/editor/gdscript_docgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
} break;

case GDP::ClassNode::Member::CONSTANT: {
if (member.constant->access_restriction != GDP::Node::AccessRestriction::ACCESS_RESTRICTION_PUBLIC) {
break;
}

const GDP::ConstantNode *m_const = member.constant;
const StringName &const_name = member.constant->identifier->name;

Expand All @@ -393,6 +397,10 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
} break;

case GDP::ClassNode::Member::FUNCTION: {
if (member.function->access_restriction != GDP::Node::AccessRestriction::ACCESS_RESTRICTION_PUBLIC) {
break;
}

const GDP::FunctionNode *m_func = member.function;
const StringName &func_name = m_func->identifier->name;

Expand Down Expand Up @@ -455,6 +463,10 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_
} break;

case GDP::ClassNode::Member::VARIABLE: {
if (member.variable->access_restriction != GDP::Node::AccessRestriction::ACCESS_RESTRICTION_PUBLIC) {
break;
}

const GDP::VariableNode *m_var = member.variable;
const StringName &var_name = m_var->identifier->name;

Expand Down
36 changes: 36 additions & 0 deletions modules/gdscript/gdscript_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
}
}

// Resolving external access
if (!parser->has_class(p_class)) {
if (parser_ref.is_null()) {
// Error already pushed.
Expand All @@ -984,6 +985,41 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
return;
}

// Simple access detection
GDScriptParser::Node *member_node = nullptr;
String action_name = "access";
String member_type = "member";

switch (member.type) {
case GDScriptParser::ClassNode::Member::CONSTANT:
member_node = member.constant;
break;
case GDScriptParser::ClassNode::Member::VARIABLE:
member_node = member.variable;
break;
case GDScriptParser::ClassNode::Member::FUNCTION:
member_node = member.function;
action_name = "call";
member_type = "method";
}

if (member_node) {
switch (member_node->access_restriction) {
case GDScriptParser::Node::AccessRestriction::ACCESS_RESTRICTION_PRIVATE:
if (parser->current_class != other_parser->current_class) {
push_error(vformat(R"(Could not %s external class %s "%s" because it is private.)", action_name, member_type, member.get_name()), p_source);
return;
}
break;
case GDScriptParser::Node::AccessRestriction::ACCESS_RESTRICTION_PROTECTED:
if (!parser->current_class->extends.has(other_parser->current_class->identifier)) {
push_error(vformat(R"(Could not %s external class %s "%s" because it is protected and accessed from a class that is not derived from "%s".)", action_name, member_type, member.get_name(), other_parser->current_class->fqcn), p_source);
return;
}
break;
}
}

return;
}

Expand Down
45 changes: 39 additions & 6 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4185,20 +4185,49 @@ bool GDScriptParser::onready_annotation(AnnotationNode *p_annotation, Node *p_ta
// Access restrictions.
bool GDScriptParser::access_private_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class)
{
ERR_FAIL_COND_V_MSG(p_target->type != Node::CONSTANT && p_target->type != Node::VARIABLE && p_target->type != Node::FUNCTION, false, R"("@private" annotation can only be applied to class members, such as constants, variables, and functions.)");
AssignableNode *member = static_cast<AssignableNode *>(p_target);

AccessibleNode *member = static_cast<AccessibleNode *>(p_target);
switch (member->access_restriction) {

case Node::AccessRestriction::ACCESS_RESTRICTION_PRIVATE:
push_error(R"("@private" annotation can only be used once per member.)", p_annotation);
return false;
case Node::AccessRestriction::ACCESS_RESTRICTION_PROTECTED:
push_error(R"("@private" and "@protected" annotations cannot be used together.)", p_annotation);
return false;
}

VariableNode *variable = static_cast<VariableNode *>(member);
if (variable->exported && variable->type != Node::Type::FUNCTION) {
push_error(R"("@export" annotation cannot be applied to private or protected members.)", p_annotation);
return false;
}

return true;
member->access_restriction = Node::AccessRestriction::ACCESS_RESTRICTION_PRIVATE;
print_line(vformat(R"(Set access restriction of memebr %s to %s)", member->identifier->name, member->access_restriction));

return true;
}

bool GDScriptParser::access_protected_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class)
{
ERR_FAIL_COND_V_MSG(p_target->type != Node::CONSTANT && p_target->type != Node::VARIABLE && p_target->type != Node::FUNCTION, false, R"("@protected" annotation can only be applied to class members, such as constants, variables, and functions.)");

AssignableNode *member = static_cast<AssignableNode *>(p_target);

switch (member->access_restriction) {
case Node::AccessRestriction::ACCESS_RESTRICTION_PRIVATE:
push_error(R"("@protected" annotation can only be used once per member.)", p_annotation);
return false;
case Node::AccessRestriction::ACCESS_RESTRICTION_PROTECTED:
push_error(R"("@private" and "@protected" annotations cannot be used together.)", p_annotation);
return false;
}

VariableNode *variable = static_cast<VariableNode *>(member);
if (variable->exported && variable->type != Node::Type::FUNCTION) {
push_error(R"("@export" annotation cannot be applied to private or protected members.)", p_annotation);
return false;
}

member->access_restriction = Node::AccessRestriction::ACCESS_RESTRICTION_PROTECTED;
return true;
}

Expand Down Expand Up @@ -4326,6 +4355,10 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
return false;
}
if (variable->access_restriction != Node::AccessRestriction::ACCESS_RESTRICTION_PUBLIC) {
push_error(R"("@export" annotation cannot be applied to private or protected members.)", p_annotation);
return false;
}

variable->exported = true;

Expand Down
18 changes: 8 additions & 10 deletions modules/gdscript/gdscript_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,15 @@ class GDScriptParser {
VARIABLE,
WHILE,
};

Type type = NONE;

enum AccessRestriction {
ACCESS_RESTRICTION_PUBLIC, // By default, public.
ACCESS_RESTRICTION_PRIVATE, // Can only be accessed from within the same class.
ACCESS_RESTRICTION_PROTECTED, // Can only be accessed from within the same class or its subclasses.
};
AccessRestriction access_restriction = AccessRestriction::ACCESS_RESTRICTION_PUBLIC;

int start_line = 0, end_line = 0;
int start_column = 0, end_column = 0;
int leftmost_column = 0, rightmost_column = 0;
Expand Down Expand Up @@ -400,14 +407,6 @@ class GDScriptParser {
};

struct AssignableNode : public Node {
enum AccessRestriction {
ACCESS_RESTRICTION_PUBLIC,
ACCESS_RESTRICTION_PRIVATE,
ACCESS_RESTRICTION_PROTECTED,
}
AccessRestriction access_restriction = ACCESS_RESTRICTION_PUBLIC; // Default to public.
Vector<ClassNode*> accessible_classes;

IdentifierNode *identifier = nullptr;
ExpressionNode *initializer = nullptr;
TypeNode *datatype_specifier = nullptr;
Expand Down Expand Up @@ -573,7 +572,6 @@ class GDScriptParser {
ENUM_VALUE, // For unnamed enums.
GROUP, // For member grouping.
};

Type type = UNDEFINED;

union {
Expand Down

0 comments on commit 89c1749

Please sign in to comment.