جو ناقد: المحلل الثابت الأكثر عنادا لـ Go


نحن نعلن عن اللتر الجديد (محلل ثابت) لـ Go ، والذي يعد أيضًا وضع حماية لنماذج أفكارك في عالم التحليل الثابت.


يقوم الناقد على الملاحظات التالية:


  • من الأفضل أن يكون الاختبار "جيدًا بما فيه الكفاية" بدلاً من عدم إجرائه على الإطلاق
  • إذا كان الشيك مثيرًا للجدل ، فهذا لا يعني أنه لا يمكن أن يكون مفيدًا. نحن نحتفل ب "رأيه" وصب
  • عادة ما تكون كتابة اللتر من الصفر أكثر صعوبة من إضافة شيك جديد إلى إطار عمل موجود إذا كان الإطار نفسه سهل الفهم.

في هذا المنشور ، سنلقي نظرة على استخدام وهندسة الناقد ، وبعض الفحوصات المنفذة فيه ، وسنصف أيضًا الخطوات الرئيسية لإضافة وظيفة المحلل لدينا.


بداية سريعة


$ cd $GOPATH $ go get -u github.com/go-critic/go-critic/... $ ./bin/gocritic check-package strings $GOROOT/src/strings/replace.go:450:22: unslice: could simplify s[:] to s $GOROOT/src/strings/replace.go:148:2: elseif: should rewrite if-else to switch statement $GOROOT/src/strings/replace.go:156:3: elseif: should rewrite if-else to switch statement $GOROOT/src/strings/replace.go:219:3: elseif: should rewrite if-else to switch statement $GOROOT/src/strings/replace.go:370:1: paramTypeCombine: func(pattern string, value string) *singleStringReplacer could be replaced with func(pattern, value string) *singleStringReplacer $GOROOT/src/strings/replace.go:259:2: rangeExprCopy: copy of r.mapping (256 bytes) can be avoided with &r.mapping $GOROOT/src/strings/replace.go:264:2: rangeExprCopy: copy of r.mapping (256 bytes) can be avoided with &r.mapping $GOROOT/src/strings/strings.go:791:1: paramTypeCombine: func(s string, cutset string) string could be replaced with func(s, cutset string) string $GOROOT/src/strings/strings.go:800:1: paramTypeCombine: func(s string, cutset string) string could be replaced with func(s, cutset string) string $GOROOT/src/strings/strings.go:809:1: paramTypeCombine: func(s string, cutset string) string could be replaced with func(s, cutset string) string $GOROOT/src/strings/strings.go:44:1: unnamedResult: consider to give name to results $GOROOT/src/strings/strings.go:61:1: unnamedResult: consider to give name to results $GOROOT/src/strings/export_test.go:28:3: rangeExprCopy: copy of r.mapping (256 bytes) can be avoided with &r.mapping $GOROOT/src/strings/export_test.go:42:1: unnamedResult: consider to give name to results 

(تم تعديل تنسيق التحذيرات ؛ النسخ الأصلية متاحة في الجوهر ).


يمكن للأداة gocritic التحقق من الحزم الفردية من خلال مسار الاستيراد الخاص بها ( check-package ) ، وكذلك اجتياز جميع الأدلة بشكل متكرر ( check-project ). على سبيل المثال ، يمكنك التحقق من $GOROOT أو $GOPATH باستخدام أمر واحد:


 $ gocritic check-project $GOROOT/src $ gocritic check-project $GOPATH/src 

هناك دعم لـ "القائمة البيضاء" لعمليات الفحص من أجل إدراج عمليات الفحص التي يجب إجراؤها بشكل -enable (علامة -enable ). بشكل افتراضي ، يتم تشغيل جميع عمليات التحقق التي لم يتم تمييزها VeryOpinionated Experimental أو رمز VeryOpinionated .


يتم التخطيط للتكامل في golangci-lint و gometalinter .


كيف بدأ كل شيء


عند إجراء مراجعة الشفرة التالية لمشروع Go ، أو عند تدقيق بعض مكتبة الطرف الثالث ، يمكنك ملاحظة نفس المشاكل مرارًا وتكرارًا.


للأسف ، لم يكن من الممكن العثور على اللتر الذي يشخص هذه الفئة من المشاكل.


قد تكون خطوتك الأولى محاولة لتصنيف المشكلة والاتصال بمؤلفي اللنتر الموجود ، مع اقتراح إضافة شيك جديد. تعتمد فرص قبول اقتراحك بشكل كبير على المشروع ويمكن أن تكون منخفضة جدًا. علاوة على ذلك ، على الأرجح ، ستتبعه أشهر من التوقعات.


ولكن ماذا لو كان الشيك غامضًا تمامًا ويمكن أن ينظر إليه شخص ما على أنه غير موضوعي للغاية أو غير دقيق بما فيه الكفاية؟


ربما يكون من المنطقي محاولة كتابة هذا الشيك بنفسك؟


go-critic موجود من أجل أن تصبح موطنًا للاختبارات التجريبية التي يسهل تنفيذها من قبل أنفسنا بدلاً من إرفاقها بالمحللات الثابتة الموجودة. يقلل جهاز go-critic نفسه من حجم السياق والإجراءات اللازمة لإضافة فحص جديد - يمكننا القول أنك تحتاج إلى إضافة ملف واحد فقط (دون احتساب الاختبارات).


كيف يعمل الناقد


الناقد هو مجموعة من القواعد التي تصف خصائص الشيكات والمدققين الصغار الذين ينفذون فحص الشفرة للامتثال لقاعدة.


يتلقى التطبيق الذي يشتمل على مادة اللتر (على سبيل المثال ، cmd / gocritic أو golangci-lint ) قائمة بالقواعد المدعومة ، ويقوم بتصفيةها بطريقة محددة ، وإنشاء وظيفة فحص لكل قاعدة محددة ، وتشغيل كل منها على الحزمة قيد الدراسة.


تتلخص عملية إضافة مدقق جديد في ثلاث خطوات رئيسية:


  1. مضيفا الاختبارات.
  2. تنفيذ التحقق نفسه.
  3. إضافة وثائق للليتر.

سنستعرض جميع هذه النقاط باستخدام مثال captLocal الذي يتطلب عدم وجود أسماء محلية تبدأ بحرف كبير.



مضيفا الاختبارات


لإضافة بيانات اختبار للتحقق الجديد ، تحتاج إلى إنشاء دليل جديد في lint / testdata .


يجب أن يحتوي كل دليل من هذا القبيل على ملف إيجابي_ tests.go ، يصف أمثلة التعليمات البرمجية التي يجب أن يعمل عليها الفحص. لاختبار عدم وجود نتائج إيجابية خاطئة ، يتم استكمال الاختبارات برمز "صحيح" ، حيث لا يجب على الشيك الجديد أن يجد أي مشاكل ( سلبية_test.go ).


أمثلة:


 // lint/testdata/positive_tests.go /// consider `in' name instead of `IN' /// `X' should not be capitalized /// `Y' should not be capitalized /// `Z' should not be capitalized func badFunc1(IN, X int) (Y, Z int) { /// `V' should not be capitalized V := 1 return V, 0 } 

 // lint/testdata/negative_tests.go func goodFunc1(in, x int) (x, y int) { v := 1 return v, 0 } 

يمكنك تشغيل الاختبارات بعد إضافة اللتر الجديد.


تنفيذ التحقق


قم بإنشاء ملف باسم المدقق: lint/captLocal_checker.go .
حسب الاصطلاح ، تحتوي جميع ملفات micro- _checker لاحقة _checker .


 package lint //  “Checker”    . type captLocalChecker struct { checkerBase upcaseNames map[string]bool } 

checkerBase هو نوع يجب تضمينه في كل مدقق.
يوفر تطبيقات افتراضية ، والتي تسمح لك بكتابة كود أقل في كل اللبيتر.
من بين أمور أخرى ، تتضمن checkerBase مؤشرًا إلى lint.context ، والذي يحتوي على معلومات النوع والبيانات الوصفية الأخرى حول الملف الذي يتم التحقق منه.


upcaseNames حقل upcaseNames على جدول بالأسماء المعروفة ، والذي upcaseNames . بالنسبة لتلك الأسماء غير الواردة في الخريطة ، يُقترح عدم استخدام حرف كبير ، ولكن لن يتم توفير بديل صحيح.


يتم تهيئة الحالة الداخلية مرة واحدة لكل مثيل.
يجب تعريف طريقة Init() فقط لأولئك الذين يحتاجون إلى تنفيذ التهيئة الأولية.


 func (c *captLocalChecker) Init() { c.upcaseNames = map[string]bool{ "IN": true, "OUT": true, "INOUT": true, } } 

تحتاج الآن إلى تحديد وظيفة الفحص نفسها.
في حالة captLocal ، نحتاج إلى التحقق من جميع ast.Ident المحلية التي تقدم متغيرات جديدة.


للتحقق من جميع التعريفات المحلية للأسماء ، يجب عليك تنفيذ طريقة بالتوقيع التالي في المدقق الخاص بك:


 VisitLocalDef(name astwalk.Name, initializer ast.Expr) 

يمكن الاطلاع على قائمة واجهات الزائر المتاحة في الملف lint / internal / Visitor.go .
captLocal بتنفيذ LocalDefVisitor .


 //  ast.Expr,         //  .      . func (c *captLocalChecker) VisitLocalDef(name astwalk.Name, _ ast.Expr) { switch { case c.upcaseNames[name.ID.String()]: c.warnUpcase(name.ID) case ast.IsExported(name.ID.String()): c.warnCapitalized(name.ID) } } func (c *captLocalChecker) warnUpcase(id *ast.Ident) { c.ctx.Warn(id, "consider `%s' name instead of `%s'", strings.ToLower(id.Name), id) } func (c *captLocalChecker) warnCapitalized(id ast.Node) { c.ctx.Warn(id, "`%s' should not be capitalized", id) } 

عادة ، يتم وضع الطرق التي تولد تحذيرات في طرق منفصلة. هناك استثناءات نادرة ، ولكن اتباع هذه القاعدة يعتبر ممارسة جيدة.


إضافة وثائق


طريقة تنفيذ ضرورية أخرى هي InitDocumentation :


 func (c *captLocalChecker) InitDocumentation(d *Documentation) { d.Summary = "Detects capitalized names for local variables" d.Before = `func f(IN int, OUT *int) (ERR error) {}` d.After = `func f(in int, out *int) (err error) {}` } 

عادة ، املأ 3 حقول فقط:


  • Summary - وصف إجراء التحقق في جملة واحدة.
  • Before - رمز قبل التصحيح.
  • After - رمز بعد التصحيح (لا ينبغي أن يسبب تحذير).

إنشاء الوثائق

إعادة إنشاء الوثائق ليست شرطا مسبقا ل linter الجديد ، ربما في المستقبل القريب ستكون هذه الخطوة مؤتمتة بالكامل. ولكن إذا كنت لا تزال تريد التحقق من كيفية ظهور ملف علامة الإخراج ، فاستخدم الأمر make docs . سيتم تحديث ملف docs/overview.md .


تسجيل اللتر الجديد وإجراء الاختبارات


اللمسة الأخيرة هي تسجيل اللتر الجديد:


 //   captLocal_checker.go init . func init() { addChecker(&captLocalChecker{}, attrExperimental, attrSyntaxOnly) } 

يتوقع addChecker مؤشرًا إلى القيمة الصفرية addChecker الجديد. بعد ذلك تأتي الحجة المتباينة ، مما يسمح لك بتمرير صفات أو أكثر تصف خصائص تنفيذ القاعدة.


يعد attrSyntaxOnly علامة اختيارية attrSyntaxOnly التي لا تستخدم معلومات النوع في تنفيذها ، مما يسمح لك بتشغيلها دون إجراء تدقيق النوع. golangci-lint علامة golangci-lint علامة على مثل هذه golangci-lint "سريعة" (لأنها تعمل بشكل أسرع).


attrExperimental هي سمة يتم تعيينها لجميع عمليات التنفيذ الجديدة. لا يمكن إزالة هذه السمة إلا بعد تثبيت الشيك المطبق.


الآن بعد أن تم تسجيل اللنتر الجديد من خلال addChecker ، يمكنك تشغيل الاختبارات:


 #  GOPATH: $ go test -v github.com/go-critic/go-critic/lint #  GOPATH/src/github.com/go-critic/go-critic: $ go test -v ./lint #  ,      make: $ make test 

دمج متفائل (تقريبًا)


عند النظر في طلبات السحب ، نحاول الالتزام باستراتيجية الدمج المتفائلة . يتم التعبير عن هذا بشكل رئيسي في قبول PRs تلك التي قد يكون للمراجع بعض المطالبات ، ولا سيما الذاتية البحتة. مباشرة بعد حقن هذا التصحيح ، قد يتبع PR من المراجع ، الذي يصحح هذه العيوب ، تتم إضافة مؤلف التصحيح الأصلي إلى CC (نسخة).


لدينا أيضًا علامتي لبرتين يمكن استخدامهما لتجنب العلامات الحمراء في حالة عدم وجود إجماع كامل:


  1. Experimental : يمكن أن يحتوي التنفيذ على كمية كبيرة من الإيجابية الزائفة ، أو يكون غير فعال (يتم تحديد مصدر المشكلة) ، أو "يسقط" في بعض المواقف. يمكنك غرس مثل هذا التنفيذ إذا قمت attrExperimental علامة عليه attrExperimental . في بعض الأحيان ، بمساعدة التجارب ، يشار إلى تلك الفحوصات التي فشلت في العثور على اسم جيد من الالتزام الأول.
  2. VeryOpinionated : إذا كان الشيك يمكن أن يكون فيه مدافعون وأعداء ، فمن الجدير وضع علامة عليه بالسمة attrVeryOpinionated . وبهذه الطريقة ، يمكننا تجنب رفض الأفكار حول نمط الكود التي قد لا تتوافق مع طعم بعض الجوف.

Experimental هي خاصية تنفيذ مؤقتة وقابلة للإصلاح. يعد VeryOpinionated خاصية قاعدة أساسية أكثر وهي مستقلة عن التنفيذ.


من المستحسن إنشاء تذكرة [checker-request] على github قبل إرسال التنفيذ ، ولكن إذا كنت قد أرسلت بالفعل طلب سحب ، يمكنك فتح المشكلة المقابلة لك.


لمزيد من التفاصيل حول عملية التطوير ، راجع CONTRIBUTING.md .
يتم سرد القواعد الأساسية في قسم القواعد الرئيسية .


فراق الكلمات


يمكنك المشاركة في المشروع ليس فقط عن طريق إضافة اللنت الجديد.
هناك العديد من الطرق الأخرى:


  • جربها في مشاريعك أو مشاريع مفتوحة المصدر كبيرة / معروفة وأبلغ عن الإيجابيات الكاذبة والسلبيات الكاذبة وأوجه القصور الأخرى. سنكون ممتنين إذا أضفت أيضًا ملاحظة حول المشكلة التي تم العثور عليها / إصلاحها إلى صفحة الجوائز .
  • اقتراح أفكار لعمليات التفتيش الجديدة. يكفي أن نخلق مشكلة على تعقبنا.
  • أضف اختبارات للوبر الموجودة.

جو go-critic ينتقد كود Go الخاص بك بأصوات جميع المبرمجين المشاركين في تطويره. يمكن لأي شخص أن ينتقد ، لذلك - انضم!


Source: https://habr.com/ru/post/ar414739/


All Articles