Skip to content

Commit

Permalink
todo
Browse files Browse the repository at this point in the history
  • Loading branch information
nedpals committed Dec 22, 2023
1 parent 4a7c998 commit 226c384
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 1 deletion.
2 changes: 2 additions & 0 deletions error_templates/java/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ func LoadErrorTemplates(errorTemplates *lib.ErrorTemplates) {
errorTemplates.MustAdd(java.Language, UnclosedCharacterLiteralError)
errorTemplates.MustAdd(java.Language, OperatorCannotBeAppliedError)
errorTemplates.MustAdd(java.Language, PrecisionLossError)
errorTemplates.MustAdd(java.Language, MissingReturnError)
errorTemplates.MustAdd(java.Language, NotAStatementError)
errorTemplates.MustAdd(java.Language, IncompatibleTypesError)
errorTemplates.MustAdd(java.Language, UninitializedVariableError)
errorTemplates.MustAdd(java.Language, AlreadyDefinedError)
errorTemplates.MustAdd(java.Language, PrivateAccessError)
}

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

import (
"fmt"
"strings"

lib "github.com/nedpals/errgoengine"
)

type missingReturnErrorCtx struct {
NearestMethod lib.SyntaxNode
}

var MissingReturnError = lib.ErrorTemplate{
Name: "MissingReturnError",
Pattern: comptimeErrorPattern(`missing return statement`),
StackTracePattern: comptimeStackTracePattern,
OnAnalyzeErrorFn: func(cd *lib.ContextData, m *lib.MainError) {
// get nearest method declaration
mCtx := missingReturnErrorCtx{}
rootNode := lib.WrapNode(m.Document, m.Document.Tree.RootNode())
pos := m.ErrorNode.StartPos
lib.QueryNode(rootNode, strings.NewReader("(method_declaration) @method"), func(ctx lib.QueryNodeCtx) bool {
match := ctx.Cursor.FilterPredicates(ctx.Match, []byte(m.Nearest.Doc.Contents))
for _, c := range match.Captures {
pointA := c.Node.StartPoint()
pointB := c.Node.EndPoint()
if uint32(pos.Line) >= pointA.Row+1 && uint32(pos.Line) <= pointB.Row+1 {
node := lib.WrapNode(m.Nearest.Doc, c.Node)
mCtx.NearestMethod = node
return false
}
}
return true
})
fmt.Println(mCtx.NearestMethod.Text())
m.Context = mCtx
},
OnGenExplainFn: func(cd *lib.ContextData, gen *lib.ExplainGenerator) {
gen.Add("This error occurs when a method is declared to return a value, but there is no return statement within the method.")
},
OnGenBugFixFn: func(cd *lib.ContextData, gen *lib.BugFixGenerator) {
ctx := cd.MainError.Context.(missingReturnErrorCtx)

// TODO
gen.Add("Provide a return statement", func(s *lib.BugFixSuggestion) {
bodyNode := ctx.NearestMethod.ChildByFieldName("body")
lastStartPosInBlock := bodyNode.EndPosition()
lastEndPosInBlock := bodyNode.EndPosition()
if bodyNode.NamedChildCount() > 0 {
lastStartPosInBlock = bodyNode.LastNamedChild().StartPosition()
lastEndPosInBlock = bodyNode.LastNamedChild().EndPosition()
}

s.AddStep(
"Since the `%s` method is declared to return an `%s`, you need to provide a return statement with the result",
ctx.NearestMethod.ChildByFieldName("name").Text(),
ctx.NearestMethod.ChildByFieldName("type").Text(),
).AddFix(lib.FixSuggestion{
NewText: "\n" + cd.MainError.Document.LineAt(lastStartPosInBlock.Line)[:lastStartPosInBlock.Column] + fmt.Sprintf("return %s;", ctx.NearestMethod.ChildByFieldName("type").Text()),
StartPosition: lastEndPosInBlock,
EndPosition: lastEndPosInBlock,
Description: "This ensures that the method returns the sum of the two input numbers.",
})
})

gen.Add("Set the method return type to void", func(s *lib.BugFixSuggestion) {
s.AddStep(
"If you don't intend to return a value from the `%s` method, you can change its return type to `void`.",
ctx.NearestMethod.ChildByFieldName("name").Text(),
).AddFix(lib.FixSuggestion{
NewText: "void",
StartPosition: ctx.NearestMethod.ChildByFieldName("type").StartPosition(),
EndPosition: ctx.NearestMethod.ChildByFieldName("type").EndPosition(),
Description: "This is appropriate if you're using the method for side effects rather than returning a value.",
})
})
},
}
68 changes: 68 additions & 0 deletions error_templates/java/private_access_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package java

