Skip to content

Commit

Permalink
Configure compilation DB path by request from SonarLint server
Browse files Browse the repository at this point in the history
Try to find it automatically in one of the parent directories,
ask user for it, otherwise.
  • Loading branch information
necto committed Jul 13, 2024
1 parent 2a08070 commit d5e0d2d
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SHELL := /usr/bin/env bash
EMACS ?= emacs
EASK ?= eask

TEST-FILES := $(shell ls test/*.el)
TEST-FILES := $(shell ls test/*-test.el)

.PHONY: clean checkdoc lint package install compile download-sonarlint test

Expand Down
1 change: 1 addition & 0 deletions fixtures/compdb/dir1/1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int x;
1 change: 1 addition & 0 deletions fixtures/compdb/dir1/compile_commands.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions fixtures/compdb/dir1/dir11/11.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int x;
1 change: 1 addition & 0 deletions fixtures/compdb/dir1/dir11/compile_commands.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions fixtures/compdb/dir1/dir12/12.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int x;
Empty file.
1 change: 1 addition & 0 deletions fixtures/compdb/dir1/dir12/dir121/121.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int x;
1 change: 1 addition & 0 deletions fixtures/compdb/no-compdb/no-compdb.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int x;
File renamed without changes.
File renamed without changes.
56 changes: 43 additions & 13 deletions lsp-sonarlint.el
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,43 @@ e.g. `-Xmx1024m`."
:group 'lsp-sonarlint
:type 'string)

(defcustom lsp-sonarlint-cfamily-compile-commands-path
"${workspaceFolder}/compile_commands.json"
"Location of the compile_commands.json file.
It is needed in C/C++ to provide your compilation options."
(defun lsp-sonarlint--find-file-in-parent-folders (fname)
"Find the closest FNAME in a buffer folder or one of its parents.
Traverse the parent folders from narrow to wide (/a/b/c, /a/b, /a, /).
If none of them contains FNAME, return nil."
(let ((dir (file-name-directory (expand-file-name (buffer-file-name))))
(found-file nil))
(while (not (or found-file (string-empty-p dir) (string-equal dir "/")))
(let ((potential-file (concat dir fname)))
(message potential-file)
(if (file-exists-p potential-file)
(setq found-file potential-file)
(setq dir (file-name-directory (directory-file-name dir))))))
found-file))

(defcustom-lsp lsp-sonarlint-path-to-compile-commands ""
"Path to the compilation DB - the compile_commands.json file."
:type 'path
:group 'lsp-sonarlint
:type 'file)
:lsp-path "sonarlint.pathToCompileCommands")

(defun lsp-sonarlint--get-compile-commands ()
"Find compile_commands.json in the parent dir or ask user."
(or (lsp-sonarlint--find-file-in-parent-folders "compile_commands.json")
(read-file-name "Provide path to compile_commands.json for this project: ")))

(defun lsp-sonarlint--set-compile-commands (_workspace _params)
"Find compile_commands.json and set it for the workspace.
As a side effect it will also send the found path to the SonarLint server."
(let ((fname (lsp-sonarlint--get-compile-commands)))
(unless (string-empty-p fname)
(message "Using compilation database from %s." fname)
(custom-set-variables `(lsp-sonarlint-path-to-compile-commands ,(expand-file-name fname))))))

(defun lsp-sonarlint-set-compile-commands (fname)
"Set FNAME as the path to the compile_commands.json file for current session."
(interactive "fCompilation DB path: ")
(custom-set-variables `(lsp-sonarlint-path-to-compile-commands ,(expand-file-name fname))))

(defconst lsp-sonarlint-go-doc-url "https://www.sonarsource.com/go/"
"Documentation sonarsource URL.")
Expand Down Expand Up @@ -324,7 +355,6 @@ See `lsp-sonarlint-analyze-folder' to see which files are ignored."
(lsp-register-custom-settings
'(("sonarlint.disableTelemetry" lsp-sonarlint-disable-telemetry)
("sonarlint.testFilePattern" lsp-sonarlint-test-file-pattern)
("sonarlint.pathToCompileCommands" lsp-sonarlint-cfamily-compile-commands-path)
("sonarlint.output.showAnalyzerLogs" lsp-sonarlint-show-analyzer-logs)
("sonarlint.output.verboseLogs" lsp-sonarlint-verbose-logs)
("sonarlint.ls.vmargs" lsp-sonarlint-vmargs)))
Expand Down Expand Up @@ -380,8 +410,7 @@ See REQUEST-HANDLERS in lsp--client in lsp-mode."
("sonarlint/readyForTests" #'ignore)
;; Sent by cfamily for analysis of C/C++ files. Sonar requires your
;; build commands specified in a compile_commands.json
("sonarlint/needCompilationDatabase" (lambda(&rest _)
(warn "Sonar could not find your compile_commands.json, please check `lsp-sonarlint-cfamily-compile-commands-path'")))
("sonarlint/needCompilationDatabase" #'lsp-sonarlint--set-compile-commands)
;; This is probably just to raise awareness of the new kind of issues:
;; secrets. That'd be too booring to implement. Hopefully, the user is
;; paying attention and will notice anyway.
Expand All @@ -398,18 +427,19 @@ See NOTIFICATION-HANDLERS in lsp--client in lsp-mode.")
:priority -1
:request-handlers lsp-sonarlint--request-handlers
:notification-handlers lsp-sonarlint--notification-handlers
:multi-root t
:multi-root nil
:add-on? t
:server-id 'sonarlint
:action-handlers (ht<-alist lsp-sonarlint--action-handlers)
:initialization-options (lambda ()
(list
:productKey "emacs"
:productName "Emacs"))
(list
:productKey "emacs"
:productName "Emacs"))
:initialized-fn (lambda (workspace)
(with-lsp-workspace workspace
(lsp--set-configuration
(lsp-configuration-section "sonarlint"))))))
(lsp-configuration-section "sonarlint"))))
:synchronize-sections '("sonarlint")))

(provide 'lsp-sonarlint)
;;; lsp-sonarlint.el ends here
53 changes: 53 additions & 0 deletions test/lsp-sonarlint-cpp-test.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
;;; lsp-sonarlint-cpp-test.el --- CFamily analyzer-specific tests for Sonarlint LSP client -*- lexical-binding: t; -*-
;;;
;; Author: Arseniy Zaostrovnykh
;; Created: 11 July 2023
;; License: GPL-3.0-or-later
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;; Tests for the C++-specific integration of the LSP mode and SonarLint language server.

;;; Code:

(require 'lsp-mode)
(require 'lsp-sonarlint)
(load-file (expand-file-name "lsp-sonarlint-test-utils.el"
(file-name-directory (or load-file-name (buffer-file-name)))))

(defun lsp-sonarlint--get-compdb-for-sample-file (cpp-file)
"Relative to fixtures folder, get compile_commands.json for CPP-FILE."
(let ((buf (find-file-noselect (lsp-sonarlint-sample-file cpp-file))))
(with-current-buffer buf
(file-relative-name (lsp-sonarlint--get-compile-commands)
(lsp-sonarlint-fixtures-dir)))))

(ert-deftest lsp-sonarlint--get-compile-commands-test ()
(should (equal (lsp-sonarlint--get-compdb-for-sample-file "compdb/dir1/1.cpp")
"compdb/dir1/compile_commands.json"))
(should (equal (lsp-sonarlint--get-compdb-for-sample-file "compdb/dir1/dir11/11.cpp")
"compdb/dir1/dir11/compile_commands.json"))
(should (equal (lsp-sonarlint--get-compdb-for-sample-file "compdb/dir1/dir12/12.cpp")
"compdb/dir1/compile_commands.json"))
(should (equal (lsp-sonarlint--get-compdb-for-sample-file "compdb/dir1/dir12/dir121/121.cpp")
"compdb/dir1/compile_commands.json")))

(ert-deftest lsp-sonarlint--get-compile-commands-interactive-test ()
(cl-letf (((symbol-function 'read-file-name) (lambda (_prompt)
(concat (lsp-sonarlint-fixtures-dir) "my_db.json"))))
(should (equal (lsp-sonarlint--get-compdb-for-sample-file "compdb/no-compdb/no-compdb.cpp")
"my_db.json"))))

;;; lsp-sonarlint-cpp-test.el ends here
19 changes: 5 additions & 14 deletions test/lsp-sonarlint-integration-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

(require 'lsp-mode)
(require 'lsp-sonarlint)
(load-file (expand-file-name "lsp-sonarlint-test-utils.el"
(file-name-directory (or load-file-name (buffer-file-name)))))

(ert-deftest lsp-sonarlint-plugin-downloaded ()
"Check whether you have downloaded SonarLint.
Expand Down Expand Up @@ -110,17 +112,6 @@ only works for specific textDocument/didOpen:languageId."
(sort (mapcar (lambda (issue) (gethash "code" issue)) issues) #'string-lessp))


(defun lsp-sonarlint--fixtures-dir ()
"Directory of the test fixtures for these tests."
(concat
(file-name-directory
(directory-file-name (file-name-directory (symbol-file #'lsp-sonarlint--fixtures-dir))))
"fixtures/"))

(defun lsp-sonarlint--sample-file (fname)
"Get the full path of the sample file FNAME."
(concat (lsp-sonarlint--fixtures-dir) fname))

(defun lsp-sonarlint--get-all-issue-codes (sample-filename &optional major-mode)
"Get all SonarLint issue-codes for given SAMPLE-FILENAME.
This functions takes some time to wait for the LSP mode to init
Expand All @@ -129,7 +120,7 @@ MAJOR-MODE specifies the major mode enabled to trigger the analysis.
Some analyzers like cfamily require specific major-modes.
If nil, use python-mode by default."
(lsp-sonarlint--exec-with-diags
(lsp-sonarlint--sample-file sample-filename)
(lsp-sonarlint-sample-file sample-filename)
(lambda (diags)
(lsp-sonarlint--get-codes-of-issues diags))
(if major-mode major-mode 'python-mode)))
Expand Down Expand Up @@ -185,7 +176,7 @@ If nil, use python-mode by default."

(ert-deftest lsp-sonarlint-c++-reports-issues ()
"Check that LSP can get go SonarLint issues for a C++ file."
(should (equal (lsp-sonarlint--get-all-issue-codes "sample.cpp" 'c++-mode)
(should (equal (lsp-sonarlint--get-all-issue-codes "cpp/sample.cpp" 'c++-mode)
'("cpp:S995"))))

(defun lsp-sonarlint--find-descr-action-at-point ()
Expand Down Expand Up @@ -217,7 +208,7 @@ If nil, use python-mode by default."
(ert-deftest lsp-sonarlint-display-rule-descr-test ()
"Check whether you can display rule description for a SonarLint issue."
(lsp-sonarlint--exec-with-diags
(lsp-sonarlint--sample-file "sample.py")
(lsp-sonarlint-sample-file "sample.py")
(lambda (diags)
(lsp-sonarlint--go-to-first-diag diags)
(let ((descr-action (lsp-sonarlint--find-descr-action-at-point)))
Expand Down
38 changes: 38 additions & 0 deletions test/lsp-sonarlint-test-utils.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
;;; integration.el --- Integration tests for Sonarlint LSP client -*- lexical-binding: t; -*-
;;;
;; Author: Arseniy Zaostrovnykh
;; Created: 11 July 2023
;; License: GPL-3.0-or-later
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;; Utility functions for the LSP SonarLint tests.

;;; Code:

(defun lsp-sonarlint-fixtures-dir ()
"Directory of the test fixtures for these tests."
(concat
(file-name-directory
(directory-file-name (file-name-directory (symbol-file #'lsp-sonarlint-fixtures-dir))))
"fixtures/"))

(defun lsp-sonarlint-sample-file (fname)
"Get the full path of the sample file FNAME."
(concat (lsp-sonarlint-fixtures-dir) fname))

(provide 'lsp-sonarlint-test-utils)

;;; lsp-sonarlint-test-utils.el ends here

0 comments on commit d5e0d2d

Please sign in to comment.