-
Notifications
You must be signed in to change notification settings - Fork 247
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9f56401
commit 0ac1275
Showing
2,410 changed files
with
26,186 additions
and
113,508 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package gopls | ||
|
||
import ( | ||
"context" | ||
"go.lsp.dev/protocol" | ||
|
||
"go.uber.org/zap" | ||
) | ||
|
||
type DummyClient struct { | ||
logger *zap.Logger | ||
} | ||
|
||
func NewDummyClient(logger *zap.Logger) *DummyClient { | ||
return &DummyClient{ | ||
logger: nil, //logger.Named("client"), | ||
} | ||
} | ||
|
||
func (d *DummyClient) Progress(ctx context.Context, params *protocol.ProgressParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: Progress", zap.Any("params", params)) | ||
} | ||
return | ||
} | ||
func (d *DummyClient) WorkDoneProgressCreate(ctx context.Context, params *protocol.WorkDoneProgressCreateParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: WorkDoneProgressCreate") | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) LogMessage(ctx context.Context, params *protocol.LogMessageParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: LogMessage", zap.Any("message", params)) | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: PublishDiagnostics") | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: ShowMessage", zap.Any("message", params)) | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) ShowMessageRequest(ctx context.Context, params *protocol.ShowMessageRequestParams) (result *protocol.MessageActionItem, err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: ShowMessageRequest", zap.Any("message", params)) | ||
} | ||
return nil, nil | ||
} | ||
|
||
func (d *DummyClient) Telemetry(ctx context.Context, params interface{}) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: Telemetry") | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) RegisterCapability(ctx context.Context, params *protocol.RegistrationParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: RegisterCapability") | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) UnregisterCapability(ctx context.Context, params *protocol.UnregistrationParams) (err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: UnregisterCapability") | ||
} | ||
return nil | ||
} | ||
|
||
func (d *DummyClient) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (result bool, err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: ApplyEdit") | ||
} | ||
return false, nil | ||
} | ||
|
||
func (d *DummyClient) Configuration(ctx context.Context, params *protocol.ConfigurationParams) (result []interface{}, err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: Configuration") | ||
} | ||
return nil, nil | ||
} | ||
|
||
func (d *DummyClient) WorkspaceFolders(ctx context.Context) (result []protocol.WorkspaceFolder, err error) { | ||
if d.logger != nil { | ||
d.logger.Debug("client: WorkspaceFolders") | ||
} | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package gopls | ||
|
||
import ( | ||
"os/exec" | ||
"github.com/pkg/errors" | ||
|
||
"go.lsp.dev/jsonrpc2" | ||
"go.lsp.dev/protocol" | ||
"context" | ||
|
||
"time" | ||
"go.uber.org/zap" | ||
"go.lsp.dev/uri" | ||
) | ||
|
||
type Connection struct { | ||
logger *zap.Logger | ||
server protocol.Server | ||
cmd *exec.Cmd | ||
conn jsonrpc2.Conn | ||
} | ||
|
||
func NewGoplsClient(ctx context.Context, logger *zap.Logger, rootDir string) *Connection { | ||
var err error | ||
|
||
logger.Debug("initializing gopls client") | ||
|
||
gopls := &Connection{ | ||
logger: logger, | ||
} | ||
|
||
client := NewDummyClient(logger) | ||
|
||
// Create a JSON-RPC connection using stdin and stdout | ||
gopls.cmd = exec.Command("gopls", "serve") | ||
|
||
stdin, err := gopls.cmd.StdinPipe() | ||
if err != nil { | ||
logger.Error("Failed to get stdin pipe", zap.Error(err)) | ||
panic(err) | ||
} | ||
|
||
stdout, err := gopls.cmd.StdoutPipe() | ||
if err != nil { | ||
logger.Error("Failed to get stdout pipe", zap.Error(err)) | ||
panic(err) | ||
} | ||
|
||
err = gopls.cmd.Start() | ||
if err != nil { | ||
logger.Error("Failed to start gopls", zap.Error(err)) | ||
panic(err) | ||
} | ||
|
||
stream := jsonrpc2.NewStream(&CombinedReadWriteCloser{ | ||
stdin: stdin, | ||
stdout: stdout, | ||
}) | ||
|
||
ctx, gopls.conn, gopls.server = protocol.NewClient(ctx, client, stream, logger) | ||
|
||
initParams := protocol.InitializeParams{ | ||
RootURI: uri.From("file", "", rootDir, "", ""), | ||
InitializationOptions: map[string]interface{}{ | ||
"symbolMatcher": "FastFuzzy", | ||
}, | ||
} | ||
|
||
_, err = gopls.server.Initialize(ctx, &initParams) | ||
if err != nil { | ||
logger.Error("Error during initialize", zap.Error(err)) | ||
panic(err) | ||
} | ||
|
||
// Step 2: Send 'initialized' notification | ||
err = gopls.server.Initialized(ctx, &protocol.InitializedParams{}) | ||
if err != nil { | ||
logger.Error("Error during initialized", zap.Error(err)) | ||
panic(err) | ||
} | ||
|
||
return gopls | ||
} | ||
|
||
func (gopls *Connection) Definition(ctx context.Context, filePath string, lineNumber int, charPosition int) (string, int, error) { | ||
// NOTE: gopls uses 0-based line and column numbers | ||
defFile, defLine, err := gopls.definition(ctx, filePath, lineNumber-1, charPosition-1) | ||
return defFile, defLine + 1, err | ||
} | ||
|
||
func (gopls *Connection) definition(ctx context.Context, filePath string, lineNumber int, charPosition int) (string, int, error) { | ||
// Define the file URI and position where the function/method is invoked | ||
fileURI := protocol.DocumentURI("file://" + filePath) // Replace with actual file URI | ||
line := lineNumber // Line number where the function is called | ||
character := charPosition // Character (column) where the function is called | ||
|
||
// Send the definition request | ||
params := &protocol.DefinitionParams{ | ||
TextDocumentPositionParams: protocol.TextDocumentPositionParams{ | ||
TextDocument: protocol.TextDocumentIdentifier{ | ||
URI: fileURI, | ||
}, | ||
Position: protocol.Position{ | ||
Line: uint32(line), | ||
Character: uint32(character), | ||
}, | ||
}, | ||
} | ||
|
||
// Create context with a timeout to avoid hanging | ||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) | ||
defer cancel() | ||
|
||
locations, err := gopls.server.Definition(ctx, params) | ||
if err != nil { | ||
return "", 0, errors.Wrap(err, "failed to fetch definition") | ||
} | ||
|
||
if len(locations) == 0 { | ||
return "", 0, errors.New("no definition found") | ||
} | ||
|
||
location := locations[0] | ||
return location.URI.Filename(), int(location.Range.Start.Line), nil | ||
} | ||
|
||
func (gopls *Connection) DidOpen(ctx context.Context, path string, content string, logger *zap.Logger) { | ||
err := gopls.server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{ | ||
TextDocument: protocol.TextDocumentItem{ | ||
URI: protocol.DocumentURI(path), | ||
LanguageID: "go", | ||
Version: 1, | ||
Text: content, | ||
}, | ||
}) | ||
if err != nil { | ||
logger.Error("failed to call DidOpen", zap.Error(err)) | ||
} | ||
} | ||
|
||
func (gopls *Connection) DidClose(ctx context.Context, path string, lgoger *zap.Logger) { | ||
err := gopls.server.DidClose(ctx, &protocol.DidCloseTextDocumentParams{ | ||
TextDocument: protocol.TextDocumentIdentifier{ | ||
URI: protocol.DocumentURI(path), | ||
}, | ||
}) | ||
if err != nil { | ||
lgoger.Error("failed to call DidClose", zap.Error(err)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package gopls | ||
|
||
import "io" | ||
|
||
// CombinedReadWriteCloser combines stdin and stdout into one interface. | ||
type CombinedReadWriteCloser struct { | ||
stdin io.WriteCloser | ||
stdout io.ReadCloser | ||
} | ||
|
||
// Write writes data to stdin. | ||
func (c *CombinedReadWriteCloser) Write(p []byte) (n int, err error) { | ||
return c.stdin.Write(p) | ||
} | ||
|
||
// Read reads data from stdout. | ||
func (c *CombinedReadWriteCloser) Read(p []byte) (n int, err error) { | ||
return c.stdout.Read(p) | ||
} | ||
|
||
// Close closes both stdin and stdout. | ||
func (c *CombinedReadWriteCloser) Close() error { | ||
err1 := c.stdin.Close() | ||
err2 := c.stdout.Close() | ||
if err1 != nil { | ||
return err1 | ||
} | ||
return err2 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
"time" | ||
"context" | ||
"go.uber.org/zap" | ||
"go.uber.org/zap/zapcore" | ||
gopls2 "github.com/status-im/status-go/cmd/lint-panics/gopls" | ||
"github.com/status-im/status-go/cmd/lint-panics/processor" | ||
"golang.org/x/tools/go/analysis/singlechecker" | ||
"golang.org/x/tools/go/analysis" | ||
"go/ast" | ||
"fmt" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
func main() { | ||
logger := buildLogger() | ||
|
||
if len(os.Args) == 0 { | ||
logger.Error("Usage: go run main.go <directory>") | ||
os.Exit(1) | ||
} | ||
|
||
// Dirty hack to get the root directory for gopls | ||
lastArg := os.Args[len(os.Args)-1] | ||
dir, err := getRootAbsolutePath(lastArg) | ||
if err != nil { | ||
logger.Error("failed to get root directory", zap.Error(err)) | ||
os.Exit(1) | ||
} | ||
|
||
logger.Info("starting analysis...", zap.String("directory", dir)) | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) | ||
defer cancel() | ||
|
||
gopls := gopls2.NewGoplsClient(ctx, logger, dir) | ||
|
||
analyzer := &analysis.Analyzer{ | ||
Name: "logpanics", | ||
Doc: "reports missing defer call to LogOnPanic", | ||
Run: func(pass *analysis.Pass) (interface{}, error) { | ||
for _, file := range pass.Files { | ||
p := processor.NewProcessor(logger, pass, gopls) | ||
ast.Inspect(file, func(n ast.Node) bool { | ||
p.ProcessNode(ctx, n) | ||
return true | ||
}) | ||
} | ||
return nil, nil | ||
}, | ||
} | ||
|
||
singlechecker.Main(analyzer) | ||
} | ||
|
||
func buildLogger() *zap.Logger { | ||
// Initialize logger with colors | ||
loggerConfig := zap.NewDevelopmentConfig() | ||
loggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder | ||
loggerConfig.Level = zap.NewAtomicLevelAt(zap.ErrorLevel) | ||
loggerConfig.Development = false | ||
loggerConfig.DisableStacktrace = true | ||
logger, err := loggerConfig.Build() | ||
if err != nil { | ||
fmt.Printf("failed to initialize logger: %s", err.Error()) | ||
os.Exit(1) | ||
} | ||
|
||
return logger.Named("main") | ||
} | ||
|
||
func getRootAbsolutePath(path string) (string, error) { | ||
// Get the absolute path of the current working directory | ||
workingDir, err := os.Getwd() | ||
if err != nil { | ||
return "", fmt.Errorf("failed to get working directory: %w", err) | ||
} | ||
|
||
if strings.HasSuffix(path, "...") { | ||
path = strings.TrimSuffix(path, "...") | ||
} | ||
|
||
// Check if the given path is absolute | ||
if !filepath.IsAbs(path) { | ||
// If the path is not absolute, join it with the working directory | ||
path = filepath.Join(workingDir, path) | ||
} | ||
|
||
// Convert the path to an absolute path (cleans the result) | ||
absolutePath, err := filepath.Abs(path) | ||
if err != nil { | ||
return "", fmt.Errorf("failed to convert to absolute path: %w", err) | ||
} | ||
|
||
return absolutePath, nil | ||
} |
Oops, something went wrong.