import (
"fmt"
"strings"

lib "github.com/nedpals/errgoengine"
)

type privateAccessErrorCtx struct {
ClassDeclarationNode lib.SyntaxNode
}

var PrivateAccessError = lib.ErrorTemplate{
Name: "PrivateAccessError",
Pattern: comptimeErrorPattern(`(?P<field>\S+) has private access in (?P<class>\S+)`),
StackTracePattern: comptimeStackTracePattern,
OnAnalyzeErrorFn: func(cd *lib.ContextData, m *lib.MainError) {
pCtx := privateAccessErrorCtx{}
className := cd.Variables["class"]
rootNode := lib.WrapNode(m.Nearest.Doc, m.Nearest.Doc.Tree.RootNode())

// locate the right node first
query := fmt.Sprintf(`((field_access (identifier) . (identifier) @field-name) @field (#eq? @field-name "%s"))`, cd.Variables["field"])
lib.QueryNode(rootNode, 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)
m.Nearest = node
return false
}
return true
})

// get class declaration node
classQuery := fmt.Sprintf(`(class_declaration name: (identifier) @class-name (#eq? @class-name "%s"))`, className)
lib.QueryNode(rootNode, strings.NewReader(classQuery), 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.ClassDeclarationNode = node
return false
}
return true
})

m.Context = pCtx
},
OnGenExplainFn: func(cd *lib.ContextData, gen *lib.ExplainGenerator) {
gen.Add("This error occurs when you try to access a private variable from another class, which is not allowed.")
},
OnGenBugFixFn: func(cd *lib.ContextData, gen *lib.BugFixGenerator) {
// ctx := cd.MainError.Context.(privateAccessErrorCtx)
fmt.Println(cd.MainError.Nearest.String())
fmt.Println(cd.Analyzer.AnalyzeNode(cd.MainError.Nearest))

gen.Add("Use a public accessor method", func(s *lib.BugFixSuggestion) {
// methodCreatorSb := &strings.Builder{}

// get return type of the private field

// methodCreatorSb.WriteString("public ")

// s.AddStep("To access a private variable from another class, create a public accessor method in `%s`", cd.Variables["class"]).
// AddFix()
})
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public class MissingReturn {
public int addNumbers(int a, int b) {
// Missing return statement
}

public static void main(String[] args) {
MissingReturn calculator = new MissingReturn();
int result = calculator.addNumbers(5, 7);
System.out.println("Result: " + result);
}
}
41 changes: 41 additions & 0 deletions error_templates/java/test_files/missing_return_error/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
template: "Java.MissingReturnError"
---
MissingReturn.java:4: error: missing return statement
}
^
1 error
===
template: "Java.MissingReturnError"
---
# MissingReturnError
This error occurs when a method is declared to return a value, but there is no return statement within the method.
```
// Missing return statement
}
^

public static void main(String[] args) {
```
## Steps to fix
### 1. Provide a return statement
Since the `addNumbers` method is declared to return an `int`, you need to provide a return statement with the result.
```diff
public class MissingReturn {
public int addNumbers(int a, int b) {
- // Missing return statement
+ // Missing return statement
+ return a;
}
```
This ensures that the method returns the sum of the two input numbers.

