Skip to content

Feature/devin 20260712 sonar quality#80

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

Feature/devin 20260712 sonar quality#80
afonsoft merged 10 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?

New Feature Submissions:

  1. Does your submission pass tests?
  2. Have you lint your code locally prior to submission?

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 written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Open in Devin Review

devin-ai-integration Bot and others added 10 commits July 12, 2026 04:22
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>
…kflows-plus

fix(ci): corrigir workflows legados
…plexity-generators

fix: reduzir complexidade do gerador e payload
@afonsoft
afonsoft merged commit 845a5d7 into main Jul 12, 2026
11 of 14 checks passed
@afonsoft
afonsoft deleted the feature/devin-20260712-sonar-quality branch July 12, 2026 16:40
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Qodana Community for .NET

1251 new problems were found

Inspection name Severity Problems
Inconsistent Naming 🔶 Warning 550
Redundant using directive 🔶 Warning 28
Member initialized value ignored 🔶 Warning 13
Assignment is not used 🔶 Warning 8
Do not use object initializer for 'using' variable: Do not use object initializer for 'using' variable 🔶 Warning 8
Possible 'System.NullReferenceException' 🔶 Warning 7
Redundant name qualifier 🔶 Warning 6
Auto-property accessor is never used: Private accessibility 🔶 Warning 5
Possible multiple enumeration 🔶 Warning 4
Redundant explicit type in array creation 🔶 Warning 4
Specify string culture explicitly 🔶 Warning 4
Auto-property accessor is never used: Non-private accessibility 🔶 Warning 4
Possible loss of fraction 🔶 Warning 3
Redundant argument with default value 🔶 Warning 3
Redundant cast 🔶 Warning 3
Expression is always 'true' or always 'false' 🔶 Warning 2
Unused parameter: Private accessibility 🔶 Warning 2
Underlying type of enum is 'int' 🔶 Warning 1
Member hides static member from outer class 🔶 Warning 1
Non-accessed field: Private accessibility 🔶 Warning 1
Possible 'System.InvalidOperationException' 🔶 Warning 1
Redundant explicit size specification in array creation 🔶 Warning 1
Redundant 'switch' expression arms 🔶 Warning 1
Redundant 'object.ToString()' call 🔶 Warning 1
Type member is never used: Private accessibility 🔶 Warning 1
Unused local variable 🔶 Warning 1
Type member is never used: Non-private accessibility ◽️ Notice 364
Use preferred body style: Convert into property, indexer, or event with preferred body style ◽️ Notice 38
Type is never used: Non-private accessibility ◽️ Notice 32
Convert into 'using' declaration ◽️ Notice 30
Member can be made private: Non-private accessibility ◽️ Notice 28
Redundant verbatim string prefix ◽️ Notice 22
Use preferred style of 'new' expression when created type is evident ◽️ Notice 12
Auto-property can be made get-only: Non-private accessibility ◽️ Notice 12
Field can be made readonly: Private accessibility ◽️ Notice 11
Merge null/pattern checks into complex pattern ◽️ Notice 9
Class is never instantiated: Non-private accessibility ◽️ Notice 4
Merge conditional ?: expression into conditional access ◽️ Notice 4
Use index from end expression ◽️ Notice 3
Auto-property can be made get-only: Private accessibility ◽️ Notice 2
Check for reference equality instead: Check for reference equality instead ◽️ Notice 2
Convert property into auto-property ◽️ Notice 2
For-loop can be converted into foreach-loop ◽️ Notice 2
Redundant string interpolation ◽️ Notice 2
Incorrect indent: Around statement braces ◽️ Notice 1
Class with virtual (overridable) members never inherited: Non-private accessibility ◽️ Notice 1
Convert lambda expression into method group ◽️ Notice 1
Convert constructor into member initializers ◽️ Notice 1
Introduce optional parameters: Non-private accessibility ◽️ Notice 1
Member can be made protected: Non-private accessibility ◽️ Notice 1
Redundant explicit collection creation in argument of 'params' parameter ◽️ Notice 1
Use string interpolation expression ◽️ Notice 1
Virtual (overridable) member is never overridden: Non-private accessibility ◽️ Notice 1

💡 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

resPoly = XORPolynoms(leadTermSource, resPoly);
leadTermSource = resPoly;
var blackModules = qrCode.ModuleMatrix.Sum(row => row.Cast<bool>().Count(x => x));
var percent = ((double)blackModules / (size * size)) * 100;
Comment on lines +1080 to +1086
}

private static bool IsInRange(char c, char min, char max)
{
return (uint)(c - min) <= (uint)(max - min);
}

