github.com/opentofu/opentofu@v1.7.1/internal/tfdiags/hcl.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package tfdiags
     7  
     8  import (
     9  	"github.com/hashicorp/hcl/v2"
    10  )
    11  
    12  // hclDiagnostic is a Diagnostic implementation that wraps a HCL Diagnostic
    13  type hclDiagnostic struct {
    14  	diag *hcl.Diagnostic
    15  }
    16  
    17  var _ Diagnostic = hclDiagnostic{}
    18  
    19  func (d hclDiagnostic) Severity() Severity {
    20  	switch d.diag.Severity {
    21  	case hcl.DiagWarning:
    22  		return Warning
    23  	default:
    24  		return Error
    25  	}
    26  }
    27  
    28  func (d hclDiagnostic) Description() Description {
    29  	return Description{
    30  		Summary: d.diag.Summary,
    31  		Detail:  d.diag.Detail,
    32  	}
    33  }
    34  
    35  func (d hclDiagnostic) Source() Source {
    36  	var ret Source
    37  	if d.diag.Subject != nil {
    38  		rng := SourceRangeFromHCL(*d.diag.Subject)
    39  		ret.Subject = &rng
    40  	}
    41  	if d.diag.Context != nil {
    42  		rng := SourceRangeFromHCL(*d.diag.Context)
    43  		ret.Context = &rng
    44  	}
    45  	return ret
    46  }
    47  
    48  func (d hclDiagnostic) FromExpr() *FromExpr {
    49  	if d.diag.Expression == nil || d.diag.EvalContext == nil {
    50  		return nil
    51  	}
    52  	return &FromExpr{
    53  		Expression:  d.diag.Expression,
    54  		EvalContext: d.diag.EvalContext,
    55  	}
    56  }
    57  
    58  func (d hclDiagnostic) ExtraInfo() interface{} {
    59  	return d.diag.Extra
    60  }
    61  
    62  // SourceRangeFromHCL constructs a SourceRange from the corresponding range
    63  // type within the HCL package.
    64  func SourceRangeFromHCL(hclRange hcl.Range) SourceRange {
    65  	return SourceRange{
    66  		Filename: hclRange.Filename,
    67  		Start: SourcePos{
    68  			Line:   hclRange.Start.Line,
    69  			Column: hclRange.Start.Column,
    70  			Byte:   hclRange.Start.Byte,
    71  		},
    72  		End: SourcePos{
    73  			Line:   hclRange.End.Line,
    74  			Column: hclRange.End.Column,
    75  			Byte:   hclRange.End.Byte,
    76  		},
    77  	}
    78  }
    79  
    80  // ToHCL constructs a HCL Range from the receiving SourceRange. This is the
    81  // opposite of SourceRangeFromHCL.
    82  func (r SourceRange) ToHCL() hcl.Range {
    83  	return hcl.Range{
    84  		Filename: r.Filename,
    85  		Start: hcl.Pos{
    86  			Line:   r.Start.Line,
    87  			Column: r.Start.Column,
    88  			Byte:   r.Start.Byte,
    89  		},
    90  		End: hcl.Pos{
    91  			Line:   r.End.Line,
    92  			Column: r.End.Column,
    93  			Byte:   r.End.Byte,
    94  		},
    95  	}
    96  }
    97  
    98  // ToHCL constructs a hcl.Diagnostics containing the same diagnostic messages
    99  // as the receiving tfdiags.Diagnostics.
   100  //
   101  // This conversion preserves the data that HCL diagnostics are able to
   102  // preserve but would be lossy in a round trip from tfdiags to HCL and then
   103  // back to tfdiags, because it will lose the specific type information of
   104  // the source diagnostics. In most cases this will not be a significant
   105  // problem, but could produce an awkward result in some special cases such
   106  // as converting the result of ConsolidateWarnings, which will force the
   107  // resulting warning groups to be flattened early.
   108  func (diags Diagnostics) ToHCL() hcl.Diagnostics {
   109  	if len(diags) == 0 {
   110  		return nil
   111  	}
   112  	ret := make(hcl.Diagnostics, len(diags))
   113  	for i, diag := range diags {
   114  		severity := diag.Severity()
   115  		desc := diag.Description()
   116  		source := diag.Source()
   117  		fromExpr := diag.FromExpr()
   118  
   119  		hclDiag := &hcl.Diagnostic{
   120  			Summary:  desc.Summary,
   121  			Detail:   desc.Detail,
   122  			Severity: severity.ToHCL(),
   123  		}
   124  		if source.Subject != nil {
   125  			hclDiag.Subject = source.Subject.ToHCL().Ptr()
   126  		}
   127  		if source.Context != nil {
   128  			hclDiag.Context = source.Context.ToHCL().Ptr()
   129  		}
   130  		if fromExpr != nil {
   131  			hclDiag.Expression = fromExpr.Expression
   132  			hclDiag.EvalContext = fromExpr.EvalContext
   133  		}
   134  
   135  		ret[i] = hclDiag
   136  	}
   137  	return ret
   138  }