Skip to content

Commit 97e88e4

Browse files
committed
Fix crash with duplicate function parameters
1 parent 0e8a1db commit 97e88e4

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

ShapeScript/Interpreter.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,7 @@ extension Definition {
692692
case let .function(names, block):
693693
let declarationContext = context
694694
let returnType: ValueType
695-
var params = Dictionary(uniqueKeysWithValues: names.map {
696-
($0.name, ValueType.any)
697-
})
695+
var params = Dictionary(names.map { ($0.name, ValueType.any) }) { $1 }
698696
do {
699697
let context = context.push(.init(.all, [:], .any, .any))
700698
block.inferTypes(for: &params, in: context)

ShapeScript/Parser.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public struct Identifier: Equatable {
9696

9797
public enum ParserErrorType: Equatable {
9898
case unexpectedToken(Token, expected: String?)
99+
case duplicateParameter(String, at: SourceRange)
99100
case custom(String, hint: String?, at: SourceRange?)
100101
}
101102

@@ -112,6 +113,8 @@ public extension ParserError {
112113
switch type {
113114
case let .unexpectedToken(token, _):
114115
return token.range
116+
case let .duplicateParameter(_, at: range):
117+
return range
115118
case let .custom(_, _, at: range):
116119
return range
117120
}
@@ -121,6 +124,8 @@ public extension ParserError {
121124
switch type {
122125
case let .unexpectedToken(token, _):
123126
return "Unexpected \(token.type.errorDescription)"
127+
case let .duplicateParameter(name, _):
128+
return "Duplicate function parameter '\(name)'"
124129
case let .custom(message, _, _):
125130
return message
126131
}
@@ -146,7 +151,7 @@ public extension ParserError {
146151
default:
147152
return nil
148153
}
149-
case .custom:
154+
case .duplicateParameter, .custom:
150155
return nil
151156
}
152157
}
@@ -158,6 +163,8 @@ public extension ParserError {
158163
return "Did you mean '\(suggestion)'?"
159164
}
160165
return expected.map { "Expected \($0)." }
166+
case .duplicateParameter:
167+
return "Function parameter names must be unique."
161168
case let .custom(_, hint: hint, _):
162169
return hint
163170
}
@@ -277,6 +284,9 @@ private extension ArraySlice where Element == Token {
277284
guard case let .identifier(name) = expression.type else {
278285
fallthrough
279286
}
287+
if names.contains(where: { $0.name == name }) {
288+
throw ParserError(.duplicateParameter(name, at: expression.range))
289+
}
280290
names.append(Identifier(name: name, range: expression.range))
281291
}
282292
return names

ShapeScriptTests/InterpreterTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,17 @@ final class InterpreterTests: XCTestCase {
19281928
XCTAssertEqual(delegate.log, [3])
19291929
}
19301930

1931+
func testFunctionWithDuplicateArgumentName() throws {
1932+
let program = """
1933+
define foo(a a) { a + a }
1934+
print foo(1 2)
1935+
"""
1936+
XCTAssertThrowsError(try evaluate(parse(program), delegate: nil)) { error in
1937+
let error = try? XCTUnwrap(error as? ParserError)
1938+
XCTAssertEqual(error?.message, "Duplicate function parameter 'a'")
1939+
}
1940+
}
1941+
19311942
// MARK: Block invocation
19321943

19331944
func testInvokePrimitive() {

ShapeScriptTests/ParserTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,14 @@ final class ParserTests: XCTestCase {
11891189
]))
11901190
}
11911191

1192+
func testFunctionWithDuplicateArgumentName() throws {
1193+
let input = "define foo(a a) { a + a }"
1194+
XCTAssertThrowsError(try parse(input)) { error in
1195+
let error = try? XCTUnwrap(error as? ParserError)
1196+
XCTAssertEqual(error?.message, "Duplicate function parameter 'a'")
1197+
}
1198+
}
1199+
11921200
// MARK: Comments
11931201

11941202
func testBlockCommentDoesntMessUpLineCalculation() {

0 commit comments

Comments
 (0)