code: func (pf *pluginSHA256LockFile) Read() map[string][]byte {
  	// Returning an empty map is different than nil because it causes
  	// us to reject all plugins as uninitialized, rather than applying no
  	// constraints at all.
  	//
  	// We don't surface any specific errors here because we want it to all
  	// roll up into our more-user-friendly error that appears when plugin
  	// constraint verification fails during context creation.
  	digests := make(map[string][]byte)
  
  	buf, err := ioutil.ReadFile(pf.Filename)
  	if err != nil {
  		// This is expected if the user runs any context-using command before
  		// running "terraform init".
  		log.Printf("[INFO] Failed to read plugin lock file %s: %s", pf.Filename, err)
  		return digests
  	}
  
  	var strDigests map[string]string
  	err = json.Unmarshal(buf, &strDigests)
  	if err != nil {
  		// This should never happen unless the user directly edits the file.
  		log.Printf("[WARN] Plugin lock file %s failed to parse as JSON: %s", pf.Filename, err)
  		return digests
  	}
  
  	for name, strDigest := range strDigests {
  		var digest []byte
  		_, err := fmt.Sscanf(strDigest, "%x", &digest)
  		if err == nil {
  			digests[name] = digest
  		} else {
  			// This should never happen unless the user directly edits the file.
  			log.Printf("[WARN] Plugin lock file %s has invalid digest for %q", pf.Filename, name)
  		}
  	}
  
  	return digests
  }
docs: // Read loads the lock information from the file and returns it. If the file
  // cannot be read, an empty map is returned to indicate that _no_ providers
  // are acceptable, since the user must run "terraform init" to lock some
  // providers before a context can be created.