Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not throw exceptions for unsupported charsets #827

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

oleosterhagen
Copy link

When there is an XML file in the workspace with a character set which is not supported by the JVM by default, the following exception will be thrown and the initialization of the filesystem is terminated.

Feb. 22, 2025 6:28:47 PM org.sonarsource.sonarlint.shaded.org.eclipse.lsp4j.jsonrpc.RemoteEndpoint fallbackResponseError
SCHWERWIEGEND: Internal error: java.nio.charset.UnsupportedCharsetException: foo-bar
java.util.concurrent.CompletionException: java.nio.charset.UnsupportedCharsetException: foo-bar
	at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
	at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:649)
	at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.nio.charset.UnsupportedCharsetException: foo-bar
	at java.base/java.nio.charset.Charset.forName(Charset.java:542)
	at org.sonarlint.eclipse.core.internal.resources.DefaultSonarLintFileAdapter.getCharset(DefaultSonarLintFileAdapter.java:88)
	at org.sonarlint.eclipse.core.internal.backend.FileSystemSynchronizer.toFileDto(FileSystemSynchronizer.java:295)
	at org.sonarlint.eclipse.core.internal.backend.SonarLintEclipseHeadlessRpcClient.lambda$0(SonarLintEclipseHeadlessRpcClient.java:89)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.sonarlint.eclipse.core.internal.backend.SonarLintEclipseHeadlessRpcClient.listFiles(SonarLintEclipseHeadlessRpcClient.java:91)
	at org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientImpl.lambda$listFiles$31(SonarLintRpcClientImpl.java:348)
	at org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientImpl.lambda$requestAsync$1(SonarLintRpcClientImpl.java:124)
	at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646)
	... 4 more

Later during the analysis, some files may not be found, because the above exception has stopped the initialization of the filesystem.

SonarLint processing file /test1/src/test1/Main1.java...
Git Repository not found for /home/ole/data/runtime-New_configuration/test1. The path /home/ole/data/runtime-New_configuration/test1 is not in a Git repository
File to analyze was not found in the file system: file:/home/ole/data/runtime-New_configuration/test1/src/test1/Main1.java
Triggering analysis with configuration: [
  baseDir: /home/ole/data/runtime-New_configuration/test1
  extraProperties: {sonar.java.target=21, sonar.java.libraries=/usr/lib/jvm/java-21-openjdk/lib/jrt-fs.jar, sonar.java.enablePreview=false, sonar.java.source=21, sonar.java.binaries=/home/ole/data/runtime-New_configuration/test1/bin, sonar.java.test.binaries=, sonar.java.test.libraries=/usr/lib/jvm/java-21-openjdk/lib/jrt-fs.jar,/home/ole/data/runtime-New_configuration/test1/bin}
  activeRules: [214 python, 24 css, 486 java, 46 Web, 14 xml, 155 php, 267 typescript, 29 secrets, 265 javascript]
  inputFiles: [
  ]
]

No file to analyze

A character set in an XML file which is not supported by the JVM by
default should not terminate the initialization of the file system.
@thahnen thahnen self-assigned this Feb 26, 2025
@thahnen
Copy link
Member

thahnen commented Feb 26, 2025

Hi @oleosterhagen,

thank you for opening the PR. We will discuss it internally before we proceed on this.

Have you tested your changes with files that have a unsupported charset? What happens if you now analyze such a file with a unsupported charset? What happens if you do a full project analysis? What happens in general when you "use" it.

I'd like to be sure that when these changes are added, no unforeseen other errors are popping up along the road.

@oleosterhagen
Copy link
Author

Hi @thahnen,

thank you for your reply and questions.

In my fix I chose the same fallback value Charset.defaultCharset() as already done in the case of CoreException:

} catch (CoreException e) {
SonarLintLogger.get().error("Unable to determine charset of file " + file, e);
return Charset.defaultCharset();
}

Normally the default charset will be UTF-8.