@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 +495 to 520
for (var x = size - 1; x >= 0; x = x - 2)
{
var currentX = x == 6 ? 5 : x;
if (x == 6)
x = 5;
for (var yMod = 1; yMod <= size; yMod++)
{
int y;
if (up)
{
y = size - yMod;
if (datawords.Count > 0 && !IsBlocked(new SKRectI(currentX, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][currentX] = datawords.Dequeue();
if (datawords.Count > 0 && currentX > 0 && !IsBlocked(new SKRectI(currentX - 1, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][currentX - 1] = datawords.Dequeue();
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
{
y = yMod - 1;
if (datawords.Count > 0 && !IsBlocked(new SKRectI(currentX, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][currentX] = datawords.Dequeue();
if (datawords.Count > 0 && currentX > 0 && !IsBlocked(new SKRectI(currentX - 1, y, 1, 1), blockedModules))
qrCode.ModuleMatrix[y][currentX - 1] = datawords.Dequeue();
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();
}
}
up = !up;
x = currentX - 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 loop refactoring requires careful equivalence verification

The old code used a while loop with a currentX variable to skip column 6 (the timing pattern column). When x == 6, currentX was set to 5, and then x = currentX - 2 = 3. The new code at QRCoder.Core/Generators/QRCodeGenerator.cs:495-519 uses a for loop with x = x - 2 in the update expression and an if (x == 6) x = 5 guard at the top of the loop body. Both produce the same sequence of column values (e.g., ..., 8, 5, 3, 1) because when x reaches 6, it's immediately adjusted to 5, and then the loop body uses 5. The for-loop update then sets x = 5 - 2 = 3. This is mechanically equivalent but the reasoning is non-obvious — worth a second look to confirm confidence.

Open in Devin Review

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

Comment on lines +725 to +746
private static int ScoreFinderPatterns(QRCodeData qrCode, int size)
{
var score = 0;
for (var y = 0; y < size; y++)
{
for (var x = 0; x < size - 10; x++)
{
if ((qrCode.ModuleMatrix[y][x] &&
!qrCode.ModuleMatrix[y][x + 1] &&
qrCode.ModuleMatrix[y][x + 2] &&
qrCode.ModuleMatrix[y][x + 3] &&
qrCode.ModuleMatrix[y][x + 4] &&
!qrCode.ModuleMatrix[y][x + 5] &&
qrCode.ModuleMatrix[y][x + 6] &&
!qrCode.ModuleMatrix[y][x + 7] &&
!qrCode.ModuleMatrix[y][x + 8] &&
!qrCode.ModuleMatrix[y][x + 9] &&
!qrCode.ModuleMatrix[y][x + 10]) ||
(!qrCode.ModuleMatrix[y][x] &&
!qrCode.ModuleMatrix[y][x + 1] &&
!qrCode.ModuleMatrix[y][x + 2] &&
!qrCode.ModuleMatrix[y][x + 3] &&
qrCode.ModuleMatrix[y][x + 4] &&
!qrCode.ModuleMatrix[y][x + 5] &&
qrCode.ModuleMatrix[y][x + 6] &&
qrCode.ModuleMatrix[y][x + 7] &&
qrCode.ModuleMatrix[y][x + 8] &&
!qrCode.ModuleMatrix[y][x + 9] &&
qrCode.ModuleMatrix[y][x + 10]))
{
score3 += 40;
}

if ((qrCode.ModuleMatrix[x][y] &&
!qrCode.ModuleMatrix[x + 1][y] &&
qrCode.ModuleMatrix[x + 2][y] &&
qrCode.ModuleMatrix[x + 3][y] &&
qrCode.ModuleMatrix[x + 4][y] &&
!qrCode.ModuleMatrix[x + 5][y] &&
qrCode.ModuleMatrix[x + 6][y] &&
!qrCode.ModuleMatrix[x + 7][y] &&
!qrCode.ModuleMatrix[x + 8][y] &&
!qrCode.ModuleMatrix[x + 9][y] &&
!qrCode.ModuleMatrix[x + 10][y]) ||
(!qrCode.ModuleMatrix[x][y] &&
!qrCode.ModuleMatrix[x + 1][y] &&
!qrCode.ModuleMatrix[x + 2][y] &&
!qrCode.ModuleMatrix[x + 3][y] &&
qrCode.ModuleMatrix[x + 4][y] &&
!qrCode.ModuleMatrix[x + 5][y] &&
qrCode.ModuleMatrix[x + 6][y] &&
qrCode.ModuleMatrix[x + 7][y] &&
qrCode.ModuleMatrix[x + 8][y] &&
!qrCode.ModuleMatrix[x + 9][y] &&
qrCode.ModuleMatrix[x + 10][y]))
{
score3 += 40;
}
if (MatchesFinderPattern(qrCode, x, y))
score += 40;
}
}

//Penalty 4
double blackModules = 0;
foreach (var row in qrCode.ModuleMatrix)
foreach (bool bit in row)
if (bit)
blackModules++;

var percent = (blackModules / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count)) * 100;
var prevMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 50) / 5;
var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 45) / 5;
score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10;
for (var x = 0; x < size - 10; x++)
{
for (var y = 0; y < size; y++)
{
if (MatchesFinderPattern(qrCode, x, y, vertical: true))
score += 40;
}
}

return score1 + score2 + score3 + score4;
return score;

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: ScoreFinderPatterns splits single loop into two separate loops

The old code checked both horizontal and vertical finder-like patterns in a single nested loop (y: 0..size, x: 0..size-10). The new code at QRCoder.Core/Generators/QRCodeGenerator.cs:725-746 splits this into two separate double-loops: one for horizontal (y outer, x inner with x < size-10) and one for vertical (x outer with x < size-10, y inner with y < size). Both cover the same set of (x, y) pairs, so the total score is identical. The split improves readability but the reviewer should note that the iteration order change is intentional and correct — the score is a simple sum so order doesn't matter.

Open in Devin Review

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

Comment on lines +2040 to +2044
AppendParameter(payload, "separeference", Uri.EscapeDataString(sepaReference));
if (authority == AuthorityType.singledirectdebitsepa)
{
bezahlCodePayload += $"account={account}&";
bezahlCodePayload += $"bnc={bnc}&";
if (postingKey > 0)
bezahlCodePayload += $"postingkey={postingKey}&";
AppendParameter(payload, "creditorid", Uri.EscapeDataString(creditorId));
AppendParameter(payload, "mandateid", Uri.EscapeDataString(mandateId));

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: Null guards removed before Uri.EscapeDataString calls in BezahlCode

The old code explicitly checked !string.IsNullOrEmpty(sepaReference), !string.IsNullOrEmpty(creditorId), and !string.IsNullOrEmpty(mandateId) before calling Uri.EscapeDataString(). The new code at QRCoder.Core/Generators/PayloadGenerator.cs:2040-2044 calls Uri.EscapeDataString() unconditionally and relies on AppendParameter to filter empty results. This is safe because: (1) these parameters default to "" in the constructor, (2) the SEPA branch validates them (e.g., sepaReference.Length would NPE on null), and (3) Uri.EscapeDataString("") returns "" which AppendParameter filters out. However, the implicit contract has changed — the old code was defensive against null, while the new code assumes non-null.

Open in Devin Review

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

Comment on lines +353 to +358
private static string ReverseString(string inp)
{
var sb = new StringBuilder(inp.Length);
for (var i = inp.Length - 1; i >= 0; i--)
sb.Append(inp[i]);
return sb.ToString();

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.

🔍 Several methods moved outside ModulePlacer nested class with inconsistent indentation

The methods ReverseString (QRCoder.Core/Generators/QRCodeGenerator.cs:353-358), PlaceAlignmentPatterns (QRCoder.Core/Generators/QRCodeGenerator.cs:581-601), and IsBlocked (QRCoder.Core/Generators/QRCodeGenerator.cs:625-628) appear to have been moved from inside the ModulePlacer nested static class to the enclosing QRCodeGenerator class scope (based on their reduced indentation level). However, other methods like PlaceVersion, PlaceFormat, PlaceFinderPatterns, etc. remain inside ModulePlacer with their original indentation. This creates an inconsistent code organization where some module-placement helpers are inside ModulePlacer and others are outside it. The code still compiles and works correctly since nested classes can access private members of enclosing classes, but the inconsistency may cause confusion.

Open in Devin Review

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

Comment on lines 1550 to 1553
public void Dispose()
{
if (this.disposed)
{
return;
}

this.disposed = true;
GC.SuppressFinalize(this);
}

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 changed from idempotent guard to GC.SuppressFinalize without finalizer

The old Dispose() method used a disposed boolean field to guard against double-disposal. The new implementation at QRCoder.Core/Generators/QRCodeGenerator.cs:1550-1553 replaces this with a single GC.SuppressFinalize(this) call. Since QRCodeGenerator has no finalizer and no unmanaged resources, calling GC.SuppressFinalize is unnecessary (but harmless). The class implements IDisposable purely for backward compatibility as noted in the XML comment. The disposed field was also removed from the class. This is a minor style concern — GC.SuppressFinalize without a finalizer is a no-op that may confuse readers into thinking there's a finalizer somewhere.

Open in Devin Review

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

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