MCPcopy Create free account
hub / github.com/AutoHotkey/AutoHotkey / FindUpVar

Method FindUpVar

source/script.cpp:7157–7223  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

7155
7156
7157Var *Script::FindUpVar(LPCTSTR aVarName, UserFunc &aInner, ResultType *aDisplayError)
7158{
7159 // Do not search outer if inner is *explicitly* assume-global, since in that case a
7160 // global var should be returned in preference to anything defined by outer.
7161 if (aInner.mDefaultVarType == VAR_DECLARE_GLOBAL)
7162 return nullptr;
7163 auto &outer = *aInner.mOuterFunc;
7164 Var *outer_var;
7165 if ( (outer_var = outer.mStaticVars.Find(aVarName)) )
7166 return outer_var;
7167 if ( !(outer_var = outer.mVars.Find(aVarName))
7168 && !(outer.mOuterFunc && (outer_var = FindUpVar(aVarName, outer, aDisplayError))) )
7169 return nullptr;
7170 // At this point, all var refs used in declarations, assignments or &var in the outer
7171 // function should have already been parsed, while it's possible that some read-refs
7172 // have not. Ignore all variables that lack an assignment, &var or declaration.
7173 // - Avoids an issue where only read-refs ABOVE the nested function are accessible
7174 // (which could also be fixed by changing PreparseVarRefs to parse one function
7175 // at a time, parsing outer in its entirety before inner).
7176 // - Avoids inconsistency between read-refs and write-refs: when the outer var is created
7177 // by a read-ref, closures which only read would capture it, while closures which write
7178 // would create their own local.
7179 // - Avoids having behaviour depend on whether a global exists: when outer and inner both
7180 // only use read-refs (but maybe assign dynamically), whether the nested function becomes
7181 // a closure might depend on whether the var is global or local to outer.
7182 // - Having a non-dynamic assignment (and no global declaration) makes it easier to see
7183 // that the var is local by looking at just the outer function, and avoids warnings.
7184 // - A clear rule is easier to remember.
7185 if ( !(outer_var->IsAssignedSomewhere() || outer_var->IsDeclared()) )
7186 return nullptr;
7187 // Since this local variable is not static, things need to be set up so that the inner
7188 // function will capture it as an upvar at the appropriate time.
7189 if (mIsReadyToExecute)
7190 {
7191 // This dynamic reference resolved to a variable that hasn't been captured by the
7192 // current function, so is not safe to return.
7193 if (aDisplayError)
7194 *aDisplayError = RuntimeError(ERR_DYNAMIC_UPVAR, aVarName);
7195 return nullptr;
7196 }
7197 ASSERT(!outer.mDownVar && !aInner.mUpVar); // PreprocessLocalVars has not yet been called.
7198 if (!(outer_var->Scope() & VAR_DOWNVAR))
7199 {
7200 outer.mDownVarCount++;
7201 outer_var->Scope() |= VAR_DOWNVAR;
7202 }
7203 // At this point aInner doesn't have a variable by this name, so create one:
7204 int insert_pos;
7205 aInner.mVars.Find(aVarName, &insert_pos);
7206 Var *inner_var = AddVar(aVarName, 0, &aInner.mVars, insert_pos, VAR_LOCAL | VAR_DECLARED); // VAR_DECLARED suppresses checking for a global for #Warn LocalSameAsGlobal or when #Warn UseUnset is triggered.
7207 if (!inner_var)
7208 {
7209 if (aDisplayError)
7210 *aDisplayError = FAIL; // Error already displayed.
7211 return nullptr;
7212 }
7213 ASSERT(outer_var->Type() != VAR_CONSTANT || outer_var->HasObject() && dynamic_cast<UserFunc*>(outer_var->Object()));
7214 bool indefinite = outer_var->Type() == VAR_CONSTANT && !((UserFunc *)outer_var->Object())->mUpVarCount;

Callers

nothing calls this directly

Calls 8

IsAssignedSomewhereMethod · 0.80
IsDeclaredMethod · 0.80
HasObjectMethod · 0.80
SetAliasDirectMethod · 0.80
MarkAssignedSomewhereMethod · 0.80
FindMethod · 0.45
TypeMethod · 0.45
ObjectMethod · 0.45

Tested by

no test coverage detected