برنامه نویسی

نمودار آشفتگی: Scottplot در فروشگاه چه چیزی دارد؟

Scottplot یک کتابخانه در زیر .NET برای ایجاد نمودارها است. کد موجود در چنین پروژه هایی در طبیعت گیج کننده است. امروز ، ما سعی خواهیم کرد که آن را کشف کنیم و با استفاده از آنالایزر استاتیک ، مسائل را پیدا کنیم.

1239_scottplot/image1.png

می توانید در مورد تمام ویژگی های Scottplot در وب سایت پروژه بیاموزید. همچنین نمودار ترسیم شده با استفاده از کتابخانه را نشان می دهد:

شرح تصویر

همانطور که قبلاً ذکر شد ، Scottplot یک کتابخانه است. توسعه دهندگان باید هنگام تدوین چنین راه حل هایی به کیفیت کد توجه ویژه کنند. از این گذشته ، خطاهای موجود در این نوع پروژه ها نه تنها بر کاربران مستقیم کتابخانه بلکه کسانی که از برنامه های نوشته شده با کمک آن استفاده می کنند ، تأثیر می گذارد. این یکی از دلایلی بود که تصمیم گرفتیم Scottplot را با استفاده از آنالایزر استاتیک PVS-Studio بررسی کنیم.

کد بررسی شده با این تعهد مطابقت دارد.

بیایید شروع به شکستن قطعات کد مشکوک کنیم.

ترفندهای بیتی

قطعه کد 1

