Repository: opentofu/opentofu
File: internal/tofu/eval_variable.go (Go)

Code before:
		})
	} else {
		// Since we don't have a source expression for a root module
		// variable, we'll just report the error from the perspective
		// of the variable declaration itself.
		diags = diags.Append(&hcl.Diagnostic{
			Severity:    hcl.DiagError,
			Summary:     errInvalidValue,
			Detail:      fmt.Sprintf("%s\n\nThis was checked by the validation rule at %s.", errorMessage, validation.DeclRange.String()),
			Subject:     config.DeclRange.Ptr(),
			Expression:  validation.Condition,
			EvalContext: hclCtx,
			Extra: &addrs.CheckRuleDiagnosticExtra{
				CheckRule: addr.CheckRule(addrs.InputValidation, ix),
			},
		})
	}

	return checkResult{
		Status:         status,
		FailureMessage: errorMessage,
	}, diags
}

// evalVariableDeprecation checks a variable "deprecated" message
func evalVariableDeprecation(addr addrs.AbsInputVariableInstance, config *configs.Variable, expr hcl.Expression, ctx EvalContext, onlyLocal bool) (diags tfdiags.Diagnostics) {
	if !config.DeprecatedSet {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s is having no deprecation configured", addr)
		return nil
	}
	if onlyLocal && strings.HasPrefix(config.DeclRange.Filename, ".terraform") {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s is part of a remote module and was requested to ignore those", addr)
		return nil
	}
	// if the variable is not given in the module call, do not show a warning
	if expr == nil {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s is marked as deprecated but is not used", addr)
		return nil
	}
	val := ctx.GetVariableValue(addr)
	if val.IsNull() {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s is marked as deprecated null value given", addr)
		return nil
	}
	log.Printf("[TRACE] evalVariableDeprecation: usage of deprecated variable %q detected", addr)
	// Any deprecated
	return diags.Append(&hcl.Diagnostic{
		Severity: hcl.DiagWarning,
		Summary:  fmt.Sprintf(`The variable %q is marked as deprecated by module author`, config.Name),
		Detail:   config.Deprecated,
		Subject:  expr.Range().Ptr(),

Review comment (performance):
I would remove the flag from that PR and implement it properly in a follow-up one. We could use a feature branch to encapsulate all the deprecation parts, however I believe it is fine to merge those sub-features directly to main.

nit: regarding the flag, I would pass an enum value here so it could be easily extended with more "statuses". For example, in the RFC, I mentioned `all` / `local` / `none`. It doesn't add any complexity so I would prefer this option (even if we are implementing only `all` and `local` for now).

Code after:
		})
	} else {
		// Since we don't have a source expression for a root module
		// variable, we'll just report the error from the perspective
		// of the variable declaration itself.
		diags = diags.Append(&hcl.Diagnostic{
			Severity:    hcl.DiagError,
			Summary:     errInvalidValue,
			Detail:      fmt.Sprintf("%s\n\nThis was checked by the validation rule at %s.", errorMessage, validation.DeclRange.String()),
			Subject:     config.DeclRange.Ptr(),
			Expression:  validation.Condition,
			EvalContext: hclCtx,
			Extra: &addrs.CheckRuleDiagnosticExtra{
				CheckRule: addr.CheckRule(addrs.InputValidation, ix),
			},
		})
	}

	return checkResult{
		Status:         status,
		FailureMessage: errorMessage,
	}, diags
}

// evalVariableDeprecation checks if a variable is deprecated and if so it returns a warning diagnostic to be shown to the user
func evalVariableDeprecation(addr addrs.AbsInputVariableInstance, config *configs.Variable, expr hcl.Expression, ctx EvalContext) tfdiags.Diagnostics {
	if config.Deprecated == "" {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s does not have deprecation configured", addr)
		return nil
	}
	// if the variable is not given in the module call, do not show a warning
	if expr == nil {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s is marked as deprecated but is not used", addr)
		return nil
	}
	val := ctx.GetVariableValue(addr)
	if val == cty.NilVal {
		log.Printf("[TRACE] evalVariableDeprecation: variable %s is marked as deprecated but no value given", addr)
		return nil
	}
	log.Printf("[TRACE] evalVariableDeprecation: usage of deprecated variable %q detected", addr)
	var diags tfdiags.Diagnostics
	return diags.Append(&hcl.Diagnostic{
		Severity: hcl.DiagWarning,
		Summary:  fmt.Sprintf(`The variable %q is marked as deprecated by module author`, config.Name),
		Detail:   fmt.Sprintf("This variable is marked as deprecated with the following message:\n%s", config.Deprecated),
		Subject:  expr.Range().Ptr(),
	})
}