Subject: Fixed bug [793909] - Problem with nonexistent namespaces

From: fvogel <[email-masked]>
Date: Sat, 12 Dec 2015 16:01:18 +0000

---
 generic/tkEntry.c  | 38 ++++++++++++++++++++++++++++++++------
 tests/entry.test   |  6 ++++++
 tests/spinbox.test |  6 ++++++
 3 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/generic/tkEntry.c b/generic/tkEntry.c
index dd753f0..338652b 100644
--- a/generic/tkEntry.c
+++ b/generic/tkEntry.c
@@ -1102,6 +1102,7 @@ ConfigureEntry(
     int valuesChanged = 0;	/* lint initialization */
     double oldFrom = 0.0;	/* lint initialization */
     double oldTo = 0.0;		/* lint initialization */
+    int code;
 
     /*
      * Eliminate any existing trace on a variable monitored by the entry.
@@ -1297,9 +1298,6 @@ ConfigureEntry(
     /*
      * If the entry is tied to the value of a variable, create the variable if
      * it doesn't exist, and set the entry's value from the variable's value.
-     * No checking of the return value of EntryValueChanged is needed since it
-     * can only return TCL_ERROR if there is a trace on the textvariable and
-     * since any existing trace on it was eliminated above.
      */
 
     if (entryPtr->textVarName != NULL) {
@@ -1307,6 +1305,17 @@ ConfigureEntry(
 
 	value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
 	if (value == NULL) {
+
+            /*
+             * Since any trace on the textvariable was eliminated above,
+             * the only possible reason for EntryValueChanged to return
+             * an error is that the textvariable lives in a namespace
+             * that does not (yet) exist. Indeed, namespaces are not
+             * automatically created as needed. Don't trap this error
+             * here, better do it below when attempting to trace the
+             * variable.
+             */
+
 	    EntryValueChanged(entryPtr, NULL);
 	} else {
 	    EntrySetValue(entryPtr, value);
@@ -1325,7 +1334,13 @@ ConfigureEntry(
 	     */
 
 	    Tcl_ListObjIndex(interp, sbPtr->listObj, 0, &objPtr);
-	    EntryValueChanged(entryPtr, Tcl_GetString(objPtr));
+
+            /*
+	     * No check for error return here as well, because any possible
+	     * error will be trapped below when attempting tracing.
+	     */
+
+            EntryValueChanged(entryPtr, Tcl_GetString(objPtr));
 	} else if ((sbPtr->valueStr == NULL)
 		&& !DOUBLES_EQ(sbPtr->fromValue, sbPtr->toValue)
 		&& (!DOUBLES_EQ(sbPtr->fromValue, oldFrom)
@@ -1350,6 +1365,12 @@ ConfigureEntry(
 		}
 	    }
 	    sprintf(sbPtr->formatBuf, sbPtr->valueFormat, dvalue);
+
+            /*
+	     * No check for error return here as well, because any possible
+	     * error will be trapped below when attempting tracing.
+	     */
+
 	    EntryValueChanged(entryPtr, sbPtr->formatBuf);
 	}
     }
@@ -1361,10 +1382,13 @@ ConfigureEntry(
 
     if ((entryPtr->textVarName != NULL)
 	    && !(entryPtr->flags & ENTRY_VAR_TRACED)) {
-	Tcl_TraceVar(interp, entryPtr->textVarName,
+	code = Tcl_TraceVar(interp, entryPtr->textVarName,
 		TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
 		EntryTextVarProc, (ClientData) entryPtr);
-	entryPtr->flags |= ENTRY_VAR_TRACED;
+        if (code != TCL_OK) {
+            return TCL_ERROR;
+        }
+        entryPtr->flags |= ENTRY_VAR_TRACED;
     }
 
     EntryWorldChanged((ClientData) entryPtr);
@@ -2267,6 +2291,8 @@ EntryValueChanged(
     /*
      * An error may have happened when setting the textvariable in case there
      * is a trace on that variable and the trace proc triggered an error.
+     * Another possibility is that the textvariable is in a namespace that
+     * does not (yet) exist.
      * Signal this error.
      */
 
diff --git a/tests/entry.test b/tests/entry.test
index da8f280..da3637d 100644
--- a/tests/entry.test
+++ b/tests/entry.test
@@ -1641,6 +1641,12 @@ test entry-23.1 {error in trace proc attached to the textvariable} {
 } [list {can't set "myvar": Intentional error here!} \
     {can't set "myvar": Intentional error here!}]
 
+test entry-24.1 {textvariable lives in a non-existing namespace} {
+    destroy .e
+    catch {entry .e -textvariable thisnsdoesntexist::myvar} result1
+    set result1
+} {can't trace "thisnsdoesntexist::myvar": parent namespace doesn't exist}
+
 destroy .e
 
 # XXX Still need to write tests for EntryBlinkProc, EntryFocusProc,
diff --git a/tests/spinbox.test b/tests/spinbox.test
index 5fba390..68c6fae 100644
--- a/tests/spinbox.test
+++ b/tests/spinbox.test
@@ -1599,6 +1599,12 @@ test spinbox-24.1 {error in trace proc attached to the textvariable} {
     {can't set "myvar": Intentional error here!} \
     {can't set "myvar": Intentional error here!}]
 
+test spinbox-25.1 {textvariable lives in a non-existing namespace} {
+    destroy .s
+    catch {spinbox .s -textvariable thisnsdoesntexist::myvar} result1
+    set result1
+} {can't trace "thisnsdoesntexist::myvar": parent namespace doesn't exist}
+
 catch {unset ::e ::vVals}
 
 ##
-- 
cgit v0.12