Skip to content

[Compiler] Fix IndexOutOfRangeException when analyzers and .editorconfig are used - #2098

Merged
RobertvanderHulst merged 1 commit into
X-Sharp:devfrom
hpetriffer:fix/analyzerconfig-generated-trees-ioore
Oct 9, 2026
Merged

RobertvanderHulst merged 1 commit into
X-Sharp:devfrom
hpetriffer:fix/analyzerconfig-generated-trees-ioore

Conversation

@hpetriffer

Copy link
Copy Markdown
Contributor

With /analyzer and /analyzerconfig, xsc crashes with an unhandled IndexOutOfRangeException for VO-dialect code (and the other runtime dialects) that contains a literal symbol, a PSZ literal or a partial type. MSBuild only reports:

XSharp.Core.targets(100,5): error MSB4181: The "Xsc" task returned false but did not log an error.

The real error is only in the task output:

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at CommonCompiler.UpdateAnalyzerConfigOptionsProvider(...) in CommonCompiler.cs:line 1042
   at CommonCompiler.GetCompilerAnalyzerConfigOptionsProvider(...)
   at CommonCompiler.CompileAndEmit(...)

Found while building a large VO library that referenced an analyzer NuGet package; the repo-level .editorconfig made MSBuild pass /analyzerconfig. SkipAnalyzers=true does not remove a PackageReference analyzer.

Cause

UpdateAnalyzerConfigOptionsProvider reads sourceFileAnalyzerConfigOptions[i] for the i-th tree in compilation.SyntaxTrees. The array has one entry per source file plus one placeholder, generatedcode.prg, added in CommonCompiler.RunCore (0f8a184, 2024). A compilation can contain two generated trees:

  1. the tree from XSharpLanguageParser.ProcessTrees (CSharpCompiler.CreateCompilation), created when there are literal symbols, PSZ literals or partial types;
  2. the default tree from AddXSharpSyntaxTree (CSharpCompilation.AddSyntaxTrees), added for the runtime dialects.

With both, there is one tree more than array entries. The code only runs when at least one analyzer or generator is loaded and at least one analyzer config file is passed.

Fix

Inside #if XSHARP, a tree beyond the end of the array uses the last entry, the generatedcode.prg placeholder. All generated trees get the same .editorconfig options, however many there are. CompilerSyntaxTreeOptionsProvider is not affected: it receives the trees before the default tree is added, and the placeholder covers the ProcessTrees tree.

Repro

FUNCTION Start() AS VOID
    LOCAL s AS SYMBOL
    s := #HELLO
    ? s
RETURN
xsc /dialect:VO /r:XSharp.Core.dll /r:XSharp.RT.dll /analyzerconfig:.editorconfig /analyzer:<any X# analyzer>.dll test.prg

The analyzer has to be built against XSharp.CodeAnalysis.dll; Roslyn analyzers such as the WinForms ones are not loaded by xsc. A no-op DiagnosticAnalyzer with [DiagnosticAnalyzer("X#")] is enough. Without the #HELLO line it compiles, because only the default tree is generated.

There is no analyzer in the repo, and the compiler test runner cannot pass one without adding a test analyzer project and a build step to runtests.cmd. I built that locally to verify the fix but did not include it here.

Testing

Based on dev @ 7627782269. Release build via src\buildcompiler.cmd Release (0 warnings, 0 errors), tests via RunCompilertests.cmd.

before after
Repro above IndexOutOfRangeException compiles, prints HELLO
Same, without the symbol literal compiles compiles
Original library (254 files) with its analyzer IndexOutOfRangeException compiles; the analyzer runs and reports its diagnostics
Local regression test (repro in the test runner, not included) crash, exit -532462766 exit 0
Compiler tests (fixed) 25 runtime tests fail the same 25 fail
Compiler tests (broken) 120 / 120 as expected 120 / 120 as expected

The 25 failing runtime tests fail on dev already; the set of failing test IDs is identical before and after.

No existing issue found (searched for IndexOutOfRangeException analyzer, UpdateAnalyzerConfigOptionsProvider, editorconfig analyzer crash).

🤖 Generated with Claude Code

…fig are used

UpdateAnalyzerConfigOptionsProvider indexes sourceFileAnalyzerConfigOptions
with the position of each tree in compilation.SyntaxTrees. X# adds one extra
entry ("generatedcode.prg") for compiler generated code, but a compilation can
contain two generated trees: the tree from XSharpLanguageParser.ProcessTrees
(literal symbols, PSZ literals, partial types) and the default tree from
AddXSharpSyntaxTree (runtime dialects). With /analyzer and /analyzerconfig
xsc then crashed with an unhandled IndexOutOfRangeException, which MSBuild
reports as MSB4181 "Xsc returned false but did not log an error".

