shithub: rgbds

Download patch

ref: 26ddf1ff4df483ddafb2992aa4903902911e6a94
parent: 20fd6eabbb574a31b559c2149315d3771aa66b14
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Thu Jul 29 14:55:59 EDT 2021

Prevent defining invalid local labels

Fixes #913

--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -245,18 +245,18 @@
 
 struct Symbol *sym_FindScopedSymbol(char const *symName)
 {
-	char const *dotPtr = strchr(symName, '.');
+	char const *localName = strchr(symName, '.');
 
-	if (dotPtr) {
-		if (strchr(dotPtr + 1, '.'))
+	if (localName) {
+		if (strchr(localName + 1, '.'))
 			fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
 				   symName);
 		/* If auto-scoped local label, expand the name */
-		if (dotPtr == symName) { /* Meaning, the name begins with the dot */
-			char fullname[MAXSYMLEN + 1];
+		if (localName == symName) { /* Meaning, the name begins with the dot */
+			char fullName[MAXSYMLEN + 1];
 
-			fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
-			return sym_FindExactSymbol(fullname);
+			fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
+			return sym_FindExactSymbol(fullName);
 		}
 	}
 	return sym_FindExactSymbol(symName);
@@ -533,7 +533,7 @@
 }
 
 /*
- * Add a local (.name or Parent.name) relocatable symbol
+ * Add a local (`.name` or `Parent.name`) relocatable symbol
  */
 struct Symbol *sym_AddLocalLabel(char const *symName)
 {
@@ -541,34 +541,40 @@
 		error("Local label '%s' in main scope\n", symName);
 		return NULL;
 	}
+	assert(!strchr(labelScope, '.')); /* Assuming no dots in `labelScope` */
 
-	char fullname[MAXSYMLEN + 1];
+	char fullName[MAXSYMLEN + 1];
+	char const *localName = strchr(symName, '.');
 
-	if (symName[0] == '.') {
-		/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
-		fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
-		symName = fullname; /* Use the expanded name instead */
+	assert(localName); /* There should be at least one dot in `symName` */
+	/* Check for something after the dot in `localName` */
+	if (localName[1] == '\0') {
+		fatalerror("'%s' is a nonsensical reference to an empty local label\n",
+			   symName);
+	}
+	/* Check for more than one dot in `localName` */
+	if (strchr(localName + 1, '.'))
+		fatalerror("'%s' is a nonsensical reference to a nested local label\n",
+			   symName);
+
+	if (localName == symName) {
+		/* Expand `symName` to the full `labelScope.symName` name */
+		fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
+		symName = fullName;
 	} else {
 		size_t i = 0;
 
-		/* Otherwise, check that `Parent` is in fact the current scope */
+		/* Find where `labelScope` and `symName` first differ */
 		while (labelScope[i] && symName[i] == labelScope[i])
 			i++;
-		/* Assuming no dots in `labelScope` */
-		assert(strchr(&symName[i], '.')); /* There should be at least one dot, though */
-		size_t parentLen = i + (strchr(&symName[i], '.') - symName);
 
-		/*
-		 * Check that `labelScope[i]` ended the check, guaranteeing that `symName` is at
-		 * least as long, and then that this was the entire `Parent` part of `symName`.
-		 */
+		/* Check that `symName` starts with `labelScope` and then a '.' */
 		if (labelScope[i] != '\0' || symName[i] != '.') {
+			size_t parentLen = localName - symName;
+
 			assert(parentLen <= INT_MAX);
 			error("Not currently in the scope of '%.*s'\n", (int)parentLen, symName);
 		}
-		if (strchr(&symName[parentLen + 1], '.')) /* There will at least be a terminator */
-			fatalerror("'%s' is a nonsensical reference to a nested local label\n",
-				   symName);
 	}
 
 	return addLabel(symName);
--- a/test/asm/empty-local.err
+++ b/test/asm/empty-local.err
@@ -1,0 +1,2 @@
+FATAL: empty-local.asm(5):
+    'Label.' is a nonsensical reference to an empty local label
--- /dev/null
+++ b/test/asm/multiple-dots-local.asm
@@ -1,0 +1,7 @@
+SECTION "sec", ROM0
+
+Parent:
+Parent.heir:
+	db 0
+Parent...spare:
+	db 1
--- /dev/null
+++ b/test/asm/multiple-dots-local.err
@@ -1,0 +1,2 @@
+FATAL: multiple-dots-local.asm(6):
+    'Parent...spare' is a nonsensical reference to a nested local label
--- /dev/null
+++ b/test/asm/nested-local-reference.asm
@@ -1,0 +1,8 @@
+SECTION "sec", ROM0
+
+Parent:
+Parent.child:
+	db 0
+.grandchild:
+	db 1
+	dw Parent.child.grandchild
--- /dev/null
+++ b/test/asm/nested-local-reference.err
@@ -1,0 +1,2 @@
+FATAL: nested-local-reference.asm(8):
+    'Parent.child.grandchild' is a nonsensical reference to a nested local symbol
--- /dev/null
+++ b/test/asm/nested-local.asm
@@ -1,0 +1,7 @@
+SECTION "sec", ROM0
+
+Parent:
+Parent.child:
+	db 0
+Parent.child.grandchild:
+	db 1
--- /dev/null
+++ b/test/asm/nested-local.err
@@ -1,0 +1,2 @@
+FATAL: nested-local.asm(6):
+    'Parent.child.grandchild' is a nonsensical reference to a nested local label
--- a/test/asm/remote-local-noexist.asm
+++ b/test/asm/remote-local-noexist.asm
@@ -4,4 +4,4 @@
 .child:
 	db 0
 NotParent:
-	dw Parent.child.fail
+	dw Parent.orphan
--- a/test/asm/remote-local-noexist.err
+++ b/test/asm/remote-local-noexist.err
@@ -1,2 +1,0 @@
-FATAL: remote-local-noexist.asm(7):
-    'Parent.child.fail' is a nonsensical reference to a nested local symbol
--- a/test/asm/sym-scope.err
+++ b/test/asm/sym-scope.err
@@ -3,7 +3,7 @@
 ERROR: sym-scope.asm(5):
     Local label 'Nice.try' in main scope
 ERROR: sym-scope.asm(17):
-    Not currently in the scope of 'Parentheses.check'
+    Not currently in the scope of 'Parentheses'
 ERROR: sym-scope.asm(21):
-    Not currently in the scope of 'Parent.check'
+    Not currently in the scope of 'Parent'
 error: Assembly aborted (4 errors)!