Skip to content

Commit

Permalink
feat: implement Java.PrecisionLossError
Browse files Browse the repository at this point in the history
  • Loading branch information
nedpals committed Dec 12, 2023
1 parent d519b0d commit 83ee4b3
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 0 deletions.
1 change: 1 addition & 0 deletions error_templates/java/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func LoadErrorTemplates(errorTemplates *lib.ErrorTemplates) {
errorTemplates.MustAdd(java.Language, NonStaticMethodAccessError)
errorTemplates.MustAdd(java.Language, UnclosedCharacterLiteralError)
errorTemplates.MustAdd(java.Language, OperatorCannotBeAppliedError)
errorTemplates.MustAdd(java.Language, PrecisionLossError)
}

func runtimeErrorPattern(errorName string, pattern string) string {
Expand Down
80 changes: 80 additions & 0 deletions error_templates/java/precision_loss_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package java

import (
"fmt"
"strings"

lib "github.com/nedpals/errgoengine"
)

type precisionLossCtx struct {
Parent lib.SyntaxNode
}

var PrecisionLossError = lib.ErrorTemplate{
Name: "PrecisionLossError",
Pattern: comptimeErrorPattern(`incompatible types: possible lossy conversion from (?P<currentType>\S+) to (?P<targetType>\S+)`),
StackTracePattern: comptimeStackTracePattern,
OnAnalyzeErrorFn: func(cd *lib.ContextData, m *lib.MainError) {
pCtx := precisionLossCtx{}
targetType := cd.Variables["targetType"]
query := fmt.Sprintf(`((local_variable_declaration type: (_) @target-type) (#eq? @target-type "%s"))`, targetType)
lib.QueryNode(m.Nearest, strings.NewReader(query), func(ctx lib.QueryNodeCtx) bool {
match := ctx.Cursor.FilterPredicates(ctx.Match, []byte(m.Nearest.Doc.Contents))
for _, c := range match.Captures {
node := lib.WrapNode(m.Nearest.Doc, c.Node)
pCtx.Parent = node.Parent()
m.Nearest = pCtx.Parent.ChildByFieldName("declarator").ChildByFieldName("value")
return false
}
return true
})
m.Context = pCtx
},
OnGenExplainFn: func(cd *lib.ContextData, gen *lib.ExplainGenerator) {
gen.Add(
"This error occurs when you try to assign a value from a data type with higher precision (%s) to a data type with lower precision (%s), which may result in a loss of precision.",
cd.Variables["currentType"],
cd.Variables["targetType"],
)
},
OnGenBugFixFn: func(cd *lib.ContextData, gen *lib.BugFixGenerator) {
// ctx := cd.MainError.Context.(precisionLossCtx)
variableInvolved := cd.MainError.Nearest
gen.Add(fmt.Sprintf("Explicitly cast to %s", cd.Variables["targetType"]), func(s *lib.BugFixSuggestion) {
s.AddStep("To resolve the precision loss, explicitly cast the `%s` to %s.", variableInvolved.Text(), cd.Variables["targetType"]).AddFix(lib.FixSuggestion{
NewText: fmt.Sprintf("(%s) ", cd.Variables["targetType"]),
StartPosition: variableInvolved.StartPosition(),
EndPosition: variableInvolved.StartPosition(),
Description: "This casting informs the compiler about the potential loss of precision and allows the assignment.",
})
})

gen.Add(fmt.Sprintf("Use an 'f' suffix for the %s literal", cd.Variables["targetType"]), func(s *lib.BugFixSuggestion) {
nearestTree := cd.InitOrGetSymbolTree(cd.MainError.DocumentPath()).GetNearestScopedTree(variableInvolved.StartPosition().Index)

involvedVariable := nearestTree.GetSymbolByNode(variableInvolved)
if involvedVariable == nil {
// TODO: remove this check(?)
return
}

involvedVariablePos := involvedVariable.Location().Range()
node := cd.MainError.Document.Tree.RootNode().NamedDescendantForPointRange(
involvedVariablePos.StartPoint,
involvedVariablePos.EndPoint,
)

involvedVariableValueNode := lib.WrapNode(cd.MainError.Document, node.ChildByFieldName("value"))

s.AddStep(
"Alternatively, you can use the 'f' suffix to specify that the literal is of type %s.",
cd.Variables["targetType"]).AddFix(lib.FixSuggestion{
NewText: involvedVariableValueNode.Text() + "f",
StartPosition: variableInvolved.StartPosition(),
EndPosition: variableInvolved.EndPosition(),
Description: "This way, you directly define the float variable without the need for casting.",
})
})
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public class PrecisionLoss {
public static void main(String[] args) {
double largeNumber = 12345678901234567890.123456789;

// Potential loss of precision: Found double, required float
float smallNumber = largeNumber;
}
}
42 changes: 42 additions & 0 deletions error_templates/java/test_files/precision_loss_error/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
template: "Java.PrecisionLossError"
---
PrecisionLoss.java:6: error: incompatible types: possible lossy conversion from double to float
float smallNumber = largeNumber;
^
1 error
===
template: "Java.PrecisionLossError"
---
# PrecisionLossError
This error occurs when you try to assign a value from a data type with higher precision (double) to a data type with lower precision (float), which may result in a loss of precision.
```
// Potential loss of precision: Found double, required float
float smallNumber = largeNumber;
^^^^^^^^^^^
}
}
```
## Steps to fix
### 1. Explicitly cast to float
To resolve the precision loss, explicitly cast the `largeNumber` to float.
```diff

// Potential loss of precision: Found double, required float
- float smallNumber = largeNumber;
+ float smallNumber = (float) largeNumber;
}
}
```
This casting informs the compiler about the potential loss of precision and allows the assignment.

### 2. Use an 'f' suffix for the float literal
Alternatively, you can use the 'f' suffix to specify that the literal is of type float.
```diff

// Potential loss of precision: Found double, required float
- float smallNumber = largeNumber;
+ float smallNumber = 12345678901234567890.123456789f;
}
}
```
This way, you directly define the float variable without the need for casting.

0 comments on commit 83ee4b3

Please sign in to comment.