Trees beyond the end of the array now use the last entry, the placeholder for
generated code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RobertvanderHulst

Copy link
Copy Markdown
Member

Hansjoerg,
Do you have an example of an analyzer that shows this, so we can add that to our test suite?

@RobertvanderHulst
RobertvanderHulst merged commit d6fa8d3 into X-Sharp:dev Oct 9, 2026
1 check passed
@hpetriffer

Copy link
Copy Markdown
Contributor Author

@RobertvanderHulst I have created a new PR

@cpyrgas

cpyrgas commented Oct 9, 2026

Copy link
Copy Markdown

@hpetriffer Which runtime tests are failing? Since the list is long, just please name a few so I get an idea. Some weeks ago I had made sure that all tests pass, might have missed something.

@hpetriffer

Copy link
Copy Markdown
Contributor Author

@cpyrgas
On dev (d6fa8d3001), 22 runtime tests fail, not 25. The 25 were from the run on 06.10. Since then C703, C901 and R832 pass. The 120 "broken" tests all behave as expected.

19 of the 22 never get as far as running, because they don't compile. The summary still says Compiler Problem: 0, and I didn't look into why the runner doesn't count these compile errors.

Why most of them fail silently: the runner (RuntimeTests.prg, DoTest) loads the .exe outside its inner TRY. If the .exe doesn't exist, the outer CATCH swallows the exception. The log then only shows Running test X / Failed Runtime Test X, with no reason.

Cause Tests Explanation
Test runtime too old C728, C765, C813, C832, C850, C913, C965, C973, C976, C979, C980, C984, R742, R751, R788, R829, R868 (17) Since the #2083 fix (bcd5879955, 25.09.), the compiler calls the runtime function __HasLocals() for FoxPro code. It exists in the source (XSharp.RT/Functions/CompilerSupport.prg:512). But the DLLs in src\CompilerTests\Runtime are version 3.0.1.1 from 15.09. and don't contain it. Result: internal compiler error XS9999: Method __HasLocals not found in type Functions, no .exe.
Depends on Windows language C515 Compares Windows system messages with English text. German Windows returns "Der Vorgang wurde erfolgreich beendet.". Not a compiler bug.
Only fails inside the runner C914 Runs cleanly on its own (macro compiler Str() in the FoxPro dialect). Inside the runner it throws a NullReferenceException in Start(). Probably because all tests share one process and DoTest doesn't set the dialect (there's a // todo: set the correct dialect in the code).
Real wrong result C978 /fox3 with COUNT TO n FOR this.expfld == FLD returns 0 instead of 2 (issue #2054, closed). It failed the same way with the compiler from 24.09., so it's older than the recent changes. The test runtime already has the runtime part of the #2054 fix. Finding the cause would need a bisect.
Compile error C837 XS0266: Cannot implicitly convert type 'array' to 'foxarray' at lines 30 and 39 (SCATTER TO aNewRec ...). It already failed on 06.10. I didn't investigate the cause further.
Warning treated as error R915 Compiled with /warnaserror+. Two warnings break it: XS0219 (oObj assigned but never used) and XS9108 (:Text without a WITH block in GetText()).

What would fix most of them: updating the runtime DLLs in src\CompilerTests\Runtime to a build that contains #2083 (a4f47d4cef). That fixes 17 of the 22, assuming the FoxPro tests have no other problems hidden behind that error. The CI runs against the same folder, so it should show the same failures.

RobertvanderHulst pushed a commit that referenced this pull request Oct 11, 2026
…d in #2098 (#2099)

C990 compiles VO code with a literal symbol together with /analyzer and
/analyzerconfig. That combination produced two compiler generated syntax
trees and crashed xsc with an IndexOutOfRangeException before #2098.

The test needs an analyzer that xsc can load, so this adds a small no-op
analyzer (src/CompilerTests/Analyzers/XSharpTestAnalyzer) that is built
against the XSharp.CodeAnalysis.dll of the tested compiler. runtests.cmd
builds it into Bin\Analyzers before the tests run.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@cpyrgas

cpyrgas commented Oct 11, 2026

Copy link
Copy Markdown

Thanks @hpetriffer , I have adjusted a couple tests. But for me the tests C703, C901 and R832 still do not pass. Also a few other tests still fail with the updated runtime, I think they were broken after some other fixes in the last weeks. Since it's work in progress, I did not update the runtime dlls yet, will do that when they are fixed.

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.

3 participants