Repository: opentofu/opentofu
File: internal/command/init.go (Go)

Code before:
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.`))
		}

		moreDiags = c.replaceLockedDependencies(newLocks)
		diags = diags.Append(moreDiags)
	}

	return true, false, diags
}

// warnOnFailedImplicitProvReference returns a warn diagnostic when the downloader fails to fetch a provider that is implicitly referenced.
// In other words, if the failed to download provider is having no required_providers entry, this function is trying to give to the user
// more information on the source of the issue and gives also instructions on how to fix it.
func warnOnFailedImplicitProvReference(provider addrs.Provider, qualifs *getproviders.ProvidersQualification) tfdiags.Diagnostic {
	if _, ok := qualifs.Explicit[provider]; !ok {
		var refFromHint string
		if refs, ok := qualifs.Implicit[provider]; ok && len(refs) > 0 {
			refFromHint = fmt.Sprintf("Implicitly referenced from %s.", refs[0].String())
			if noOfTotalRes := len(refs) - 1; noOfTotalRes > 0 {
				refFromHint += fmt.Sprintf(" And is referenced from %d more resource(s).", noOfTotalRes)
			}
		}
		details := fmt.Sprintf(`The provider %s doesn't exist and is referenced by a resource implicitly without a 'required_providers' blocks. This typically happens when you are missing the 'required_providers' block or you are not referencing it correctly using the 'provider=' parameter in your resource or data source block.`,
			provider.ForDisplay(),
		)
		if len(refFromHint) > 0 {
			details += fmt.Sprintf("\n\n%s", refFromHint)
		}
		return tfdiags.Sourceless(
			tfdiags.Warning,
			"No explicit definition for the provider",
			details,
		)
	}
	return nil
}

// backendConfigOverrideBody interprets the raw values of -backend-config
// arguments into a hcl Body that should override the backend settings given
// in the configuration.
//
// If the result is nil then no override needs to be provided.
//
// If the returned diagnostics contains errors then the returned body may be
// incomplete or invalid.
func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) {
	items := flags.AllItems()
	if len(items) == 0 {
		return nil, nil
	}

Review comment (suggestion):
Hmm... GitHub somehow lost this comment the first time, so I'm going to try again. :confounded: 

I'd suggest the following error message text both to try to write this in the same "voice" as other messages OpenTofu produces and to hopefully help users understand how OpenTofu infers provider requirements automatically when none are specified.

```suggestion
		details := fmt.Sprintf(
			"Due to the prefix of the resource type name OpenTofu guessed that you intended to associate %s with a provider whose local name is %q, but that name is not declared in this module's required_providers block. OpenTofu therefore guessed that you intended to use %s, but that provider does not exist.\n\nMake at least one of the following changes to tell OpenTofu which provider to use:\n - Add a declaration for local name %q to this module's required_providers block, specifying the full source address for the provider you intended to use.\n - Verify that %q is the correct resource type name to use. Did you omit a prefix which would imply the correct provider?\n - Use a \"provider\" argument within this resource block to override OpenTofu's automatic selection of the local name %q.",
			/* (..this message needs some more information than we currently have in scope here..) */
		)
```

The main extra thing I want to add here is the information that, for example, OpenTofu guesses that `aws_s3_object` belongs to provider local name `aws` based on the prefix. It's hard to explain that behavior concisely -- making this message longer than I would prefer -- but I hope we can make it understandable by including the actual resource address and inferred local provider name directly in the error message.

Of course, to do that we'd need to track more detailed information in the ProvidersQualification object. I personally think it's worth tracking the full set of resource addresses (`addrs.ConfigResource` values) that had implicit references to each distinct provider address, rather than just the source address as this is currently doing, so that we can say exactly which resource and provider local name we're talking about. Here's how I would expect the above error message to render, just in case it isn't clear what information each of those printf verbs might be representing:

```
Warning: Automatically-inferred provider dependency

  on example.tf line 2:
   2: resource "s3_bucket" "example" {

Due to the prefix of the resource type name OpenTofu guessed that
you intended to associate s3_bucket.example with a provider whose
local name is "s3", but that name is not declared in this module's
required_providers block. OpenTofu therefore guessed that you
intended to use hashicorp/s3, but that provider does not exist.

Make at least one of the following changes to tell OpenTofu which
provider to use:

- Add a declaration for local name "s3" to this module's
required_providers block, specifying the full source address
for the provider you intended to use.
- Verify that "s3_bucket" is the correct resource type name to use.
Did you omit a prefix which would imply the correct provider?
- Use a "provider" argument within this resource block to override
OpenTofu's automatic selection of the local name "s3".
```

Although I very much do not like putting bullet lists in diagnostic messages, I've found from helping people with variations of this problem over the years that all three of these are potential causes and I don't think we really have enough information here to automatically make a strong guess as to which one is most likely. :confounded: 

This rendering also assumes that we'd place the source address of the resource declaration into the diagnostic's "Subject", rather than just inserting the string representation as part of the error message, which isn't necesarily required but would make that information more accessible to tools that are consuming the JSON representation of diagnostics, rather than the human-readable form.




Code after:
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.`))
		}

		moreDiags = c.replaceLockedDependencies(newLocks)
		diags = diags.Append(moreDiags)
	}

	return true, false, diags
}

// warnOnFailedImplicitProvReference returns a warn diagnostic when the downloader fails to fetch a provider that is implicitly referenced.
// In other words, if the failed to download provider is having no required_providers entry, this function is trying to give to the user
// more information on the source of the issue and gives also instructions on how to fix it.
func warnOnFailedImplicitProvReference(provider addrs.Provider, qualifs *getproviders.ProvidersQualification) tfdiags.Diagnostics {
	if _, ok := qualifs.Explicit[provider]; ok {
		return nil
	}
	refs, ok := qualifs.Implicit[provider]
	if !ok || len(refs) == 0 {
		// If there is no implicit reference for that provider, do not write the warn, let just the error to be returned.
		return nil
	}

	// NOTE: if needed, in the future we can use the rest of the "refs" to print all the culprits or at least to give
	// a hint on how many resources are causing this
	ref := refs[0]
	if ref.ProviderAttribute {
		return nil
	}
	details := fmt.Sprintf(
		implicitProviderReferenceBody,
		ref.CfgRes.String(),
		provider.Type,
		provider.ForDisplay(),
		provider.Type,
		ref.CfgRes.Resource.Type,
		provider.Type,
	)
	return tfdiags.Diagnostics{}.Append(
		&hcl.Diagnostic{
			Severity: hcl.DiagWarning,
			Subject:  ref.Ref.ToHCL().Ptr(),
			Summary:  implicitProviderReferenceHead,
			Detail:   details,
		})
}

// backendConfigOverrideBody interprets the raw values of -backend-config
// arguments into a hcl Body that should override the backend settings given
// in the configuration.