### 2. Set the method return type to void
If you don't intend to return a value from the `addNumbers` method, you can change its return type to `void`.
```diff
public class MissingReturn {
- public int addNumbers(int a, int b) {
+ public void addNumbers(int a, int b) {
// Missing return statement
}
```
This is appropriate if you're using the method for side effects rather than returning a value.
12 changes: 12 additions & 0 deletions error_templates/java/test_files/private_access_error/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
public class Main {
public static void main(String[] args) {
AnotherClass anotherClass = new AnotherClass();
// Attempting to access a private variable from another class
int value = anotherClass.privateVariable;
System.out.println(value);
}
}

class AnotherClass {
private int privateVariable = 10;
}
39 changes: 39 additions & 0 deletions error_templates/java/test_files/private_access_error/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
template: "Java.PrivateAccessError"
---
Main.java:5: error: privateVariable has private access in AnotherClass
int value = anotherClass.privateVariable;
^
1 error
===
template: "Java.PrivateAccessError"
---
# AlreadyDefinedError
This error occurs when you try to access a private variable from another class, which is not allowed.

## Steps to fix
### 1. Use a public accessor method
1. To access a private variable from another class, create a public accessor method in `AnotherClass`.
```diff
+ public int getPrivateVariable() {
+ return privateVariable;
+ }
```
2. Then, use this method to get the value in the `Main` class.
```diff
- int value = anotherClass.privateVariable;
+ int value = anotherClass.getPrivateVariable();
```
This way, you respect encapsulation by using a method to access the private variable.

### 2. Make the variable public (not recommended)
1. If you must access the variable directly, you can make it public, but this is generally not recommended for maintaining encapsulation.
```diff
- private int privateVariable = 10;
+ public int privateVariable = 10;
```
2. Access it directly in the `Main` class.
```diff
- int value = anotherClass.privateVariable;
+ int value = anotherClass.privateVariable;
```
Choose the fix that aligns with your design principles. Using an accessor method is a better practice for encapsulation.
6 changes: 6 additions & 0 deletions languages/java/language.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ func (an *javaAnalyzer) AnalyzeNode(n lib.SyntaxNode) lib.Symbol {

sym := an.FindSymbol(n.Text(), int(n.StartByte()))
if sym == nil {
if n.Type() == "type_identifier" {
an.ContextData.

// mark type as unresolved
return lib.UnresolvedSymbol

Check failure on line 81 in languages/java/language.go

View workflow job for this annotation

GitHub Actions / build

syntax error: unexpected return, expected name or (
}
return BuiltinTypes.NullSymbol
}

Expand Down
17 changes: 17 additions & 0 deletions symbols.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func NewSymbolKindFromString(str string) SymbolKind {

const (
SymbolKindUnknown SymbolKind = 0
SymbolKindUnresolved SymbolKind = iota
SymbolKindBuiltin SymbolKind = iota
SymbolKindClass SymbolKind = iota
SymbolKindFunction SymbolKind = iota
Expand Down Expand Up @@ -219,6 +220,22 @@ func (sym ImportSymbol) Location() Location {
}
}

type unresolvedSymbol struct{}

func (sym unresolvedSymbol) Name() string {
return "unresolved"
}

func (sym unresolvedSymbol) Kind() SymbolKind {
return SymbolKindUnresolved
}

func (sym unresolvedSymbol) Location() Location {
return Location{}
}

var UnresolvedSymbol Symbol = unresolvedSymbol{}

// TODO:
// func (sym ImportSymbol) Children() *SymbolTree {
// // TODO:
Expand Down
3 changes: 2 additions & 1 deletion tests/java/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"bugbuddy.path": "/Users/nedpals/Documents/coding/bugbuddy-proto/server/cmd/bugbuddy"
"bugbuddy.path": "/Users/nedpals/Documents/coding/bugbuddy-proto/server/cmd/bugbuddy",
"java.debug.settings.onBuildFailureProceed": true
}

0 comments on commit 226c384

Please sign in to comment.