Later when analyzing the file, the content will be read with this charset (see: https://github.com/SonarSource/sonarlint-core/blob/83448ab386d7fea28960f8ddea2d2690f65e62b7/backend/core/src/main/java/org/sonarsource/sonarlint/core/fs/ClientFile.java#L115-L120):

    var charsetToUse = charset != null ? charset : Charset.defaultCharset();
    try {
      return Files.readString(fsPath, charsetToUse);
    } catch (IOException e) {
      throw new IllegalStateException("Unable to read file " + fsPath + "content with charset " + charsetToUse, e);
    }

When the analyzed file happens to contain an illegal UTF-8 sequence like 0xff, then the exception handler will be executed. When analyzing a single file only an error but no stacktrace will be shown in the console:

Analyzing all except non binary files
Unable to analyze file [uri=file:/home/ole/data/runtime-New_configuration/test1/test2.xml]: Unable to read file /home/ole/data/runtime-New_configuration/test1/test2.xmlcontent with charset UTF-8
Analysis detected 0 issues and 0 Security Hotspots in 2987ms
Found 0 issue(s) on project 'test1'

When analyzing the whole project a stacktrace is displayed:

Error executing sensor: 'TextAndSecretsSensor'

java.lang.IllegalStateException: Unable to read file /home/ole/data/runtime-New_configuration/test1/test2.xmlcontent with charset UTF-8
	at org.sonarsource.sonarlint.core.fs.ClientFile.getContent(ClientFile.java:119)
	at org.sonarsource.sonarlint.core.analysis.BackendInputFile.inputStream(BackendInputFile.java:64)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.filesystem.SonarLintInputFile.inputStream(SonarLintInputFile.java:133)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.filesystem.InputFileBuilder.lambda$create$0(InputFileBuilder.java:48)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.filesystem.SonarLintInputFile.checkMetadata(SonarLintInputFile.java:65)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.filesystem.SonarLintInputFile.lines(SonarLintInputFile.java:209)
	at org.sonar.plugins.common.Analyzer.lambda$analyzeFiles$0(Analyzer.java:59)
	at java.base/java.util.Comparator.lambda$comparingInt$7b0bb60$1(Unknown Source)
	at java.base/java.util.TimSort.countRunAndMakeAscending(Unknown Source)
	at java.base/java.util.TimSort.sort(Unknown Source)
	at java.base/java.util.Arrays.sort(Unknown Source)
	at java.base/java.util.ArrayList.sort(Unknown Source)
	at org.sonar.plugins.common.Analyzer.analyzeFiles(Analyzer.java:59)
	at org.sonar.plugins.common.TextAndSecretsSensor.lambda$execute$1(TextAndSecretsSensor.java:125)
	at org.sonar.plugins.common.DurationStatistics.lambda$timed$0(DurationStatistics.java:63)
	at org.sonar.plugins.common.DurationStatistics.timed(DurationStatistics.java:75)
	at org.sonar.plugins.common.DurationStatistics.timed(DurationStatistics.java:62)
	at org.sonar.plugins.common.TextAndSecretsSensor.execute(TextAndSecretsSensor.java:125)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.sensor.SensorsExecutor.executeSensor(SensorsExecutor.java:101)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.sensor.SensorsExecutor.executeSensors(SensorsExecutor.java:91)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.sensor.SensorsExecutor.execute(SensorsExecutor.java:82)
	at org.sonarsource.sonarlint.core.analysis.container.analysis.AnalysisContainer.doAfterStart(AnalysisContainer.java:122)
	at org.sonarsource.sonarlint.core.plugin.commons.container.SpringComponentContainer.startComponents(SpringComponentContainer.java:180)
	at org.sonarsource.sonarlint.core.plugin.commons.container.SpringComponentContainer.execute(SpringComponentContainer.java:159)
	at org.sonarsource.sonarlint.core.analysis.container.module.ModuleContainer.analyze(ModuleContainer.java:75)
	at org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand.doRunAnalysis(AnalyzeCommand.java:80)
	at org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand.lambda$execute$0(AnalyzeCommand.java:61)
	at org.sonarsource.sonarlint.core.progress.RpcProgressMonitor.lambda$startTask$0(RpcProgressMonitor.java:44)
	at org.sonarsource.sonarlint.core.progress.TaskManager.startTask(TaskManager.java:64)
	at org.sonarsource.sonarlint.core.progress.RpcProgressMonitor.startTask(RpcProgressMonitor.java:44)
	at org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand.execute(AnalyzeCommand.java:60)
	at org.sonarsource.sonarlint.core.analysis.command.AnalyzeCommand.execute(AnalyzeCommand.java:38)
	at org.sonarsource.sonarlint.core.analysis.AnalysisEngine$AsyncCommand.execute(AnalysisEngine.java:147)
	at org.sonarsource.sonarlint.core.analysis.AnalysisEngine.executeQueuedCommands(AnalysisEngine.java:71)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.nio.charset.MalformedInputException: Input length = 1
	at java.base/java.lang.String.throwMalformed(Unknown Source)
	at java.base/java.lang.String.decodeUTF8_UTF16(Unknown Source)
	at java.base/java.lang.String.newStringUTF8NoRepl(Unknown Source)
	at java.base/java.lang.String.newStringNoRepl1(Unknown Source)
	at java.base/java.lang.String.newStringNoRepl(Unknown Source)
	at java.base/java.lang.System$2.newStringNoRepl(Unknown Source)
	at java.base/java.nio.file.Files.readString(Unknown Source)
	at org.sonarsource.sonarlint.core.fs.ClientFile.getContent(ClientFile.java:117)
	... 34 more

There may be other ways or places to skip files with unsupported charsets, e.g. catching the UnsupportedCharsetException in SonarLintEclipseHeadlessRpcClient.listFiles(String):

var files = new ArrayList<>(project.files().stream()
.map(slFile -> FileSystemSynchronizer.toFileDto(slFile, new NullProgressMonitor()))
.filter(Objects::nonNull)
.collect(Collectors.toList()));

What do you suggest?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants