
في هذه المقالة ، سأتحدث عن مكتبة التحليل الثابت الجديدة ( go-ruleguard
(والأداة المساعدة) التي تتكيف مع gogrep
لاستخدامها داخل gogrep
.
ميزة مميزة: أنت تصف قواعد التحليل الثابت في DSL خاص بـ Go-like ، والذي يتحول في بداية ruleguard
إلى مجموعة من التشخيصات. ربما تكون هذه واحدة من أكثر الأدوات التي يمكن تكوينها بسهولة لتنفيذ عمليات التفتيش المخصصة لـ Go.
على سبيل المكافأة ، سنتحدث عن go/analysis
والأسلاف السابقة .
تحليل ثابت القابلية للتوسعة
هناك العديد من البطانات لـ Go ، يمكن توسيع بعضها. عادة ، لتمديد linter ، تحتاج إلى كتابة رمز Go باستخدام API linter الخاص.
هناك طريقتان رئيسيتان: Go plugins و monolith. يتضمن المتراصة أن جميع الشيكات (بما في ذلك الشيكات الشخصية) متوفرة في مرحلة التجميع.
يتطلب revive
الشيكات الجديدة ليتم تضمينها في نواة للتوسع. go-critic
بالإضافة إلى ذلك المكونات الإضافية ، والتي تسمح لك بجمع الإضافات بغض النظر عن الرمز الرئيسي. يعني كل من هذه الطرق أنك تقوم بتنفيذ عمليات go/ast
و go/types
man على Go باستخدام API linter. حتى الشيكات البسيطة تتطلب الكثير من التعليمات البرمجية .
يهدف go/analysis
إلى تبسيط الصورة من خلال حقيقة أن "إطار" اللنتير يصبح متطابقًا تقريبًا ، لكنه لا يحل مشكلة تعقيد التنفيذ التقني للتشخيصات نفسها.
استطراد على 'loader` و `go / package`
عندما تكتب محللًا لـ Go ، فإن هدفك النهائي هو التفاعل مع AST وأنواعه ، لكن قبل أن تتمكن من القيام بذلك ، يجب أن يتم "تحميل" الكود المصدري بالطريقة الصحيحة. لتبسيط ، ويشمل مفهوم التحميل تحليل ، والتحقق من النوع ، واستيراد التبعيات .
الخطوة الأولى في تبسيط خط الأنابيب هذا كانت حزمة go/loader
، والتي ستتيح لك "تنزيل" كل ما تحتاجه من خلال مكالمات عدة. كان كل شيء جيدًا تقريبًا ، ثم أصبح مهملاً لصالح go/packages
. go/packages
لها واجهة برمجة تطبيقات محسّنة قليلاً ، ومن الناحية النظرية ، تعمل بشكل جيد مع الوحدات.
الآن ، من الأفضل عدم استخدام أي مما سبق مباشرة لكتابة المحللون ، لأن go/analysis
أعطت go/packages
شيئًا لم يكن لدى أي من الحلول السابقة - هيكل لبرنامجك. الآن يمكننا استخدام نموذج go/analysis
إملاءه وإعادة تحليل المحللين بشكل أكثر كفاءة. يحتوي هذا النموذج على نقاط مثيرة للجدل ، على سبيل المثال ، go/analysis
مناسب تمامًا للتحليل على مستوى حزمة واحدة وتوابعها ، ولكن لإجراء تحليل عالمي عليها دون حيل هندسية الماكرة لن يكون بالأمر السهل.
go/analysis
يبسط أيضا اختبار محلل .
ما هو حارس القانون؟

go-ruleguard
هي أداة مساعدة للتحليل الثابت لا تتضمن فحصًا واحدًا افتراضيًا.
ruleguard
تحميل قواعد ruleguard
في البداية ، من ملف gorules
خاص gorules
أنماط الأكواد التي يجب أن تصدر التحذيرات إليها. يمكن تحرير هذا الملف بحرية بواسطة مستخدمي ruleguard
.
ليس من الضروري gorules
برنامج التحكم للاتصال بالشيكات الجديدة ، لذلك يمكن gorules
القواعد من gorules
ديناميكية .
يشبه برنامج التحكم ruleguard
هذا:
package main import ( "github.com/quasilyte/go-ruleguard/analyzer" "golang.org/x/tools/go/analysis/singlechecker" ) func main() { singlechecker.Main(analyzer.Analyzer) }
في الوقت نفسه ، analyzer
تطبيق analyzer
خلال حزمة ruleguard
، والتي يجب عليك استخدامها إذا كنت ترغب في استخدامها كمكتبة.
سيادة الحرس VS إحياء
خذ مثالًا بسيطًا لكنه حقيقي: افترض أننا نريد تجنب runtime.GC()
المكالمات runtime.GC()
في برامجنا. في إحياء هناك بالفعل تشخيص منفصل لهذا ، ويسمى "call-to-gc"
.
تطبيق Call-to-gc (70 خطًا في Elven)
package rule import ( "go/ast" "github.com/mgechev/revive/lint" )
قارن الآن كيف يتم ذلك في go-ruleguard
:
package gorules import "github.com/quasilyte/go-ruleguard/dsl/fluent" func callToGC(m fluent.Matcher) { m.Match(`runtime.GC()`).Report(`explicit call to the garbage collector`) }
لا شيء أكثر من ذلك ، ما هو مهم حقًا - runtime.GC
ورسالة يجب إصدارها في حالة وجود قاعدة.
قد تسأل: هل هذا كل شيء؟ لقد بدأت على وجه التحديد بمثال بسيط لإظهار مقدار الشفرة التي قد تكون مطلوبة لتشخيص تافه للغاية في حالة النهج التقليدي. أعدك أنه سيكون هناك المزيد من الأمثلة المثيرة.
بداية سريعة
لدى rangeExprCopy
go-critic
تشخيص rangeExprCopy
يعثر على نسخ صفيف غير متوقعة في الكود.
يتم تكرار هذا الرمز على نسخة من المصفوفة:
var xs [2048]byte for _, x := range xs {
إصلاح هذه المشكلة هو إضافة حرف واحد:
var xs [2048]byte - for _, x := range xs { // Copies 2048 bytes + for _, x := range &xs { // No copy // Loop body. }
على الأرجح ، لا تحتاج إلى هذا النسخ ، كما أن أداء الإصدار الذي تم تصحيحه أفضل دائمًا. يمكنك الانتظار حتى يتحسن برنامج التحويل البرمجي Go ، أو يمكنك اكتشاف مثل هذه الأماكن في الكود وتصحيحها اليوم باستخدام نفس go-critic
.
يمكن تنفيذ هذا التشخيص في اللغة rules.go
(ملف rules.go
):
package gorules import "github.com/quasilyte/go-ruleguard/dsl/fluent" func _(m fluent.Matcher) { m.Match(`for $_, $_ := range $x { $*_ }`, `for $_, $_ = range $x { $*_ }`). Where(m["x"].Addressable && m["x"].Type.Size >= 128). Report(`$x copy can be avoided with &$x`). At(m["x"]). Suggest(`&$x`) }
تبحث القاعدة for-range
جميع حلقات for-range
حيث يتم استخدام كلا المتغيرين القابلين للتكرار (هذه هي الحالة التي تؤدي إلى النسخ). يجب أن يكون التعبير القابل للتكرار $x
addressable
ويجب أن يكون أكبر من الحد المحدد بالبايت.
يعرّف Report()
الرسالة التي سيتم إصدارها إلى المستخدم ، ويصف Suggest()
قالب quickfix
الذي يمكن استخدامه في برنامج التحرير الخاص بك عبر gopls (LSP) ، وكذلك بشكل تفاعلي إذا ruleguard
استدعاء ruleguard
باستخدام الوسيطة ruleguard
(سنعود إلى هذا). At()
يعلق التحذير و quickfix
إلى جزء معين من القالب. نحتاج هذا لاستبدال $x
بـ &$x
، بدلاً من إعادة كتابة الحلقة بأكملها.
يقبل كل من Report()
Suggest()
سلسلة يمكن فيها تحريف التعبيرات التي تم التقاطها بواسطة القالب من Match()
. المتغير المحدد مسبقًا يعني "كل جزء تم التقاطه" (مثل $0
في التعبيرات العادية).
قم rangecopy.go
ملف rangecopy.go
:
package example
الآن يمكننا تشغيل ruleguard
:
$ ruleguard -rules rules.go -fix rangecopy.go rangecopy.go:12:20: builtins copy can be avoided with &builtins
إذا نظرنا بعد ذلك إلى rangecopy.go
، rangecopy.go
إصدارًا ثابتًا ، لأنه تم استدعاء ruleguard
باستخدام المعلمة ruleguard
.
أبسط القواعد يمكن تصحيحها دون إنشاء ملف gorules
:
$ ruleguard -c 1 -e 'm.Match(`return -1`)' rangecopy.go rangecopy.go:17:2: return -1 16 } 17 return -1 18 }
بفضل استخدام go/analysis/singlechecker
، لدينا الخيار -c
، والذي يسمح لنا بعرض خطوط السياق المحددة مع التحذير نفسه. تعتبر السيطرة على هذه المعلمة غير بديهية بعض الشيء: القيمة الافتراضية هي -c=-1
، مما يعني "بلا سياق" ، و -c=0
سينتج سطرًا واحدًا من السياق (الخط المشار إليه بواسطة التشخيص).
وهنا بعض gorules
أكثر إثارة للاهتمام:
- اكتب القوالب التي تتيح لك تحديد الأنواع المتوقعة. على سبيل المثال ، توضح
map[$t]$t
التعبيرية map[$t]$t
جميع المخططات التي يتطابق نوع القيمة مع نوع المفتاح ، بينما يلتقط *[$len]$elem
جميع المؤشرات إلى صفائف. - ضمن وظيفة واحدة ، قد يكون هناك عدة قواعد ،
والوظائف نفسها يجب أن تسمى مجموعات القاعدة . - يتم تطبيق القواعد في المجموعة واحدة تلو الأخرى ، بالترتيب الذي تم تعريفها به. القاعدة الأولى التي يتم تشغيلها تلغي المقارنة مع القواعد المتبقية. هذا مهم ليس كثيرًا للتحسين بقدر أهمية تخصيص قواعد لحالات معينة. مثال على ذلك ، حيث يكون هذا مفيدًا هو قاعدة إعادة كتابة
$x=$x+$y
إلى $x+=$y
، بالنسبة للحالة مع $y=1
تريد تقديم $x++
، وليس $x+=1
.
يمكن العثور على مزيد من المعلومات حول DSL المستخدمة في docs/gorules.md
.
المزيد من الأمثلة
package gorules import "github.com/quasilyte/go-ruleguard/dsl/fluent" func exampleGroup(m fluent.Matcher) {
إذا لم يكن هناك استدعاء Report()
للقاعدة ، فسيتم استخدام إخراج الرسالة من Suggest()
. هذا يسمح في بعض الحالات بتجنب الازدواجية.
يمكن للمرشحات اكتب والنماذج الفرعية التحقق من الخصائص المختلفة. على سبيل المثال ، تكون خصائص Pure
و Const
مفيدة:
Var.Pure
يعني أن التعبير ليس له آثار جانبية.- يعني
Var.Const
أنه يمكن استخدام التعبير في سياق ثابت (على سبيل المثال ، بعد صفيف).
للأسماء package-qualified
للحزم في شروط Where()
، تحتاج إلى استخدام الأسلوب Import()
. للراحة ، تم استيراد جميع الحزم القياسية لك ، لذلك في المثال أعلاه لا نحتاج إلى القيام بواردات إضافية.
go/analysis
الإجراءات quickfix
quickfix
دعم quickfix
عن طريق go/analysis
بالنسبة لنا.
في نموذج go/analysis
، يولد المحلل التشخيصات والحقائق . يتم إرسال التشخيص إلى المستخدمين ، والوقائع مخصصة للاستخدام من قبل المحللين الآخرين.
يمكن أن تحتوي التشخيصات على مجموعة من الإصلاحات المقترحة ، يصف كل منها كيفية تغيير أكواد المصدر في النطاق المحدد من أجل إصلاح المشكلة التي عثر عليها التشخيص.
الوصف الرسمي متاح في go/analysis/doc/suggested_fixes.md
.
استنتاج

جرِّب ruleguard
في مشاريعك ، وإذا وجدت خطأً أو كنت ترغب في طلب ميزة جديدة ، فافتح المشكلة .
إذا كنت لا تزال تجد صعوبة في التوصل إلى تطبيق ruleguard
، فإليك بعض الأمثلة:
- قم بتطبيق تشخيصاتك الخاصة لـ Go.
- ترقية تلقائيا أو refactor رمز مع
-fix
. - جمع إحصائيات الكود باستخدام معالجة
-json
.
خطط التطوير ruleguard
في المستقبل القريب:
روابط وموارد مفيدة