public static Interactivity.Key GetKey(this Keys keys)
{

  Keys keyCode = keys & ~Keys.Modifiers;                   // <=
  Interactivity.Key key = keyCode switch
  {
    Keys.Alt => Interactivity.StandardKeys.Alt,            // <=
    Keys.Menu => Interactivity.StandardKeys.Alt,
    Keys.Shift => Interactivity.StandardKeys.Shift,        // <=
    Keys.ShiftKey => Interactivity.StandardKeys.Shift,
    Keys.LShiftKey => Interactivity.StandardKeys.Shift,
    Keys.RShiftKey => Interactivity.StandardKeys.Shift,
    Keys.Control => Interactivity.StandardKeys.Control,    // <=
    Keys.ControlKey => Interactivity.StandardKeys.Control,
    Keys.Down => Interactivity.StandardKeys.Down,
    Keys.Up => Interactivity.StandardKeys.Up,
    Keys.Left => Interactivity.StandardKeys.Left,
    Keys.Right => Interactivity.StandardKeys.Right,
    _ => Interactivity.StandardKeys.Unknown,
  };

  ....
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-Studio: کد غیرقابل دستیابی V3202 شناسایی شده است. مقدار “مورد” خارج از محدوده بیان مسابقه است. scottplot.winforms formsplotextension.cs 106

مقادیر چند الگوی در switch در زمینه فعلی غیرممکن هستند. بیایید ببینیم اینجا چه خبر است.

ابتدا باید به مقادیری که مطابق با عناصر نادرست شمارش است ، نگاه کنیم.

[Flags]
[TypeConverter(typeof(KeysConverter))]
[Editor(....)]
public enum Keys
{
  /// 
  ///  The bit mask to extract modifiers from a key value.
  /// 
  Modifiers = unchecked((int)0xFFFF0000),

  ....
  /// 
  ///  The SHIFT modifier key.
  /// 
  Shift = 0x00010000,

  /// 
  ///  The  CTRL modifier key.
  /// 
  Control = 0x00020000,

  /// 
  ///  The ALT modifier key.
  /// 
  Alt = 0x00040000
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بعد ، بیایید آنها را به باینری تبدیل کنیم:

نام مقدار (اعشاری) بازنمایی دودویی
اصلاح کننده ها 0xffff0000 1111 1111 1111 1111 0000 0000 0000 0000
تغییر مکان 0x00010000 0000 0000 0000 0001 0000 0000 0000 0000
کنترل کردن 0x00020000 0000 0000 0000 0010 0000 0000 0000 0000
با صفر 0x00040000 0000 0000 0000 0100 0000 0000 0000 0000

اکنون مشخص است که Modifiers شامل هر یک از عناصر شمارش اشتباه است.

مقدار منتقل شده به switch از keys & ~Keys.Modifiers بیان این عبارت حذف می کند Keys.Modifiers مقدار از keysبشر علاوه بر این Keys.Modifiers، با این حال ، Shiftبا Controlوت Alt از آنجا که مستثنی خواهد بود Modifiers در حال حاضر این مقادیر را شامل می شود (Modifiers یک بیت غیر صفر برای هر بیت غیر صفر از عناصر شمارش اشتباه) داشته باشید.

از همه اینها می توانیم نتیجه بگیریم که ترکیب کمی که تولید می کند Shiftبا Control، یا Alt برای keys & ~Keys.Modifiers عملیات وجود ندارد.

مسئله ممکن است در switch اجرای به جای مقادیر شمارش.

خواص مشکوک

قطعه کد 2

internal double BackAngleSweep
{
  get
  {
    double maxBackAngle = CircularBackground ? 360 : MaximumSizeAngle;
    if (!Clockwise) maxBackAngle = -maxBackAngle;
    return maxBackAngle;
  }

  private set { BackAngleSweep = value; }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-Studio: بازگشت بی نهایت V3110 در داخل خاصیت “backanglesweep”. scottplot radialgauge.cs 45

اجرای تنظیم کننده بسیار غیرمعمول از BackAngleSweep خاصیت اگر از نزدیک نگاه کنیم ، می بینیم که این ملک در تنظیم کننده به خودش اختصاص داده شده است. این منجر به a خواهد شد StackOverflowExceptionبشر

وقتی به کاربردهای این خاصیت نگاه کردم ، متوجه شدم که هیچ ارزشی به آن اختصاص ندارد. این بدان معنی است که تنظیم کننده حتی کاری نمی کند. اگر یک توسعه دهنده بخواهد چیزی بنویسد BackAngleSweep، آنها به ناچار “سرریز پشته” را دریافت می کنند.

قطعه کد 3

public bool Rounded
{
    get => StrokeCap == SKStrokeCap.Round;
    set { StrokeCap = SKStrokeCap.Round; StrokeJoin = SKStrokeJoin.Round; }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-Studio: V3077 تنظیم کننده ویژگی “گرد” از پارامتر “مقدار” خود استفاده نمی کند. Scottplot linestyle.cs 36

چیزی مبهم دوباره در تنظیم کننده املاک در حال تولید است. اختصاص هر مقدار به آن منجر به از دست دادن آن مقدار می شود زیرا برای آن نوشته نشده است value یا در هر جای دیگر ذخیره شده است.

آیا می توانیم مقایسه کنیم؟

قطعه کد 4

public class CoordinateRangeMutable : IEquatable<CoordinateRangeMutable>
{
  ....
  public bool Equals(CoordinateRangeMutable? other)
  {
    if (other is null)
      return false;

    return Equals(Min, other.Min) && Equals(Min, other.Min);  // <=
  }

  public override bool Equals(object? obj)
  {
    if (obj is null)
      return false;

    if (obj is CoordinateRangeMutable other)
      return Equals(other);

    return false;
  }

  public override int GetHashCode()
  {
    return Min.GetHashCode() ^ Max.GetHashCode();             // <=
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدارهای PVS-Studio:

v3192 خاصیت “حداکثر” در روش “gethashcode” استفاده می شود اما از روش “برابر” گم شده است. SCOTTPLOT COMMINATERANGEMINALICE.cs 198

v3001 “موارد زیربناهای” یکسان (حداقل ، دیگر. مین) “در سمت چپ و سمت راست اپراتور” && “وجود دارد. SCOTTPLOT COMMINATERANGEMINALE.CS 172

آنالایزر برای این قطعه کد دو هشدار صادر کرد.

بیایید با هشدار V3192 شروع کنیم. پیام آنالایزر می گوید که Max خاصیت در GetHashCode روش اما در Equals روش اگر ما به ناعادلانه نگاه کنیم Equals روش ، ما می توانیم آن را دیگری ببینیم Equals در بدن آن خوانده می شود. در آنجا می توانیم موارد زیر را مشاهده کنیم: Equals(Min, other.Min) && Equals(Min, other.Min)بشر قانون تشخیصی V3001 به این قطعه اشاره کرد.

واضح است ، یکی از && عملیات باید Equals(Max, other.Max) فرم

بنابراین ، آنالایزر درست است –Max در Equals روش

قطعه کد 5

public class CoordinateRangeMutable : IEquatable<CoordinateRangeMutable>
{
  ....
  public static bool operator ==(CoordinateRangeMutable a, 
                                 CoordinateRangeMutable b)
  {
    return a.Equals(b);
  }

  public static bool operator !=(CoordinateRangeMutable a, 
                                 CoordinateRangeMutable b)
  {
    return !a.Equals(b);
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدارهای PVS-Studio:

V3115 عبور “تهی” به “==” اپراتور نباید منجر به “nullReferenceException” شود. SCOTTPLOT COMMINATERANGEMINALE.CS 188

V3115 عبور “تهی” به “! =” اپراتور نباید منجر به “nullReferenceException” شود. SCOTTPLOT COMMINATERANGEMINALE.CS 193

آنالایزر هشدارهایی را برای CoordinateRangeMutable کلاس. هشدارهای جدید نشان می دهد که == وت != اپراتورهای اضافه بار ممکن است باعث شود NullReferenceExceptionبشر این درست است: اگر عمل چپ باشد null هنگام مقایسه اشیاء CoordinateRangeMutable نوع ، ما قطعاً NullReferenceExceptionبشر

این اتفاق می افتد زیرا ، در اجرای بیش از حد اپراتور ، Equals مقایسه را بدون انجام null بررسی کنید

همیشه درست

قطعه کد 6

private double GetIdealTickSpacing(CoordinateRange range,
                                   PixelLength axisLength,
                                   PixelLength maxLabelLength)
{
  int targetTickCount = (int)(axisLength.Length / maxLabelLength.Length) + 1;

  int radix = 10;                                          // <=
  int exponent = (int)Math.Log(range.Length, radix) + 1;
  double initialSpace = Math.Pow(radix, exponent);
  List<double> tickSpacings = [initialSpace];

  double[] divBy;
  if (radix == 10)                                         // <=
    divBy = [2, 2, 2.5]; // 10, 5, 2.5, 1
  else if (radix == 16)
    divBy = [2, 2, 2, 2]; // 16, 8, 4, 2, 1
  else
    throw new ArgumentException($"radix {radix} is not supported");

  ....
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-studio: بیان V3022 “Radix == 10” همیشه درست است. scottplot decimaltickspacingcalculator.cs 42

آنالایزر گزارش می دهد که radix == 10 بیان همیشه درست است – ما لازم نیست که برای دیدن آن بسیار دور باشیم. در ابتدای روش ، یک مقدار به radix یک بار متغیر. بنابراین ، هنگامی که radix == 10 شرایط بررسی می شود ، مقدار آن همیشه 10 است.

به احتمال زیاد ، مقدار متغیر مورد استفاده برای تغییر تحت برخی شرایط ، اما این شرایط پس از ویرایش کد ناپدید شد. توجه به چنین مواردی و تمیز کردن سریع کدی که بر عملکرد برنامه تأثیر نمی گذارد ، مهم است.

می تواند سریعتر پیش برود

قطعه کد 7

public void Apply(RenderPack rp, bool beforeLayout)
{
  ....

  if (isPanning & Math.Abs(newLimits.Max - oldRight) < tickDelta / 2)
  {
    newRight = oldRight;
    newLeft = oldLeft;
  }
  ....
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-Studio: V3093 اپراتور '&' هر دو عمل را ارزیابی می کند. شاید به جای آن از یک اپراتور اتصال کوتاه '&&' استفاده شود. Scottplot snaptoticksx.cs 94

آنالایزر پیشنهاد را جایگزین می کند & با && از آنجا که محاسبه قسمت راست یک بیان منطقی در صورتی بی معنی است falseبشر

چنین رفع می تواند منطق برنامه را تغییر دهد ، به عنوان مثال ، اگر اجرای کد اپند راست چیزی را در دامنه جهانی (یک زمینه یا خاصیت) تغییر دهد. با این حال ، در مورد ما ، روش در سمت راست هیچ چیزی را تغییر نمی دهد ، فقط یک مقدار را برمی گرداند.

اندازه منفی

قطعه کد 8

public class DraggableRows() : IMultiplotLayout
{

  readonly List<float> PlotHeights = [];

  ....

  float[] GetDividerPositions()
  {
    if (PlotHeights.Count == 1)
      return [PlotHeights[0]];

    float[] positions = new float[PlotHeights.Count - 1]; // <=

    positions[0] = PlotHeights[0];
    for (int i = 1; i < positions.Length; i++)
    {
      positions[i] = positions[i - 1] + PlotHeights[i];
    }

    return positions;
  }

  ....
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS -Studio: V3171 مقدار مورد استفاده به عنوان اندازه یک آرایه می تواند به -1 برسد. بازرسی را در نظر بگیرید: plotheights.count – 1. Scottplot draggablerows.cs 42

نگاهی بیندازید positions آرایه ایجاد شده است. استفاده می کند PlotHeights.Count - 1 به عنوان اندازه آن با این حال ، هیچ تضمینی وجود ندارد PlotHeights.Count بیشتر از 0 خواهد بود. تنها PlotHeights بررسی اندازه تضمین می کند که این مجموعه نمی کند هنگام ایجاد آرایه ، یک عنصر واحد را شامل می شود. شایان ذکر است که PlotHeights به عنوان یک مجموعه خالی آغاز می شود. این احتمال استفاده از مقدار منفی هنگام مشخص کردن اندازه آرایه را افزایش می دهد.

پارامتر استفاده نشده

قطعه کد 9

private void RenderColorbarAxis(RenderPack rp,
                                PixelRect colormapRect,
                                float size,
                                float offset)
{
  GenerateTicks(rp.DataRect);

  float size2 = Edge switch
  {
    Edge.Left => size - colormapRect.Width,
    Edge.Right => size - colormapRect.Width,
    Edge.Bottom => size - colormapRect.Height,
    Edge.Top => size - colormapRect.Height,
    _ => throw new NotImplementedException(),
  };

  float offset2 = Edge switch
  {
    Edge.Left => rp.DataRect.Left - colormapRect.Left,
    Edge.Right => colormapRect.Right - rp.DataRect.Right,
    Edge.Bottom => colormapRect.Bottom - rp.DataRect.Bottom,
    Edge.Top => rp.DataRect.Top - colormapRect.Top,
    _ => throw new NotImplementedException(),
  };

  Axis.Render(rp, size2, offset2);
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-Studio: V3196 از پارامتر “افست” در داخل بدنه روش استفاده نمی شود ، اما شناسه ای با نام مشابه در همان روش استفاده می شود. scottplot colorbar.cs 133

این یک مسئله نسبتاً متداول است ، اما آن را بی ضرر تر نمی کند. در offset پارامتر در روش استفاده نمی شود. مسئله اصلی این است که کد فراخوانی مقادیر را محاسبه می کند تا آن را به عنوان آرگومان به پارامتر مربوطه منتقل کند ، که واقعاً مفید نیست.

هنگام استفاده از RenderColorbarAxis روش ، یک توسعه دهنده ممکن است از اجرای آن غافل شود و به اشتباه فکر کند که offset تصویب به عنوان یک استدلال بر منطق روش تأثیر می گذارد.

دچار آشفتگی

قطعه کد 10

public Rectangle Rectangle(CoordinateRect rect)
{
  return Rectangle(rect.Left, rect.Right, rect.Top, rect.Bottom);
}

public Rectangle Rectangle(double left, double right, double bottom, double top)
{
  Color color = GetNextColor();
  Rectangle rp = new()
  {
    X1 = left,
    X2 = right,
    Y1 = bottom,
    Y2 = top,
    LineColor = color,
    FillColor = color.WithAlpha(.5),
  };

  Plot.PlottableList.Add(rp);
  return rp;
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هشدار PVS-studio: v3066 ترتیب نادرست آرگومان ها به روش “مستطیل” منتقل شده است: “rect.top” و “rect.bottom”. Scottplot plotteableadder.cs 1078

آنالایزر گزارش می دهد که دستور استدلال هنگام انتقال آنها به Rectangle روش این درست است: rect.Top استدلال به bottom پارامتر ، و rect.Bottom استدلال به topبشر

با اطمینان می توان گفت که آیا در اینجا خطایی وجود دارد ، اما به نظر می رسد مشکوک است. حتی اگر خطا وجود داشته باشد ، به نظر می رسد بسیار مهم نیست. با این حال ، این واقعیت به این معنی نیست که آنجا نیست 🙂

پایان

کد پروژه کاملاً تمیز است- آنالایزر کمی بیش از 50 هشدار سطح متوسط ​​و بالا صادر کرد. با این وجود ، من موفق شدم 10 موردی را انتخاب کنم که به نظر می رسید ارزش نگاه داشته باشد.

نکته جالب اینجاست که این پروژه هشدارهایی در رابطه با تنظیم مجدد تهی بالقوه ندارد. با این حال ، چنین خطاهایی مواردی هستند که توسعه دهندگان معمولاً هنگام تجزیه و تحلیل پروژه ها با آنها روبرو می شوند. برای مقایسه ، من از شما دعوت می کنم تا یک پروژه دیگر را بخوانید.

و اگر می خواهید پروژه خود را با استفاده از PVS-Studio بررسی کنید ، می توانید این لینک را دنبال کنید تا آنالایزر را امتحان کنید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا