Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow weird assignments #14815

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ module Crystal
assert_syntax_error "a.b() += 1"
assert_syntax_error "a.[]() += 1"

assert_syntax_error "a.[] 0 = 1"
assert_syntax_error "a.[] 0 += 1"
assert_syntax_error "a b: 0 = 1"

it_parses "def foo\n1\nend", Def.new("foo", body: 1.int32)
it_parses "def downto(n)\n1\nend", Def.new("downto", ["n".arg], 1.int32)
it_parses "def foo ; 1 ; end", Def.new("foo", body: 1.int32)
Expand Down Expand Up @@ -2821,5 +2825,29 @@ module Crystal
node = Parser.parse(source).as(Annotation).path
node_source(source, node).should eq("::Foo")
end

it "sets correct location of call dot" do
parser = Parser.new("a.b")
node = parser.parse.as(Call)
dot_location = node.dot_location.not_nil!
dot_location.line_number.should eq(1)
dot_location.column_number.should eq(2)
end

it "sets correct location of call dot in assignment" do
parser = Parser.new("a.b = c")
node = parser.parse.as(Call)
dot_location = node.dot_location.not_nil!
dot_location.line_number.should eq(1)
dot_location.column_number.should eq(2)
end

it "sets correct location of call dot in operator assignment" do
parser = Parser.new("a.b += c")
node = parser.parse.as(OpAssign).target.as(Call)
dot_location = node.dot_location.not_nil!
dot_location.line_number.should eq(1)
dot_location.column_number.should eq(2)
end
end
end
4 changes: 2 additions & 2 deletions spec/support/syntax.cr
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ class Crystal::ASTNode
end
end

def assert_syntax_error(str, message = nil, line = nil, column = nil, metafile = __FILE__, metaline = __LINE__, metaendline = __END_LINE__)
it "says syntax error on #{str.inspect}", metafile, metaline, metaendline do
def assert_syntax_error(str, message = nil, line = nil, column = nil, metafile = __FILE__, metaline = __LINE__, metaendline = __END_LINE__, *, focus : Bool = false)
it "says syntax error on #{str.inspect}", metafile, metaline, metaendline, focus: focus do
begin
parse str
fail "Expected SyntaxException to be raised", metafile, metaline
Expand Down
1 change: 1 addition & 0 deletions src/compiler/crystal/syntax/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ module Crystal
property block : Block?
property block_arg : ASTNode?
property named_args : Array(NamedArgument)?
property dot_location : Location?
property name_location : Location?
@name_size = -1
property doc : String?
Expand Down
9 changes: 8 additions & 1 deletion src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,8 @@ module Crystal
break
end
when .op_period?
dot_location = @token.location

check_void_value atomic, location

@wants_regex = false
Expand Down Expand Up @@ -761,6 +763,7 @@ module Crystal
end

atomic = Call.new(atomic, "#{name}=", arg).at(location).at_end(end_location)
atomic.dot_location = dot_location
atomic.name_location = name_location
next
when .assignment_operator?
Expand All @@ -769,6 +772,7 @@ module Crystal
next_token_skip_space_or_newline
value = parse_op_assign
call = Call.new(atomic, name).at(location)
call.dot_location = dot_location
call.name_location = name_location
atomic = OpAssign.new(call, method, value).at(location)
atomic.name_location = op_name_location
Expand All @@ -790,6 +794,7 @@ module Crystal
block = parse_block(block, stop_on_do: @stop_on_do)
atomic = Call.new atomic, name, (args || [] of ASTNode), block, block_arg, named_args
atomic.has_parentheses = has_parentheses
atomic.dot_location = dot_location
atomic.name_location = name_location
atomic.end_location = block.try(&.end_location) || call_args.try(&.end_location) || end_location
atomic.at(location)
Expand Down Expand Up @@ -6185,7 +6190,9 @@ module Crystal
when Var, InstanceVar, ClassVar, Path, Global, Underscore
true
when Call
!node.has_parentheses? && ((node.obj.nil? && node.args.empty? && node.block.nil?) || node.name == "[]")
return false if node.has_parentheses?
return true if node.obj.nil? && node.args.empty? && node.named_args.nil? && node.block.nil?
node.name == "[]" && node.dot_location.nil?
else
false
end
Expand Down
Loading