Skip to content

fix: consolidate SonarCloud cleanup into main#83

Merged
afonsoft merged 22 commits into
mainfrom
feature/devin-20260712-sonar-quality
Jul 12, 2026
Merged

fix: consolidate SonarCloud cleanup into main#83
afonsoft merged 22 commits into
mainfrom
feature/devin-20260712-sonar-quality

Conversation

@afonsoft

@afonsoft afonsoft commented Jul 12, 2026

Copy link
Copy Markdown
Owner

All Submissions:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you successfully ran tests with your changes locally?

Summary

This is the consolidation PR from the feature/devin-20260712-sonar-quality integration branch into main. It contains the SonarCloud cleanup work executed in phases during this session.

Phases merged into the integration branch:

  • Engine and data model (#81)
  • PayloadGenerator (#82)
  • Core renderers and extensions (#84)
  • Remaining renderers (#85)
  • SKPaint/SKBitmap leak fixes (#86)
  • S2259 null guard (#87)
  • ArtQRCode background image scaling and disposal (#88)

Verification:

  • dotnet build QRCoder.Core.sln --configuration Release passes for all target frameworks.
  • dotnet test QRCoder.Core.Tests/ -f net10.0 --configuration Release passes (502 tests).
  • dotnet test QRCoder.Core.Tests/ -f net8.0 --configuration Release passes (502 tests).
  • net48 tests do not run on Linux due to missing mono.

Public APIs are preserved; obsolete overloads are kept with [Obsolete] and [SuppressMessage] where needed.

Link to Devin session: https://app.devin.ai/sessions/20c92a1bad724f63b8be89a9e195e664
Requested by: @afonsoft


Open in Devin Review

devin-ai-integration Bot and others added 8 commits July 12, 2026 17:26
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
…load

fix: resolve SonarCloud issues in PayloadGenerator
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Qodana Community for .NET

It seems all right 👌

No new problems were found according to the checks applied

💡 Qodana analysis was run in the pull request mode: only the changed files were checked

View the detailed Qodana report

To be able to view the detailed Qodana report, you can either:

To get *.log files or any other Qodana artifacts, run the action with upload-result option set to true,
so that the action will upload the files as the job artifacts:

      - name: 'Qodana Scan'
        uses: JetBrains/qodana-action@v2026.1.3
        with:
          upload-result: true
Contact Qodana team

Contact us at qodana-support@jetbrains.com

Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 potential issues.

Open in Devin Review

Comment on lines +480 to 486
for (var y = 0; y < x; y++)
{
for (var y = 0; y < x; y++)
if (!IsBlocked(new SKRectI(x, y, 1, 1), blockedModules))
{
if (!IsBlocked(new SKRectI(x, y, 1, 1), blockedModules))
{
qrCode.ModuleMatrix[y][x] ^= methods[selectedPattern.Value](x, y);
qrCode.ModuleMatrix[x][y] ^= methods[selectedPattern.Value](y, x);
}
qrCode.ModuleMatrix[y][x] ^= pattern(x, y);
qrCode.ModuleMatrix[x][y] ^= pattern(y, x);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: ApplyMask shares a single IsBlocked check for two symmetric module positions

The extracted ApplyMask method at QRCoder.Core/Generators/QRCodeGenerator.cs:476-493 faithfully preserves a pre-existing behavior: when y < x, a single IsBlocked(new SKRectI(x, y, 1, 1), blockedModules) check gates XOR operations on both ModuleMatrix[y][x] and ModuleMatrix[x][y]. This means position (y, x) (the transposed cell) is never independently checked against blockedModules. If (x, y) is blocked but (y, x) is not, neither gets masked. If (x, y) is unblocked but (y, x) is blocked, the blocked cell still gets masked. This was identical in the old code and may be intentional (the blocked regions are symmetric in valid QR codes), but it's worth being aware of if the blocking logic ever changes.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +516 to +527
for (var y = yStart; y != yEnd && datawords.Count > 0; y += yStep)
{
int y;
if (up)
{
y = size - yMod;
if (datawords.Count > 0 && !IsBlocked(new SKRectI(x, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][x] = datawords.Dequeue();
if (datawords.Count > 0 && x > 0 && !IsBlocked(new SKRectI(x - 1, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue();
}
else
TryPlaceDataWord(qrCode, x, y, datawords, blockedModules);
if (x > 0)
{
y = yMod - 1;
if (datawords.Count > 0 && !IsBlocked(new SKRectI(x, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][x] = datawords.Dequeue();
if (datawords.Count > 0 && x > 0 && !IsBlocked(new SKRectI(x - 1, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue();
TryPlaceDataWord(qrCode, x - 1, y, datawords, blockedModules);
}
}

up = !up;
x -= 2;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: PlaceDataWords inner loop now exits early when data is exhausted

The refactored PlaceDataWords adds datawords.Count > 0 to the inner for-loop condition at line 516. The old code always iterated all y values for every x column pair, relying on the datawords.Count > 0 guard inside each placement call. The new code exits the y-loop as soon as data runs out. This is functionally equivalent (the old code did nothing with empty data), but it's a minor performance improvement — the loop no longer iterates over remaining y positions once all data bits are placed. The outer x-loop still runs to completion, toggling up and decrementing x, which has no effect since the inner loop will immediately exit.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 1050 to 1060
}
else
{
var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i);
var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], iteration);
resPoly = ConvertToDecNotation(resPoly);
resPoly = XORPolynoms(leadTermSource, resPoly);
leadTermSource = resPoly;
}

iteration++;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: CalculateECCWords iteration counter increments on both branches, matching original for-loop

The conversion from for (var i = 0; ...; i++) to a while loop with a separate iteration variable at QRCoder.Core/Generators/QRCodeGenerator.cs:1042-1060 is correct but has a subtle aspect worth noting: iteration increments on every loop pass (line 1059), including when the if branch (coefficient == 0) is taken and MultiplyGeneratorPolynomByLeadterm is not called. This matches the original for loop where i++ ran unconditionally. The iteration value is only consumed in the else branch as the lowerExponentBy parameter, so skipped iterations effectively increase the exponent reduction on the next non-zero coefficient — the same behavior as before.

(Refers to lines 1042-1060)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +1589 to +1597
protected virtual void Dispose(bool disposing)
{
if (_disposedValue)
{
return;
}

_disposedValue = true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Dispose pattern added but no resources are actually released

The new Dispose(bool disposing) method at lines 1589-1597 implements the standard dispose pattern but the body only sets _disposedValue = true without releasing any resources. The disposing parameter is not even checked. Compare this with QRCodeData.Dispose(bool) at QRCoder.Core/Models/QRCodeData.cs:214-221 which actually nulls out ModuleMatrix. The QRCodeGenerator class holds no instance state (all fields are static), so there's nothing to dispose — this is purely for pattern compliance. However, the disposing parameter should arguably still be checked per the standard pattern, even if the body is empty, to be consistent with QRCodeData's implementation.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +463 to +474
private static QRCodeData CopyMatrix(QRCodeData source, int size)
{
var copy = new QRCodeData(source.Version);
for (var y = 0; y < size; y++)
{
for (var x = 0; x < size; x++)
{
copy.ModuleMatrix[y][x] = source.ModuleMatrix[y][x];
}
}
return copy;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: CopyMatrix uses source.Version instead of the size-derived version parameter

The CopyMatrix helper at line 463-474 constructs the copy via new QRCodeData(source.Version) rather than accepting a separate version parameter. The old inline code in MaskCode used the version parameter passed to MaskCode. These are always equal in practice because PlaceModules creates QRCodeData(version) and passes the same version to MaskCode. Using source.Version is arguably more robust since it's self-consistent with the source data, but it's a semantic change worth noting — if someone ever called MaskCode with a mismatched version, the behavior would differ from the old code.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

…derers-core

fix: resolve SonarCloud issues in core renderers and extensions
Comment thread QRCoder.Core/Renderers/QRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/QRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/QRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/QRCode.cs Fixed
devin-ai-integration Bot and others added 6 commits July 12, 2026 17:47
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
…derers-others

fix: resolve SonarCloud issues in remaining renderers
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
…pose-fix

fix: dispose SKPaint and SKBitmap in renderer icon/dot helpers
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
…l-guard

fix: add null guard in HexSKColorToByteArray to resolve S2259
Comment thread QRCoder.Core/Renderers/ArtQRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/ArtQRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/ArtQRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/ArtQRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/SvgQRCode.cs Fixed
devin-ai-integration Bot and others added 4 commits July 12, 2026 18:05
Resize was creating an empty scaledImage and returning it without
drawing the source image, so the background image was invisible.
Use image.Resize() to scale and dispose the temporary bitmap,
and dispose the resized result in RenderGraphicCore.

Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
…t ratios

Ensure scaled dimensions are at least 1 pixel before calling
image.Resize(). Create the output bitmap only after a successful
resize to prevent leaking it if the resize operation fails.

Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
…ize-fix

fix(ArtQRCode): scale and dispose background image
@devin-ai-integration devin-ai-integration Bot changed the title Feature/devin 20260712 sonar quality fix: consolidate SonarCloud cleanup into main Jul 12, 2026
Comment thread QRCoder.Core/Renderers/ArtQRCode.cs Fixed
Comment thread QRCoder.Core/Renderers/ArtQRCode.cs Fixed
- Cast SKRect/DrawBitmap multiplications to float before evaluation to avoid int overflow.

- Guard nullable ImageAttributes in SvgQRCode.IsBlockedByLogo.

- Update renderer tests to use non-obsolete options overloads.

- Correct PayloadGeneratorTests pragma (CS0612 -> CS0618).

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
PixelsPerModule = 10,
DarkSKColor = SKColors.Black,
LightSKColor = SKColors.Transparent,
Icon = SKBitmap.Decode(System.IO.Path.Combine(HelperFunctions.GetAssemblyPath(), "assets", "noun_software-engineer_2909346.png"))
PixelsPerModule = 10,
DarkSKColor = SKColors.Black,
LightSKColor = SKColors.White,
Icon = SKBitmap.Decode(System.IO.Path.Combine(HelperFunctions.GetAssemblyPath(), "assets", "noun_software-engineer_2909346.png"))
The workflow expects a qodana.sarif.json baseline; without it Qodana compares against an empty report and reports all 1225 pre-existing issues as NEW. This baseline was generated with Qodana Community for .NET 2026.1 against the integration branch and will be used by PRs targeting main after merge.

Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

@afonsoft
afonsoft merged commit b558e32 into main Jul 12, 2026
26 checks passed
@afonsoft
afonsoft deleted the feature/devin-20260712-sonar-quality branch July 13, 2026 01:29
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