| EXTRACLANGTOOLS(1) | Extra Clang Tools | EXTRACLANGTOOLS(1) |
نام (NAME)
extraclangtools - مستندات ابزارهای تکمیلی کلنگ (Extra Clang Tools)
به پروژه clang-tools-extra که شامل ابزارهای اضافی ساختهشده با استفاده از APIهای ابزارسازی Clang است خوش آمدید.
یادداشتهای انتشار EXTRA CLANG TOOLS 22.1.8
- مقدمه
- چه چیزهایی در Extra Clang Tools 22.1.8 جدید است؟
- ویژگیهای عمده جدید
- تغییرات بالقوه ناسازگار
- بهبودهای clangd
- فلگهای کامپایل
- عیبیابیها (Diagnostics)
- Hover
- ارجاعات متقابل (Cross-references)
- نکات درونخطی (Inlay hints)
- سلسلهمراتب فراخوانی (Call hierarchy)
- نمادهای سند (Document symbols)
- جمعکردن کد (Code folding)
- تکمیل خودکار کد (Code completion)
- اقدامات کد (Code actions)
- پشتیبانی از C++20 Modules
- متفرقه
- بهبودهای clang-doc
- بهبودهای clang-query
- بهبودهای clang-tidy
- بررسیهای جدید
- نامهای مستعار جدید برای بررسیها
- تغییرات در بررسیهای موجود
- بررسیهای حذفشده
- متفرقه
- بهبودهای include-fixer
- بهبودهای clang-include-fixer
- بهبودهای modularize
- بهبودهای pp-trace
- پلاگین Visual Studio برای Clang-tidy
نوشتهشده توسط تیم LLVM https://llvm.org
مقدمه
این سند شامل یادداشتهای انتشار برای Extra Clang Tools، بخشی از انتشار 22.1.8 نرمافزار Clang است. در اینجا وضعیت ابزارهای اضافی Clang را با جزئیات شرح میدهیم، از جمله بهبودهای عمده نسبت به نسخه قبلی و کارهای صورتگرفته روی ویژگیهای جدید. تمام انتشارهای LLVM ممکن است از وبگاه انتشارهای LLVM بارگیری شوند https://llvm.org/releases.
برای اطلاعات بیشتر درباره Clang یا LLVM، شامل اطلاعات درباره آخرین نسخه، لطفاً به وبگاه Clang https://clang.llvm.org یا وبگاه LLVM https://llvm.org مراجعه کنید.
توجه داشته باشید که اگر این فایل را از یک checkout در Git یا صفحه اصلی وبگاه Clang میخوانید، این سند برای نسخه بعدی صدق میکند، نه نسخه فعلی. برای مشاهده یادداشتهای انتشار مربوط به یک نسخه خاص، لطفاً صفحه انتشارها را مشاهده کنید https://llvm.org/releases.
چه چیزهایی در Extra Clang Tools 22.1.8 جدید است؟
برخی از ویژگیهای اصلی جدید و بهبودهای صورتگرفته در Extra Clang Tools در اینجا فهرست شدهاند. بهبودهای عمومی در کل Extra Clang Tools یا در زیرساختهای بنیادین آن ابتدا توضیح داده شده و به دنبال آن بخشهای اختصاصی هر ابزار آمده است.
ویژگیهای عمده جدید
تغییرات بالقوه ناسازگار
- ماژول hicpp در clang-tidy منسوخ شد. تمام بررسیها در عوض به ماژولهای دیگر منتقل شدهاند. ماژول hicpp برای نسخه 23.x حذف خواهد شد.
- ماژول zircon در clang-tidy منسوخ شد. تمام بررسیها در عوض به ماژول fuchsia منتقل شدهاند. ماژول zircon در نسخه بیست و چهارم حذف خواهد شد.
- گزینههای سراسری IgnoreMacros و StrictMode از clang-tidy حذف شدند، که از زمان clang-tidy-20 به عنوان منسوخشده مستند شده بودند. کاربران باید در عوض از گزینههای اختصاصی بررسی با همین نام استفاده کنند.
- بررسیهای clang-analyzer-* از بررسیهای پیشفرض در clang-tidy حذف شدند. از این پس، کاربران باید صریحاً با استفاده از clang-analyzer-* مشخص کنند که میخواهند بررسیهای CSA در clang-tidy اجرا شوند.
- نام چند گزینه از بررسیهای clang-tidy تغییر یافت، زیرا دارای غلط املایی بودند:
- NamePrefixSuffixSilenceDissimilarityTreshold به NamePrefixSuffixSilenceDissimilarityThreshold در bugprone-easily-swappable-parameters <>
- CharTypdefsToIgnore به CharTypedefsToIgnore در bugprone-signed-char-misuse <>
- قالب پیام سفارشی bugprone-unsafe-functions <> با اختصاص یک معنای خاص به نویسه > در ابتدای مقدار گزینه CustomFunctions اصلاح شد. اگر مقدار گزینه با > شروع شود، بخش پیشنهاد جایگزینی پیام (که به صورت پیشفرض گنجانده میشد) حذف میشود. (این کار مکانهای هشدار را تغییر نمیدهد.)
- ابزار clang-tidy اکنون به طور پیشفرض هشدارهای تمام هدرهای غیرسیستمی را نمایش میدهد. پیش از این، کاربران باید صریحاً با استفاده از -header-filter='.*' برای دریافت هشدارهای هدر اعلام موافقت میکردند. برای غیرفعال کردن هشدارها از هدرهای غیرسیستمی، -header-filter را روی یک رشته خالی تنظیم کنید.
- هدر clang-tidy/ClangTidyModuleRegistry.h منسوخ شده و در LLVM 24 حذف خواهد شد. تمام نمادهایی که قبلاً تعریف میکرد به clang-tidy/ClangTidyModule.h منتقل شدهاند.
بهبودهای clangd
فلگهای کامپایل
- •
- افزودن پشتیبانی مناسب از فلگ -std=c++latest، که اغلب توسط کاربران ویندوز برای نوشتن کدهای C++23 استفاده میشود.
عیبیابیها
- •
- رفع باگی که باعث کار نکردن رفع سریع (quick-fix) برای عیبیابی readability-identifier-naming در برخی ویرایشگرها میشد.
قابلیت Hover
- افزودن پشتیبانی از تجزیه و نمایش مستندات doxygen و markdown
- افزودن گزینهٔ پیکربندی MacroContentsLimit تا کاربران بتوانند در صورت تمایل، تعاریف ماکروها را حتی اگر بسیار طولانی باشند در hover مشاهده کنند
- رفع باگی که مانع از نمایش مستندات برای اعضای قالبهای کلاسی تعریفشده در یک هدر میشد
- رفع باگی که مانع از نمایش مستندات برای قالبهای توابع خلاصه شده (abbreviated function templates) میشد
مراجع متقابل (Cross-references)
- بهبود پشتیبانی از "go to definition" برای auto وابسته
- بهبود پشتیبانی از "go to definition" برای قابلیت "deducing this" در C++23
- رفع باگی که در آن ویژگیها (attributes) روی یک متد باعث اختلال در قابلیتهای مختلف، از جمله "go to definition" روی نام یک متد میشد
- اکنون قابلیت "Find references" روی یک سازنده، محلهای فراخوانی توابع فورواردکننده مانند make_unique را نیز پیدا میکند
- قابلیت "Document links" اکنون از دایرکتیوهای #include با آرگومان ماکرو پشتیبانی میکند
نکات درونخطی (Inlay hints)
- نمایش نکات مشخصکننده (designator hints) برای مقداردهی اولیهٔ تجمعی ساختارها با نحو مقداردهی پرانتز-لیست (CXXParenListInitExpr).
- نمایش نکات نوع (type hints) برای حالتهای سادهٔ auto وابسته
سلسلهمراتب فراخوانی (Call hierarchy)
- پشتیبانی از فراخوانی سلسلهمراتب فراخوانی روی ثابتهای enum
- هنگام اجرا روی یک تابع مجازی، فراخوانندههای توابع پایه نیز نمایش داده میشوند
نمادهای سند (Document symbols)
- •
- شامل کردن توابع دوست درونخطی (inline friend) در نمادهای سند
تا کردن کد (Code folding)
- پشتیبانی از تا کردن شاخههای پیشپردازنده
- پشتیبانی از تا کردن بلوکهای #pragma region
تکمیل خودکار کد (Code completion)
- افزودن گزینهٔ پیکربندی جدید MacroFilter به بخش Completion به منظور امکان تطبیق تقریبی (fuzzy-matching) با گزینهٔ FuzzyMatch هنگام پیشنهاد دادن ماکروها. حالت پیشفرض ExactPrefix است که رفتار پیشین را در پیشنهاد ماکروهایی که دقیقاً با پیشوند مطابقت دارند حفظ میکند.
- بهبود پشتیبانی از "deducing this" در C++23
- درج هدرها اکنون به جای مسیرهای نوشتهشده، AngledHeaders و QuotedHeaders را در برابر مسیرهای حلشده بررسی میکند که با رفتار موجود در عیبیابیهای include-cleaner سازگار است.
عملیاتهای کد (Code actions)
- عملیات کد جدید Override pure virtual methods. در صورت اجرا روی تعریف یک کلاس، این عملیات بهطور خودکار اعلانهای override در C++ را برای تمام متدهای مجازی محض ارثبریشده از کلاسهای پایه که هنوز پیادهسازی نشدهاند تولید میکند. بدنههای تولیدشده برای متدها از کاربر درخواست پیادهسازی واقعی را میکنند. موارد بازنویسیشده بهصورت هوشمندانه تحت مشخصکنندههای دسترسی اصلی خود (مانند public، protected) گروهبندی میشوند و در صورت نیاز بلوکهای مشخصکنندهٔ دسترسی جدید ایجاد میگردد.
- بهبود منطق عملیات کد "move function body out-of-line" برای انتخاب محل قرار دادن تعریف. اکنون تلاش میکند تا آن را در کنار تعریف متدی با یک اعلان مجاور قرار دهد.
پشتیبانی از ماژولهای C++20
- رفع کرش هنگام استفاده از --experimental-modules-support بدون پایگاهداده کامپایل (compilation database)
- افزودن گزینهٔ --debug-modules-builder برای کمک به اشکالزدایی پشتیبانی ماژولها در clangd
- بهبود عیبیابیهای مربوط به ماژولها
- اکنون clangd سعی میکند در صورت مناسب بودن، از ماژول ساخت پروژه مجدداً استفاده کند
متفرقه
- •
- افزودن فایل تطبیق الگوی .gitignore به دایرکتوری نمایهسازی clangd.
بهبودهای clang-doc
- استفاده از مولد HTML مبتنی بر Mustache به عنوان گزینهٔ پیشفرض HTML.
- مستندسازی conceptها، typedefها، aliasها، اعضای عمومی کلاس، رکوردهای تو در تو، فضاهای نام تو در تو، توابع سراسری، متغیرهای سراسری، متدها و اعضای محافظتشده و توابع دوست در HTML.
- مستندسازی کامنتهای throws در HTML.
- افزودن محل تعریف کلاسها در HTML.
- نمایش اعلان قالب کلاس در HTML.
- افزودن یک صفحهٔ نمایه سطح بالا در HTML. این صفحه فضاهای نام سطح بالا را فهرست میکند و به عنوان نشانی وب ریشه مستندات عمل میکند.
- افزودن دکمهٔ Home به نوار ناوبری که جایگزین دکمههای غیرمفید میشود. این دکمه جدید به صفحهٔ نمایه سطح بالا پیوند دارد.
- افزودن نوار ناوبری مسیر راهنما (breadcrumb) به navbar. این به پیمایش در زمینهٔ موجودیت مستندشده کمک میکند.
- اگر نام فایل بیش از حد طولانی باشد، نام کوتاه شده و شناسه USR موجودیت مستندشده به انتهای آن اضافه میشود تا یکتا بودن آن تضمین گردد.
- بازگرداندن ساختار دایرکتوری تخت (flat) به ساختار دایرکتوری تو در تو.
بهبودهای clang-query
- •
- پرسوجوهای Matcher که توسط clang-query تفسیر میشوند اکنون از کامای انتهایی (,) در آرگومانهای matcher پشتیبانی میکنند. توجه داشته باشید که C++ هنوز این مورد را در آرگومانهای تابع مجاز نمیداند. بنابراین هنگام انتقال یک پرسوجو به C++، تمام نمونههای کامای پایانی را حذف کنید (در غیر این صورت کامپایلر C++ خطای "expected expression" صادر میکند).
بهبودها در clang-tidy
- ابزار check_clang_tidy.py اکنون هنگام اجرا روی فایلهای C، آرگومان -std را شناسایی میکند. اگر -std مشخص نشده باشد، مقدار پیشفرض آن c99-or-later خواهد بود.
- clang-tidy اکنون به طور پیشفرض هشدارهای تمام هدرهای غیرسیستمی را نمایش میدهد. پیش از این، کاربران مجبور بودند با استفاده از -header-filter='.*' بهطور صریح هشدارهای مربوط به هدرها را فعال کنند. برای غیرفعال کردن هشدارهای هدرهای غیرسیستمی، -header-filter را روی یک رشته خالی تنظیم کنید.
- clang-tidy دیگر بهطور پیشفرض تلاشی برای تجزیه و تحلیل کدهای هدرهای سیستمی نمیکند، که این امر کارایی را به شدت بهبود میبخشد. در صورت فعال بودن گزینه SystemHeaders، این رفتار غیرفعال میشود.
- clang-tidy اکنون از بررسیهای سفارشی مبتنی بر پرسمان (query based) به کمک گزینه پیکربندی CustomChecks پشتیبانی میکند. مستندات Query Based Custom Check <>
- اسکریپتهای run-clang-tidy.py و clang-tidy-diff.py اکنون بهطور پیشفرض با استفاده از تمام رشتههای پردازشی (threads) سختافزاری موجود، بررسیها را به صورت موازی اجرا میکنند. هر دو اسکریپت تعداد رشتههای مورد استفاده را در خروجی خود نمایش میدهند.
- ابزار run-clang-tidy.py با افزودن گزینه جدید enable-check-profile بهبود یافته است تا پروفایلهای زمانی را به ازای هر بررسی فعال کرده و گزارشی را بر اساس تمامی فایلهای تجزیه و تحلیلشده چاپ کند.
- مستندات پرچم خط فرمان -line-filter در clang-tidy و run-clang-tidy.py بهبود یافته است.
- گزینه -quiet در clang-tidy با جلوگیری از نمایش پیامهای شمارش عیبیابیها بهبود یافته است.
- clang-tidy بهبود یافته تا در صورت استفاده از یک فیلد خالی directory در پایگاه داده کامپایل، دچار فروپاشی (crash) نشود؛ در عوض، دایرکتوری کاری فعلی استفاده خواهد شد و یک پیام خطا چاپ میشود.
- گزینههای سراسری IgnoreMacros و StrictMode در clang-tidy که از زمان clang-tidy-20 به عنوان منسوخشده مستند شده بودند، حذف شدند. کاربران باید به جای آنها از گزینههای همنام مخصوص هر بررسی استفاده کنند.
- اسکریپتهای run-clang-tidy.py و clang-tidy-diff.py با افزودن گزینه -hide-progress برای جلوگیری از نمایش پیامهای پیشرفت و اطلاعاتی، بهبود یافتهاند.
- بررسیهای clang-analyzer-* از بررسیهای پیشفرض در clang-tidy حذف شدند. از این پس، کاربران باید صراحتاً مشخص کنند که تمایل به اجرای بررسیهای CSA در clang-tidy را دارند.
- clang-tidy با اضافه شدن گزینه --removed-arg جهت حذف آرگومانهای ارسالشده به کامپایلر در هنگام فراخوانی Clang-Tidy بهبود یافته است. این گزینه همچنین به run-clang-tidy.py و clang-tidy-diff.py اضافه شده و میتواند از طریق گزینه RemovedArgs در فایل پیکربندی تنظیم شود.
- ماژول zircon در clang-tidy منسوخ شده است. تمامی بررسیها به ماژول fuchsia منتقل شدهاند. ماژول zircon در انتشار بیست و چهارم حذف خواهد شد.
- تجزیه پیکربندی clang-tidy با امکان استفاده از نحو فهرست مشابه در WarningsAsErrors همانند Checks، بهبود یافته است.
بررسیهای جدید
- بررسی جدید
abseil-unchecked-statusor-access <>.
موارد استفاده از absl::StatusOr را بدون بررسی وجود مقدار، پیدا میکند.
- بررسی جدید
bugprone-derived-method-shadowing-base-method <>.
متدهای کلاس مشتق را که متد کلاس پایه (غیرمجازی) را میپوشانند (shadow)، پیدا میکند.
- بررسی جدید
bugprone-invalid-enum-default-initialization <>.
مقداردهی اولیه پیشفرض (به 0) متغیرهای دارای نوع enum را در شرایطی که شمارنده هیچ مقداری برابر با 0 ندارد، شناسایی میکند.
- بررسی جدید
cppcoreguidelines-pro-bounds-avoid-unchecked-container-access <>.
فراخوانیهای operator[] در کانتینرهای STL را پیدا کرده و جایگزینی آنها با موارد امن را پیشنهاد میدهد.
- بررسی جدید
google-runtime-float <>.
موارد استفاده از long double را پیدا کرده و به دلیل عدم پایداری در قابلیت حمل (portability)، پیشنهاد به عدم استفاده از آن میکند.
- بررسی جدید
llvm-mlir-op-builder <>.
موارد استفاده از فرم قدیمی/رو به منسوخ شدن OpBuilder::create<T> در MLIR را بررسی کرده و استفاده از T::create را پیشنهاد میدهد.
- بررسی جدید
llvm-use-ranges <>.
فراخوانی الگوریتمهای تکرارگر کتابخانه STL را که میتوانند با الگوریتمهای مبتنی بر دامنه (range) متعلق به LLVM از llvm/ADT/STLExtras.h جایگزین شوند، پیدا میکند.
- بررسی جدید
misc-override-with-different-visibility <>.
بازنویسی توابع مجازی با قابلیت دید (visibility) متفاوت نسبت به تابع در کلاس پایه را پیدا میکند.
- بررسی جدید
readability-inconsistent-ifelse-braces <>.
دستورات if/else را که در آنها یک شاخه از آکلاد استفاده میکند و شاخه دیگر استفاده نمیکند، شناسایی میکند.
- بررسی جدید
readability-redundant-parentheses <>.
پرانتزهای اضافی را شناسایی میکند.
- بررسی جدید
readability-redundant-typename <>.
موارد استفاده زائد از کلمه کلیدی typename را پیدا میکند.
نامهای مستعار جدید بررسیها
- تغییر نام cert-dcl50-cpp <> به modernize-avoid-variadic-functions <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-dcl58-cpp <> به bugprone-std-namespace-modification <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-env33-c <> به bugprone-command-processor <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-err34-c <> به bugprone-unchecked-string-to-number-conversion <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-err52-cpp <> به modernize-avoid-setjmp-longjmp <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-err58-cpp <> به bugprone-throwing-static-initialization <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-err60-cpp <> به bugprone-exception-copy-constructor-throws <>.
- تغییر نام cert-flp30-c <> به bugprone-float-loop-counter <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-mem57-cpp <> به bugprone-default-operator-new-on-overaligned-type <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-msc30-c <> به misc-predictable-rand <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-msc32-c <> به bugprone-random-generator-seed <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-msc50-cpp <> به misc-predictable-rand <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-msc51-cpp <> به bugprone-random-generator-seed <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-oop57-cpp <> به bugprone-raw-memory-call-on-non-trivial-type <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام cert-oop58-cpp <> به bugprone-copy-constructor-mutates-argument <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام fuchsia-multiple-inheritance <> به misc-multiple-inheritance <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام google-build-namespaces <> به misc-anonymous-namespace-in-header <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
- تغییر نام google-readability-casting <> به modernize-avoid-c-style-cast <> ضمن حفظ بررسی اولیه به عنوان نام مستعار برای مورد جدید.
تغییرات در بررسیهای موجود
- بهبود بررسی bugprone-chained-comparison <> با افزودن گزینه جدید IgnoreMacros جهت فرونشاندن هشدارها در گسترشهای ماکرو.
- بهبود بررسی bugprone-dynamic-static-initializers <>:
- جلوگیری از هشدارهای نادرست (false positives) برای متغیرهای constexpr که مقداردهندههای اولیه آنها وابسته به مقدار (value-dependent) است.
- افزودن پشتیبانی از متغیرهای constinit در C++20.
- بهبود بررسی bugprone-easily-swappable-parameters <> با تصحیح یک غلط املایی در گزینه NamePrefixSuffixSilenceDissimilarityTreshold.
- بهبود بررسی bugprone-exception-escape <>:
- استثناها از گیراندازیهای لامبدا (lambda captures) اکنون عیبیابی میشوند؛ استثناها در بدنه لامبداهایی که در واقع فراخوانی نمیشوند، گزارش نخواهند شد.
- رفع مشکلی که در آن بررسی، پرتاب استثناها در آرگومانهای ارسالی به توابع یا سازندهها را عیبیابی نمیکرد.
- افزودن پیکربندی دقیق از طریق گزینههای CheckDestructors، CheckMoveMemberFunctions، CheckMain، CheckedSwapFunctions، و CheckNothrowFunctions.
- بهبود بررسی bugprone-infinite-loop <> با افزودن تشخیص برای متغیرهای معرفیشده توسط مقیدسازیهای ساختاریافته (structured bindings).
- بهبود bugprone-invalid-enum-default-initialization <> با گزینه جدید IgnoredEnums برای نادیده گرفتن enumهای مشخصشده در طول تحلیل.
- بهبود بررسی bugprone-macro-parentheses <> با رفع هشدارهای نادرست هنگام استفاده از پارامترهای قالب C++.
- بهبود بررسی bugprone-narrowing-conversions <> با رفع هشدار نادرست ناشی از تحلیل یک عبارت شرطی در C.
- بهبود بررسی bugprone-not-null-terminated-result <> با رفع اصلاحات خودکار (fix-it) اشتباه برای strncmp و wcsncmp در Windows و یک کرش ناشی از برخی عبارات وابسته به مقدار.
- بهبود بررسی bugprone-reserved-identifier <> با نادیده گرفتن اعلانها و ماکروها در سرآیندهای سیستمی.
- بهبود بررسی bugprone-signed-char-misuse <> با رفع هشدارهای نادرست روی enumهای C23 با نوع پایهای ثابت signed char.
- بهبود بررسی bugprone-sizeof-expression <> با رفع یک کرش روی sizeof یک آرایه با نوع وابسته (dependent type).
- بهبود بررسی bugprone-suspicious-include <> با افزودن گزینه IgnoredRegex.
- بهبود bugprone-tagged-union-member-count <> با رفع یک هشدار نادرست هنگامی که enumها یا unionهای موجود در فایلهای سرآیند سیستمی یا فضای نام std به ترتیب به عنوان برچسب یا بخش داده یک tagged union تعریفشده توسط کاربر در نظر گرفته میشدند.
- بهبود بررسی bugprone-throw-keyword-missing <>:
- اکنون فقط انواع متعارف (canonical types) کلاسهای پایه را همانطور که نوشته شدهاند در نظر میگیرد.
- افزودن یک یادداشت برای کلاس پایهای که باعث برانگیختن هشدار شده است.
- رفع مشکلی که در آن بررسی در سازندهها یا (در زمینههای خاص) لامبداها فعال نمیشد.
- •
- بهبود بررسی bugprone-unchecked-optional-access <>:
- افزودن پشتیبانی از NullableValue::makeValue و NullableValue::makeValueInplace برای جلوگیری از هشدارهای نادرست برای BloombergLP::bdlb::NullableValue.
- رفع هشدارهای نادرست برای bsl::optional حاوی نوع allocator-aware.
- افزودن گزینه IgnoreValueCalls برای فرونشاندن عیبیابیها برای optional::value().
- افزودن گزینه IgnoreSmartPointerDereference برای نادیده گرفتن optionalهایی که از طریق ارجاعزدایی شبیه به اشارهگر هوشمند به دست آمدهاند، در عین حفظ عیبیابی ارجاعزداییهای مستعد UB از طریق operator* و operator->.
- بهبود بررسی bugprone-unhandled-self-assignment <> با افزودن یک تطبیقدهنده دیگر که تشخیص الگوی قاعده copy-and-swap را تعمیم میدهد.
- بهبود بررسی bugprone-unsafe-functions <> با پنهان کردن پسوند پیشفرض هنگامی که دلیل با نویسه > در گزینه CustomFunctions آغاز میشود.
- بهبود بررسی bugprone-use-after-move <>:
- افزودن گزینه InvalidationFunctions جهت پشتیبانی از توابع بیاعتبارسازی سفارشی.
- افزودن گزینه ReinitializationFunctions جهت پشتیبانی از توابع مقداردهی اولیه مجدد سفارشی.
- بهبود بررسی cppcoreguidelines-avoid-non-const-global-variables <> با افزودن گزینه جدید AllowThreadLocal که هشدارها را برای متغیرهای سراسری غیر-const با طول عمر ذخیرهسازی thread-local فرو مینشاند.
- بهبود بررسی cppcoreguidelines-init-variables <> با رفع محل درج برای اشارهگرهای تابع با چندین پارامتر.
- بهبود بررسی cppcoreguidelines-macro-usage <> با مستثنی کردن بدنه ماکروهایی که با کلیدواژه __attribute__((..)) شروع میشوند. چنین بدنه ماکرویی بعید است یک عبارت معتبر باشد و بنابراین پیشنهاد دادن یک بازنویسی غیرممکن به یک تابع قالب به کاربران باید اجتناب شود.
- بهبود بررسی cppcoreguidelines-prefer-member-initializer <> برای جلوگیری از هشدارهای نادرست روی اعضای به ارث رسیده در قالبهای کلاس.
- بهبود بررسی cppcoreguidelines-pro-bounds-pointer-arithmetic <> با افزودن گزینه AllowIncrementDecrementOperators برای مجاز کردن محاسبات اشارهگر از طریق عملگرهای کاهش یا افزایش پیشوندی/پسوندی.
- بهبود بررسی cppcoreguidelines-pro-type-member-init <> برای نادیده گرفتن صحیح std::array و سایر نگهدارندههای شبهآرایه هنگامی که گزینه IgnoreArrays روی true تنظیم شده است.
- بهبود fuchsia-multiple-inheritance <> با رفع مشکلی که در آن بررسی فقط اولین کلاس با یک نام دادهشده در برنامه را تحلیل میکرد، و کلاسهای بعدی با همان نام (اعلانشده در یک حوزه متفاوت) را نادیده میگرفت.
- بهبود بررسی google-readability-casting <> با افزودن یادداشتهای fix-it برای تبدیلهای روبهپایین (downcasts) و تبدیل به اشارهگر void.
- بهبود بررسی google-readability-todo <> برای پذیرش قالب جدید TODO از Google Style Guide.
- بهبود بررسی llvm-prefer-isa-or-dyn-cast-in-conditionals <>:
- بخش Fix-it فراخواندهشدهها با nested-name-specifier را بهدرستی مدیریت میکند.
- دستورهای if دارای init-statement (if (auto X = ...; ...)) بهدرستی مدیریت میشوند.
- حلقههای for پشتیبانی میشوند.
- •
- بهبود بررسی misc-const-correctness <>:
- رفع هشدارهای نادرست هنگام انتقال اشارهگرها به ارجاعهای غیر-const.
- رفع هشدارهای نادرست اشارهگر تابع، هنگام بازگرداندن اشارهگر غیر-const، روی عملگر اشارهگر-به-عضو.
- رفع هشدارهای نادرست هنگامی که آدرس یک متغیر برای ارسال به یک تابع گرفته میشود.
- بهبود بررسی misc-coroutine-hostile-raii <> با افزودن گزینه AllowedCallees، که امکان معاف کردن توابع فراخواندهشده با قابلیت انتظار ایمن (safely awaitable) را از بررسی فراهم میکند.
- بهبود کارایی بررسی misc-header-include-cycle <>.
- بهبود بررسی misc-use-internal-linkage <>:
- افزودن پیشنهاد پیوند داخلی (internal linkage) برای انواع تعریفشده توسط کاربر (structها، classها، unionها و enumها).
- افزودن گزینههای دقیق AnalyzeFunctions، AnalyzeVariables، AnalyzeTypes برای کنترل اینکه آیا بررسی باید توابع، متغیرها، و/یا انواع تعریفشده توسط کاربر را عیبیابی کند یا خیر.
- فعالسازی این بررسی برای C.
- بهبود modernize-avoid-c-arrays <> جهت عدم عیبیابی انواع آرایهای که بخشی از یک نمونهسازی ضمنی یک قالب هستند.
- بهبود بررسی modernize-use-constraints <> با رفع یک کرش در استفاده از enable_if غیر استاندارد با امضایی متفاوت از std::enable_if (مانند boost::enable_if).
- بهبود بررسی modernize-use-default-member-init <> برای افزایش استحکام تشخیص مقداردهنده اولیه عضو.
- بهبود بررسی modernize-use-designated-initializers <> برای پیشنهاد استفاده از مقداردهندههای اولیه تعیینشده (designated initializers) برای انواع تجمعی ناممستعاردار (aliased aggregate).
- بهبود modernize-use-integer-sign-comparison <> با ارائه fix-itهای صحیح هنگامی که سمت راست یک مقایسه شامل یک تبدیل غیراستایل C است.
- بهبود بررسی modernize-use-nullptr <> با رفع یک کرش در Windows هنگامی که بررسی با یک باینری ۳۲ بیتی clang-tidy فعال شده بود.
- بهبود modernize-use-override <> با رفع مشکلی که در آن بررسی گاهی اوقات درج override را در مکانی نامعتبر پیشنهاد میداد.
- بهبود بررسی modernize-use-ranges <> برای پیشنهاد استفاده از روش مصطلحتر std::views::reverse در جایی که قبلاً std::ranges::reverse_view را پیشنهاد میکرد.
- بهبود بررسی modernize-use-scoped-lock <> با رفع یک کرش روی کدهای بدریخت (که هنگام استفاده از clang-tidy از طریق clangd رایج است).
- بررسی modernize-use-std-format <> بهبود یافته تا در مواردی که رشتهٔ قالببندی با فراخوانی سازندهٔ ضمنی به نوع دیگری تبدیل میشود، به درستی تطابق پیدا کند.
- بررسی modernize-use-std-print <> بهبود یافته تا هنگام تبدیل رشتهٔ قالببندی به نوع دیگری از طریق فراخوانی سازندهٔ ضمنی به درستی تطابق یابد، و همچنین یک کرش هنگام پردازش رشتههای قالببندی حاوی نویسههای غیر ASCII رفع شده است.
- بررسی modernize-use-using <> بهبود یافته تا راهحلهای اصلاحی (fix-its) را برای typedefهای اشارهگرها یا ارجاعها به انواع آرایهای بهدرستی ارائه دهد.
- بررسی performance-move-const-arg <> با جلوگیری از موارد مثبت کاذب در انواع با قابلیت کپی ساده (trivially copyable) که دارای سازندهٔ کپی غیرعمومی هستند، بهبود یافته است.
- بررسی performance-unnecessary-copy-initialization <> با چاپ نوع متغیر خطایابیشده بهبود یافته است.
- بررسی performance-unnecessary-value-param <> با چاپ نوع متغیر خطایابیشده و تولید صحیح اعلانهای اصلاحی (fix-it hints) برای آرگومانهای بسته پارامتر (parameter-pack) بهبود یافته است.
- بررسی portability-template-virtual-member-function <> بهبود یافته تا از موارد مثبت کاذب در توابع عضو مجازی محض (pure virtual) جلوگیری کند.
- بررسی readability-container-contains <> بهبود یافته تا از مقایسهٔ رشته با npos پشتیبانی کند. تغییرات داخلی ممکن است باعث بروز مثبت کاذبهای نادر جدیدی در کانتینرهای غیر استاندارد شود.
- بررسی readability-container-data-pointer <> با اضافه کردن صحیح پرانتزها هنگامی که عبارت کانتینر عمل دیریفرنس (dereference) است، بهبود یافته است.
- بررسی readability-container-size-empty <> بهبود یافته تا هنگامی که متد size از this ضمنی فراخوانی میشود، نکات اصلاحی را بهدرستی تولید کند؛ سازندههای پیشفرض با آرگومانهای ارائهشده توسط کاربر را نادیده بگیرد؛ و قابلیت شناسایی را در متدهای کانتینر بهجز empty اضافه کند.
- بررسی readability-duplicate-include <> با اضافه شدن گزینهٔ IgnoredFilesList (فهرستی از عبارتهای منظم یا نامفایلها که با نقطهویرگول از هم جدا شدهاند) برای مجاز کردن includeهای تکراریِ عمدی بهبود یافته است.
- بررسی readability-identifier-naming <> بهبود یافته است:
- اکنون اعلانها و ماکروها را در هدرهای سیستمی نادیده میگیرد.
- مستندات برای تفکیک گزینههای عمومی از گزینههای خاص بهبود یافته است.
- گزینههایی برای کنترل دقیق متغیرهای constexpr اضافه شدهاند.
- گزینههای پیشفرضی اضافه شدهاند که با حذف نیاز به مشخص کردن جداگانهٔ هر نوع شناسه، پیکربندیها را ساده میکنند.
- •
- بررسی readability-implicit-bool-conversion <> بهبود یافته است:
- اکنون وقتی عبارات درونی چندین بار بهطور ضمنی تبدیل میشوند، پرانتزها را بهدرستی اضافه میکند.
- این بررسی برای C99 و استانداردهای بعد از آن فعال شده است.
- اکنون تبدیلهای ضمنی از bool به نوع صحیح را هنگام استفاده بهعنوان عملوندهای عملگرهای منطقی (&&، ||) در C مجاز میداند.
- بررسی readability-inconsistent-declaration-parameter-name <> با عدم تحمیل یکسانی نام پارامتر بین یک بسته پارامتر متغیر (variadic parameter pack) در الگوی اصلی و پارامترهای مشخص در تخصیصهای (specializations) آن بهبود یافته است.
- بررسی readability-math-missing-parentheses <> با خطایابی و تشخیص صحیح اولویت عملگرها درون عبارات داخل پرانتز بهبود یافته است.
- بررسی readability-qualified-auto <> با افزودن گزینهٔ IgnoreAliasing بهبود یافته است، که امکان عدم بررسی انواع پایهای نامهای مستعار نوع (type aliases) را فراهم میکند.
- بررسی readability-redundant-casting <> با رفع موارد منفی کاذب در هنگام تبدیل صریح (explicit cast) از اشارهگر تابع بهبود یافته است.
- بررسی readability-redundant-control-flow <> با رفع مشکلی که در آن بررسی گاهی پیشنهاد حذف نه تنها یک return یا continue اضافه، بلکه خطوط غیرمرتبطِ ماقبل آن را نیز میداد، بهبود یافته است.
- بررسی readability-uppercase-literal-suffix <> برای شناسایی پسوندهای لیترال اضافهشده در C++23 و C23 بهبود یافته است.
- بررسی readability-use-concise-preprocessor-directives <> برای تولید اصلاحات مناسب برای حالتهای بدون فاصله پس از دایرکتیو بهبود یافته است.
- بررسی readability-use-std-min-max <> با اطمینان از حفظ کامنتهای بین شرط if و بلوک then هنگام اعمال اصلاحات بهبود یافته است.
بررسیهای حذفشده
متفرقه
بهبودهای include-fixer
بهبودها عبارتند از...
بهبودهای clang-include-fixer
بهبودها عبارتند از...
بهبودهای modularize
بهبودها عبارتند از...
بهبودهای pp-trace
پلاگین Clang-tidy برای Visual Studio
CLANG-TIDY
فهرست
- •
- Clang-Tidy
- استفاده از Clang-Tidy
- اتوماسیون Clang-Tidy
- اجرای موازی Clang-Tidy
- اجرای Clang-Tidy روی Diff
- •
- فرونشاندن عیبیابیهای ناخواسته
همچنین ببینید:
بررسیهای Clang-Tidy
abseil-cleanup-ctad
پیشنهاد میدهد در C++17 و بالاتر، الگوی مقداردهی اولیهی نمونههای absl::Cleanup از تابع کارخانهای (factory function) به استنتاج آرگومان الگوی کلاس (CTAD) تغییر یابد.
auto c1 = absl::MakeCleanup([] {});
const auto c2 = absl::MakeCleanup(std::function<void()>([] {}));
تبدیل میشود به
absl::Cleanup c1 = [] {};
const absl::Cleanup c2 = std::function<void()>([] {});
abseil-duration-addition
مواردی را بررسی میکند که در آنها جمع مقادیر باید در دامنهٔ absl::Time انجام شود. هنگام جمع دو مقدار، در صورتی که مشخص باشد یکی از آنها از نوع absl::Time است، میتوان نتیجه گرفت که مقدار دیگر باید بهصورت یک absl::Duration با مقیاس مشابه تفسیر شود، و این استنباط را صریح کرد.
مثالها:
// Original - Addition in the integer domain int x; absl::Time t; int result = absl::ToUnixSeconds(t) + x; // Suggestion - Addition in the absl::Time domain int result = absl::ToUnixSeconds(t + absl::Seconds(x));
abseil-duration-comparison
مقایسههایی را بررسی میکند که باید بهجای دامنههای ممیز شناور (floating point) یا عدد صحیح (integer)، در دامنهٔ absl::Duration انجام گیرند.
توجه: در مواردی که یک Duration ابتدا به عدد صحیح تبدیل شده و سپس با یک مقدار ممیز شناور مقایسه میشود، عمل قطع کردن اعشار (truncation) در حین تبدیل Duration ممکن است نتیجهٔ متفاوتی ایجاد کند. در عمل این حالت بسیار نادر است، و با این حال نشاندهندهٔ یک باگ است که باید برطرف شود.
مثالها:
// Original - Comparison in the floating point domain double x; absl::Duration d; if (x < absl::ToDoubleSeconds(d)) ... // Suggested - Compare in the absl::Duration domain instead if (absl::Seconds(x) < d) ... // Original - Comparison in the integer domain int x; absl::Duration d; if (x < absl::ToInt64Microseconds(d)) ... // Suggested - Compare in the absl::Duration domain instead if (absl::Microseconds(x) < d) ...
abseil-duration-conversion-cast
تبدیلهای نوع (cast) توابع تبدیل absl::Duration را بررسی کرده و بهجای آنها تابع تبدیل مناسب را پیشنهاد میدهد.
مثالها:
// Original - Cast from a double to an integer absl::Duration d; int i = static_cast<int>(absl::ToDoubleSeconds(d)); // Suggested - Use the integer conversion function directly. int i = absl::ToInt64Seconds(d); // Original - Cast from a double to an integer absl::Duration d; double x = static_cast<double>(absl::ToInt64Seconds(d)); // Suggested - Use the integer conversion function directly. double x = absl::ToDoubleSeconds(d);
نکته: در مثال دوم، اصلاح پیشنهادی ممکن است نتیجهٔ متفاوتی در پی داشته باشد، زیرا تبدیل به عدد صحیح میتواند باعث قطع اعشار شود. در عمل، این حالت بسیار نادر است، و شما باید بهجای آن از absl::Trunc برای انجام صریح این عملیات استفاده کنید.
abseil-duration-division
عملیات ریاضی در absl::Duration مانند اعداد صحیح عمل میکند. این بدان معناست که تقسیم دو شیء absl::Duration یک int64 برمیگرداند که هر بخش کسری آن به سمت ۰ قطع (truncate) میشود. برای اطلاعات بیشتر در مورد محاسبات ریاضی با absl::Duration این پیوند را ببینید: https://github.com/abseil/abseil-cpp/blob/29ff6d4860070bf8fcbd39c8805d0c32d56628a3/absl/time/time.h#L137
برای مثال:
absl::Duration d = absl::Seconds(3.5); int64 sec1 = d / absl::Seconds(1); // Truncates toward 0. int64 sec2 = absl::ToInt64Seconds(d); // Equivalent to division. assert(sec1 == 3 && sec2 == 3); double dsec = d / absl::Seconds(1); // WRONG: Still truncates toward 0. assert(dsec == 3.0);
اگر نیاز به تقسیم ممیز شناور دارید، باید یا از تابع absl::FDivDuration() استفاده کنید، یا یکی از توابع تبدیل واحد مانند absl::ToDoubleSeconds() را به کار ببرید. برای مثال:
absl::Duration d = absl::Seconds(3.5); double dsec1 = absl::FDivDuration(d, absl::Seconds(1)); // GOOD: No truncation. double dsec2 = absl::ToDoubleSeconds(d); // GOOD: No truncation. assert(dsec1 == 3.5 && dsec2 == 3.5);
این بررسی به دنبال مواردی از تقسیم absl::Duration میگردد که در بستر ممیز شناور انجام میشوند، و استفاده از تابعی که مقدار ممیز شناور برمیگرداند را پیشنهاد میدهد.
abseil-duration-factory-float
مواردی را بررسی میکند که در آنها نسخههای بارگذاریمجددشدهٔ (overloaded) ممیز شناور توابع مختلف سازندهٔ absl::Duration فراخوانی شدهاند، در حالی که میشد بهجای آنها از نسخههای صحیح با کارایی بالاتر استفاده کرد.
این بررسی اصلاحاتی را برای مقادیر لیترال که شامل اجزای کسری اعشاری هستند یا عبارات غیرلیترال پیشنهاد نمیدهد. اما حذف تبدیلهای نوع (cast) اضافی را پیشنهاد خواهد داد.
مثالها:
// Original - Providing a floating-point literal. absl::Duration d = absl::Seconds(10.0); // Suggested - Use an integer instead. absl::Duration d = absl::Seconds(10); // Original - Explicitly casting to a floating-point type. absl::Duration d = absl::Seconds(static_cast<double>(10)); // Suggested - Remove the explicit cast absl::Duration d = absl::Seconds(10);
abseil-duration-factory-scale
مواردی را بررسی میکند که در آنها آرگومانهای توابع کارخانهای absl::Duration در داخل مقیاسبندی شدهاند و میتوان آنها را با تابع کارخانهای متفاوتی جایگزین کرد. این بررسی همچنین به دنبال آرگومانهایی با مقدار صفر میگردد و استفاده از absl::ZeroDuration() را به جای آنها پیشنهاد میکند.
مثالها:
// Original - Internal multiplication. int x; absl::Duration d = absl::Seconds(60 * x); // Suggested - Use absl::Minutes instead. absl::Duration d = absl::Minutes(x); // Original - Internal division. int y; absl::Duration d = absl::Milliseconds(y / 1000.); // Suggested - Use absl:::Seconds instead. absl::Duration d = absl::Seconds(y); // Original - Zero-value argument. absl::Duration d = absl::Hours(0); // Suggested = Use absl::ZeroDuration instead absl::Duration d = absl::ZeroDuration();
abseil-duration-subtraction
مواردی را بررسی میکند که در آنها عمل تفریق باید در دامنهٔ absl::Duration انجام شود. هنگام تفریق دو مقدار، و زمانی که مشخص است مقدار اول حاصل یک تبدیل از absl::Duration است، میتوان نتیجه گرفت که مقدار دوم نیز باید به عنوان یک absl::Duration تفسیر شود و این استنتاج را صریح کرد.
مثالها:
// Original - Subtraction in the double domain double x; absl::Duration d; double result = absl::ToDoubleSeconds(d) - x; // Suggestion - Subtraction in the absl::Duration domain instead double result = absl::ToDoubleSeconds(d - absl::Seconds(x)); // Original - Subtraction of two Durations in the double domain absl::Duration d1, d2; double result = absl::ToDoubleSeconds(d1) - absl::ToDoubleSeconds(d2); // Suggestion - Subtraction in the absl::Duration domain instead double result = absl::ToDoubleSeconds(d1 - d2);
نکته: همانند سایر بررسیهای clang-tidy، این احتمال وجود دارد که چند اصلاح با یکدیگر تداخل داشته باشند (مانند حالت عبارتهای تو در تو)، بنابراین ممکن است تمام موارد در یک بار اجرا تبدیل نشوند. بهطور خاص، این حالت ممکن است برای عبارتهای تفریق تو در تو رخ دهد. اجرای چندبارهٔ clang-tidy این تداخلها را پیدا و برطرف خواهد کرد.
abseil-duration-unnecessary-conversion
مواردی را پیدا و اصلاح میکند که در آنها مقادیر absl::Duration به انواع عددی و دوباره به حالت اولیه تبدیل میشوند.
مثالهای ممیز شناور:
// Original - Conversion to double and back again absl::Duration d1; absl::Duration d2 = absl::Seconds(absl::ToDoubleSeconds(d1)); // Suggestion - Remove unnecessary conversions absl::Duration d2 = d1; // Original - Division to convert to double and back again absl::Duration d2 = absl::Seconds(absl::FDivDuration(d1, absl::Seconds(1))); // Suggestion - Remove division and conversion absl::Duration d2 = d1;
مثالهای عدد صحیح:
// Original - Conversion to integer and back again absl::Duration d1; absl::Duration d2 = absl::Hours(absl::ToInt64Hours(d1)); // Suggestion - Remove unnecessary conversions absl::Duration d2 = d1; // Original - Integer division followed by conversion absl::Duration d2 = absl::Seconds(d1 / absl::Seconds(1)); // Suggestion - Remove division and conversion absl::Duration d2 = d1;
خارج کردن عملیات اسکالر از بستهبندی:
// Original - Multiplication by a scalar absl::Duration d1; absl::Duration d2 = absl::Seconds(absl::ToInt64Seconds(d1) * 2); // Suggestion - Remove unnecessary conversion absl::Duration d2 = d1 * 2;
نکته: تبدیل به یک عدد صحیح و سپس بازگرداندن آن به absl::Duration، چنانچه مقدار با مقیاس تبدیل همتراز نباشد ممکن است یک عملیات کوتاه کردن (حذف بخش کسری) باشد. در موارد نادری که این نتیجه مد نظر است، فراخوانها باید از absl::Trunc برای کوتاه کردن صریح استفاده کنند.
abseil-faster-strsplit-delimiter
نمونههایی از absl::StrSplit() یا absl::MaxSplits() را که در آنها جداکننده یک لیترال رشتهای تکنویسهای است پیدا کرده و با یک نویسه (کاراکتر) جایگزین میکند. این بررسی پیشنهادی برای تبدیل لیترال رشتهای به نویسه ارائه میدهد. همچنین کدهایی را که از absl::ByAnyChar() فقط برای یک نویسهٔ واحد استفاده میکنند شناسایی کرده و آنها را نیز به یک نویسهٔ منفرد تبدیل میکند.
این تغییرات نتیجهٔ یکسانی خواهند داشت، اما استفاده از نویسهها به جای لیترالهای رشتهای تکنویسهای بهینهتر و خواناتر است.
مثالها:
// Original - the argument is a string literal.
for (auto piece : absl::StrSplit(str, "B")) {
// Suggested - the argument is a character, which causes the more efficient
// overload of absl::StrSplit() to be used.
for (auto piece : absl::StrSplit(str, 'B')) {
// Original - the argument is a string literal inside absl::ByAnyChar call.
for (auto piece : absl::StrSplit(str, absl::ByAnyChar("B"))) {
// Suggested - the argument is a character, which causes the more efficient
// overload of absl::StrSplit() to be used and we do not need absl::ByAnyChar
// anymore.
for (auto piece : absl::StrSplit(str, 'B')) {
// Original - the argument is a string literal inside absl::MaxSplits call.
for (auto piece : absl::StrSplit(str, absl::MaxSplits("B", 1))) {
// Suggested - the argument is a character, which causes the more efficient
// overload of absl::StrSplit() to be used.
for (auto piece : absl::StrSplit(str, absl::MaxSplits('B', 1))) {
abseil-no-internal-dependencies
اگر کدی که از Abseil استفاده میکند به جزئیات داخلی وابسته باشد، هشدار میدهد. اگر چیزی در یک فضای نام (namespace) قرار داشته باشد که شامل کلمه "internal" است، کد مجاز نیست به آن وابسته باشد زیرا از جزئیات پیادهسازی محسوب میشود. نمیتوان آن را دوست (friend) کرد، include کرد، به آن اشاره کرد یا به هر شکلی به آن ارجاع داد. انجام این کار راهنماهای سازگاری Abseil را نقض میکند و ممکن است منجر به شکست و خرابی کد شود. برای اطلاعات بیشتر به https://abseil.io/about/compatibility مراجعه کنید.
موارد زیر منجر به صدور هشدار خواهند شد:
absl::strings_internal::foo();
// warning triggered on this line
class foo {
friend struct absl::container_internal::faa;
// warning triggered on this line
};
absl::memory_internal::MakeUniqueResult();
// warning triggered on this line
abseil-no-namespace
اطمینان حاصل میکند که کد اقدام به باز کردن namespace absl نکند، چرا که این کار راهنماهای سازگاری Abseil را نقض میکند. کد نباید namespace absl را باز کند، زیرا این عمل با راهنماهای سازگاری Abseil در تعارض است و ممکن است به خرابی منجر شود.
هر کدی که از موارد زیر استفاده کند:
namespace absl {
...
}
با یک هشدار مواجه خواهد شد.
برای اطلاعات بیشتر، راهنماهای کامل سازگاری Abseil را در https://abseil.io/about/compatibility ببینید.
abseil-redundant-strcat-calls
حذف فراخوانیهای غیرضروری به absl::StrCat را هنگامی که نتیجه به یک فراخوانی دیگر به absl::StrCat یا absl::StrAppend فرستاده میشود، پیشنهاد میدهد.
فراخوانیهای اضافه باعث ساختهشدن رشتههای موقتِ غیرضروری میشوند. حذف آنها کد را کوچکتر و سریعتر میکند.
مثالها:
std::string s = absl::StrCat("A", absl::StrCat("B", absl::StrCat("C", "D")));
//before
std::string s = absl::StrCat("A", "B", "C", "D");
//after
absl::StrAppend(&s, absl::StrCat("E", "F", "G"));
//before
absl::StrAppend(&s, "E", "F", "G");
//after
abseil-str-cat-append
استفاده از absl::StrCat() را برای الحاق (append) به یک std::string نشانهگذاری میکند. پیشنهاد میکند که به جای آن باید از absl::StrAppend() استفاده شود.
فراخوانیهای اضافی باعث ایجاد رشتههای موقت غیرضروری میشوند. حذف آنها کد را کوچکتر و سریعتر میکند.
a = absl::StrCat(a, b); // Use absl::StrAppend(&a, b) instead.
مواردی را که در آنها absl::StrCat() به عنوان یک آرگومان قالب برای یک functor استفاده شده است، عیبیابی نمیکند.
abseil-string-find-startswith
بررسی میکند که آیا نتیجهٔ std::string::find() یا std::string::rfind() (و متدهای متناظر در std::string_view) با 0 مقایسه شده است یا خیر، و پیشنهاد میکند که با absl::StartsWith() جایگزین شود. این موضوع هم از نظر خوانایی و هم از نظر عملکرد حائز اهمیت است.
تابع starts_with به عنوان یک تابع توکار در C++20 به این نوعها اضافه شد. در صورت در دسترس بودن، ترجیحاً فعالسازی modernize-use-starts-ends-with <> را به جای این بررسی مد نظر قرار دهید.
string s = "...";
if (s.find("Hello World") == 0) { /* do something */ }
if (s.rfind("Hello World", 0) == 0) { /* do something */ }
تبدیل میشود به
string s = "...";
if (absl::StartsWith(s, "Hello World")) { /* do something */ }
if (absl::StartsWith(s, "Hello World")) { /* do something */ }
گزینهها
- StringLikeClasses
- فهرستی جدا شده با نقطهویرگول از نام کلاسهای شبهرشته (string-like). به صورت پیشفرض، هر دو std::basic_string و std::basic_string_view در نظر گرفته میشوند. فهرست متدهایی که در نظر گرفته میشوند ثابت است.
- IncludeStyle
- یک رشته که مشخص میکند کدام سبک include استفاده میشود: llvm یا google. پیشفرض برابر llvm است.
- AbseilStringsMatchHeader
- مکان هدر strings/match.h متعلق به Abseil. پیشفرض روی absl/strings/match.h تنظیم شده است.
abseil-string-find-str-contains
مقایسههای s.find(...) == string::npos را (برای انواع مختلف شبهرشته) پیدا کرده و پیشنهاد میدهد با absl::StrContains() جایگزین شوند.
این کار خوانایی را بهبود میبخشد و احتمال ترکیب تصادفی find() و npos از انواع مختلف شبهرشته را کاهش میدهد.
به صورت پیشفرض، «انواع شبهرشته» شامل ::std::basic_string، ::std::basic_string_view، و ::absl::string_view میشود. برای تغییر این مورد، گزینهٔ StringLikeClasses را ببینید.
std::string s = "...";
if (s.find("Hello World") == std::string::npos) { /* do something */ }
absl::string_view a = "...";
if (absl::string_view::npos != a.find("Hello World")) { /* do something */ }
تبدیل میشود به
std::string s = "...";
if (!absl::StrContains(s, "Hello World")) { /* do something */ }
absl::string_view a = "...";
if (absl::StrContains(a, "Hello World")) { /* do something */ }
گزینهها
- StringLikeClasses
- فهرستی جداشده با نقطه-ویرگول از نامهای کلاسهای شبهرشته (string-like). بهطور پیشفرض شامل ::std::basic_string، ::std::basic_string_view، و ::absl::string_view است.
- IncludeStyle
- رشتهای که سبک include مورد استفاده را مشخص میکند، llvm یا google. پیشفرض llvm است.
- AbseilStringsMatchHeader
- مکان پرونده سرآیند strings/match.h متعلق به Abseil. مقدار پیشفرض absl/strings/match.h است.
abseil-time-comparison
مقایسهها در دامنه absl::Time را به مقایسه در دامنه اعداد صحیح (integer) ترجیح میدهد.
توجه: در مواردی که یک absl::Time به عدد صحیح تبدیل میشود، ممکن است همترازی (alignment) رخ دهد. اگر مقایسه به این همترازی وابسته باشد، انجام مقایسه در دامنه absl::Time ممکن است نتیجه متفاوتی به همراه داشته باشد. در عمل این وضعیت بسیار نادر است و با این حال نشاندهنده یک باگ است که باید رفع شود.
مثالها:
// Original - Comparison in the integer domain int x; absl::Time t; if (x < absl::ToUnixSeconds(t)) ... // Suggested - Compare in the absl::Time domain instead if (absl::FromUnixSeconds(x) < t) ...
abseil-time-subtraction
عبارتهای تفریق absl::Time را پیدا کرده و اصلاح میکند تا عمل تفریق به جای دامنه عددی، در دامنه زمان (Time) انجام شود.
دو حالت از تفریق زمانی وجود دارد که در آنها اطلاعات نوع (type) اضافی استنتاج میشود:
- زمانی که نتیجه یک absl::Duration باشد و آرگومان اول یک absl::Time باشد.
- زمانی که آرگومان دوم یک absl::Time باشد.
در حالت اول، ما باید از نتیجه عملیات آگاه باشیم، چرا که بدون آن عملوند دوم میتواند یک absl::Time یا یک absl::Duration باشد. در حالت دوم، عملوند اول باید یک absl::Time باشد، زیرا تفریق یک absl::Time از یک absl::Duration تعریف نشده است.
مثالها:
int x; absl::Time t; // Original - absl::Duration result and first operand is an absl::Time. absl::Duration d = absl::Seconds(absl::ToUnixSeconds(t) - x); // Suggestion - Perform subtraction in the Time domain instead. absl::Duration d = t - absl::FromUnixSeconds(x); // Original - Second operand is an absl::Time. int i = x - absl::ToUnixSeconds(t); // Suggestion - Perform subtraction in the Time domain instead. int i = absl::ToInt64Seconds(absl::FromUnixSeconds(x) - t);
abseil-unchecked-statusor-access
این بررسی، دسترسیهای ناامن به مقادیر موجود در اشیاء absl::StatusOr<T> را شناسایی میکند. در ادامه به این نوع با عنوان StatusOr<T> اشاره خواهیم کرد.
دسترسی به مقدار یک StatusOr<T> زمانی رخ میدهد که یکی از توابع عضو value، operator* یا operator-> آن فراخوانی شود. برای همسویی با باورهای نادرست رایج، این بررسی این توابع عضو را معادل هم در نظر میگیرد، اگرچه تفاوتهای ظریفی در رابطه با استثناها (exceptions) در مقابل رفتار تعریفنشده (undefined behavior) وجود دارد.
دسترسی به مقدار یک StatusOr<T> تنها در صورتی ایمن در نظر گرفته میشود که کد در حوزه محلی (مانند بدنه تابع) تضمین کند وضعیت StatusOr<T> در تمام مسیرهای اجرایی ممکنی که میتوانند به دسترسی برسند، ok است. این امر باید یا از طریق یک بررسی صریح، با استفاده از تابع عضو StatusOr<T>::ok، یا با ساخت StatusOr<T> به روشی که نشان دهد وضعیت آن بدون ابهام ok است (مثلاً با ارسال یک مقدار به سازنده آن) صورت گیرد.
در زیر چند نمونه از الگوهای دسترسی ایمن و ناامن به StatusOr<T> آورده شده است.
نکته: اگر این بررسی در یک قطعه کد آنطور که انتظار داشتید رفتار نمیکند، لطفاً آن را در http://github.com/llvm/llvm-project/issues/new گزارش دهید.
منفیهای کاذب (False negatives)
این بررسی عموماً منفی کاذب ایجاد نمیکند. این بدان معناست که اگر یک دسترسی به عنوان ناامن علامتگذاری نشود، از نظر منطقی قابل اثبات است که ایمن است. اگر نتواند ایمن بودن یک دسترسی را اثبات کند، آن را ناامن فرض میکند. در برخی موارد، تحلیل ایستای کد به دلایل گوناگون (مانند پایاها/invariants مدلسازی نشده از توابع فراخوانیشده) نمیتواند ایمن بودن یک دسترسی را با وجود ایمن بودن آن اثبات کند. در این موارد، تحلیل گزارشهای مثبت کاذب (false positive) تولید میکند.
با این حال، برخی روشهای مکاشفهای (heuristics) استفاده میشوند که در موارد بسیار نادر ممکن است نادرست باشند:
- •
- یک متد دسترسیدهنده const (بدون آرگومان) که هنگام چندین بار فراخوانی، مقادیر متفاوتی را برمیگرداند <#functionstability>.
اگر فکر میکنید این بررسی یک منفی کاذب تولید کرده است، لطفاً آن را در http://github.com/llvm/llvm-project/issues/new گزارش دهید.
محدودیتهای شناختهشده
این یک فهرست ناکامل از ساختارهایی است که در حال حاضر در این بررسی مدلسازی نشدهاند و منجر به مثبتهای کاذب (false positives) میشوند:
- بررسی یک StatusOr و سپس گرفتن آن در یک لامبدا <#lambdas>
- ایندکس کردن در یک ظرف (container) با همان ایندکس یکسان <#containers>
- توابع کمکی مختص پروژه <#uncommonapi>،
- توابعی با مقدار بازگشتی پایدار <#functionstability>
- هرگونه استدلال بینتابعی (cross-function) <#crossfunction>. این مورد طبق طراحی قبلی است و در آینده تغییر نخواهد کرد.
بررسی ok بودن وضعیت و سپس دسترسی به مقدار
این بررسی تمام روشهای سرراست برای بررسی وضعیت و دسترسی به مقدار موجود در یک شیء StatusOr<T> را تشخیص میدهد. برای نمونه:
void f(absl::StatusOr<int> sor) {
if (sor.ok()) {
use(*sor);
}
}
بررسی ok بودن وضعیت، سپس دسترسی به مقدار از یک کپی
معیاری که این بررسی به کار میبرد معنایی (semantic) است، نه نحوی (syntactic). این ابزار متوجه میشود که چه زمانی یک رونوشت (کپی) از شیء StatusOr<T> مورد دسترسی، دارای وضعیت ok شناخته شده است. برای نمونه:
void f(absl::StatusOr<int> sor1) {
if (sor1.ok()) {
absl::optional<int> sor2 = sor1;
use(*sor2);
}
}
اطمینان از ok بودن وضعیت با استفاده از ماکروهای متداول
این بررسی از ماکروهای متداول مانند ABSL_CHECK و ASSERT_THAT آگاه است. از آنها میتوان برای اطمینان از ok بودن وضعیت شیء StatusOr<T> استفاده کرد. برای نمونه:
void f(absl::StatusOr<int> sor) {
ABSL_DCHECK_OK(sor);
use(*sor);
}
اطمینان از ok بودن وضعیت، سپس دسترسی به مقدار در یک شاخه مرتبط
این بررسی از شاخههای همبسته و مرتبط در کد آگاه است و میتواند تشخیص دهد چه زمانی تضمین شده که یک شیء StatusOr<T> در تمام مسیرهای اجرایی منتهی به دسترسی، دارای وضعیت ok است. برای نمونه:
void f(absl::StatusOr<int> sor) {
bool safe = false;
if (sor.ok() && SomeOtherCondition()) {
safe = true;
}
// ... more code...
if (safe) {
use(*sor);
}
}
دسترسی به مقدار بدون بررسی وضعیت
این بررسی دسترسیهایی به مقدار را که به صورت محلی توسط بررسی وضعیت محافظت نشدهاند پرچمگذاری میکند:
void f1(absl::StatusOr<int> sor) {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
void f2(absl::StatusOr<MyStruct> sor) {
use(sor->member); // unsafe: it is unclear whether the status of `sor` is ok.
}
void f3(absl::StatusOr<int> sor) {
use(sor.value()); // unsafe: it is unclear whether the status of `sor` is ok.
}
از ABSL_CHECK_OK استفاده کنید تا نشان دهید که آگاهانه میخواهید در مقادیر غیرِ OK برنامه دچار crash (فروپاشی) شود.
نکته: با اینکه استفاده از .value() روی یک StatusOr غیرِ ok() طوری تعریف شده که کرش کند، اما این کار اغلب غیرعمدی است. به همین دلیل بررسیکننده ما این موارد را نیز پرچمگذاری میکند.
دسترسی به مقدار در شاخه اشتباه
این بررسی از حالت شیء StatusOr<T> در شاخههای مختلف کد آگاه است. برای نمونه:
void f(absl::StatusOr<int> sor) {
if (sor.ok()) {
} else {
use(*sor); // unsafe: it is clear that the status of `sor` is *not* ok.
}
}
فرض بر پایدار بودن نتیجه تابع
این بررسی آگاه است که نتایج تابع ممکن است پایدار نباشند. یعنی فراخوانیهای متوالی از یک تابع ممکن است مقادیر متفاوتی را بازگردانند. برای نمونه:
void f(Foo foo) {
if (foo.sor().ok()) {
use(*foo.sor()); // unsafe: it is unclear whether the status of `foo.sor()` is ok.
}
}
در چنین مواردی بهتر است نتیجه فراخوانی تابع را در یک متغیر محلی ذخیره کرده و از آن برای دسترسی به مقدار استفاده کنید. برای نمونه:
void f(Foo foo) {
if (const auto& foo_sor = foo.sor(); foo_sor.ok()) {
use(*foo_sor);
}
}
این بررسی فرض میکند که توابع دسترسیدهنده (accessor) با واجد شرایط const، در صورتی که هیچ تابع غیرِ const دیگری بین دو فراخوانی اجرا نشده باشد، مقدار پایداری بازمیگردانند:
class Foo {
const absl::StatusOr<int>& get() const {
[...];
}
}
void f(Foo foo) {
if (foo.get().ok()) {
use(*foo.get());
}
}
اگر فراخوانی به یک تابع بدون مشخصه const وجود داشته باشد، این بررسی فرض میکند که مقدار بازگشتی دسترسیدهنده تغییر یافته است.
class Foo {
const absl::StatusOr<int>& get() const {
[...];
}
void mutate();
}
void f(Foo foo) {
if (foo.get().ok()) {
foo.mutate();
use(*foo.get()); // unsafe: mutate might have changed the state of the object
}
}
تکیه بر ناورداهای APIهای غیرمعمول
این بررسی از ناورداهای APIهای غیرمعمول آگاه نیست. به عنوان مثال:
void f(Foo foo) {
if (foo.HasProperty("bar")) {
use(*foo.GetProperty("bar")); // unsafe: it is unclear whether the status of `foo.GetProperty("bar")` is ok.
}
}
در چنین مواردی بهتر است بهطور صریح بررسی شود که وضعیت شیء StatusOr<T> مناسب (ok) است. به عنوان مثال:
void f(Foo foo) {
if (const auto& property = foo.GetProperty("bar"); property.ok()) {
use(*property);
}
}
بررسی مناسب بودن وضعیت و سپس ارسال StatusOr<T> به تابعی دیگر
این بررسی بر استدلال محلی متکی است. عمل بررسی و دسترسی به مقدار، هر دو باید در یک تابع رخ دهند. یک دسترسی حتی در صورتی که فراخوانندهٔ تابعِ دسترسیگیرنده اطمینان دهد وضعیت StatusOr<T> مناسب (ok) است، باز هم ناامن تلقی میشود. به عنوان مثال:
void g(absl::StatusOr<int> sor) {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
void f(absl::StatusOr<int> sor) {
if (sor.ok()) {
g(sor);
}
}
در چنین مواردی بهتر است هنگام فراخوانی تابع یا مقدار را مستقیماً ارسال کنید، یا اینکه مناسب بودن وضعیت StatusOr<T> را درون دامنهٔ محلی تابع فراخوانیشده بررسی نمایید. به عنوان مثال:
void g(int val) {
use(val);
}
void f(absl::StatusOr<int> sor) {
if (sor.ok()) {
g(*sor);
}
}
نامهای مستعار ایجاد شده از طریق اعلانهای using
این بررسی از نامهای مستعار انواع StatusOr<T> که از طریق اعلانهای using ایجاد میشوند آگاه است. به عنوان مثال:
using StatusOrInt = absl::StatusOr<int>;
void f(StatusOrInt sor) {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
کانتینرها
این بررسی در مورد کانتینرهای مقادیر StatusOr<T> بیش از حد سختگیر است. صرفاً بررسی مناسب بودن وضعیت یکی از عناصر کانتینر برای امن تلقی شدن دسترسی به آن کافی نیست. به عنوان مثال:
void f(std::vector<absl::StatusOr<int>> sors) {
if (sors[0].ok()) {
use(*sors[0]); // unsafe: it is unclear whether the status of `sors[0]` is ok.
}
}
باید یک ارجاع به آن شیء خاص دریافت کرده و به جای آن استفاده کنید:
void f(std::vector<absl::StatusOr<int>> sors) {
absl::StatusOr<int>& sor0 = sors[0];
if (sor0.ok()) {
use(*sor0);
}
}
نسخههای بعدی میتوانند درک الگوهای امنتر استفاده که شامل کانتینرها هستند را بهبود بخشند.
لانمبداها
این بررسی قادر به گزارش دسترسیهای ناامن StatusOr<T> در لانمبداها هست، اما آنقدر هوشمند نیست که اطلاعات را از زمینهٔ بیرونی به درون لانمبدا منتقل کند. این بدان معناست که الگوی زیر به عنوان یک دسترسی ناامن گزارش خواهد شد:
void f(absl::StatusOr<int> sor) {
if (sor.ok()) {
[&sor]() {
use(*sor); // unsafe: it is unclear whether the status of `sor` is ok.
}
}
}
برای جلوگیری از این مشکل، باید یک ارجاع به شیء موجود درون آن دریافت کنید و به جای آن، آن ارجاع را ضبط (capture) نمایید:
void f(absl::StatusOr<int> sor) {
if (sor.ok()) {
auto& s = *sor;
[&s]() {
use(s);
}
}
}
همچنین میتوانید بررسی را درون لانمبدا، جایی که به مقدار دسترسی پیدا میشود، اضافه کنید:
void f(absl::StatusOr<int> sor) {
[&sor]() {
if (sor.ok()) {
use(*sor);
}
}
}
abseil-upgrade-duration-conversions
فراخوانیهای عملگرهای محاسباتی و توابع سازنده (factory) مربوط به absl::Duration را که آرگومان آنها برای ادامهی کامپایل پس از تغییرات آتی API نیاز به تبدیل صریح (explicit cast) دارد، پیدا میکند.
عملگرهای *=، /=، * و / برای absl::Duration در حال حاضر آرگومانی از نوع کلاس را که به یک نوع محاسباتی قابل تبدیل باشد میپذیرند. چنین فراخوانی در حال حاضر مقدار را به یک int64_t تبدیل میکند، حتی در مواردی مانند std::atomic<float> که منجر به تبدیل همراه با اتلاف داده (lossy conversion) میشود.
علاوه بر این، توابع سازنده absl::Duration (مانند absl::Hours، absl::Minutes و غیره) در حال حاضر یک int64_t یا یک نوع ممیز شناور را میپذیرند. مشابه عملگرهای محاسباتی، فراخوانیها با آرگومانی از نوع کلاس که قابل تبدیل به یک نوع محاسباتی باشد از مسیر int64_t عبور میکنند.
این عملگرها و توابع سازنده برای جلوگیری از رفتارهای ناخواسته تغییر خواهند کرد تا فقط انواع محاسباتی را بپذیرند. پس از انتشار این تغییرات، ارسال آرگومانی از نوع کلاس دیگر کامپایل نخواهد شد، حتی اگر آن نوع به صورت ضمنی به یک نوع محاسباتی قابل تبدیل باشد.
در اینجا نمونههایی از اصلاحات ایجاد شده توسط این بررسی آورده شده است:
std::atomic<int> a; absl::Duration d = absl::Milliseconds(a); d *= a;
تبدیل میشود به:
std::atomic<int> a; absl::Duration d = absl::Milliseconds(static_cast<int64_t>(a)); d *= static_cast<int64_t>(a);
توجه داشته باشید که این بررسی برای حفظ رفتار فعلی کد کاربر، همیشه یک تبدیل نوع به int64_t اضافه میکند. این احتمال وجود دارد که این کار رفتارهای ناخواستهای را به دلیل انواعِ به طور ضمنی قابل تبدیل به نوع ممیز شناور، آشکار کند.
altera-id-dependent-backward-branch
متغیرها و فیلدهای وابسته به شناسه (ID-dependent) را که درون حلقهها استفاده میشوند، پیدا میکند. این امر باعث ایجاد انشعاب در داخل حلقهها شده و در نتیجه منجر به افت کارایی میشود.
// The following code will produce a warning because this ID-dependent
// variable is used in a loop condition statement.
int ThreadID = get_local_id(0);
// The following loop will produce a warning because the loop condition
// statement depends on an ID-dependent variable.
for (int i = 0; i < ThreadID; ++i) {
std::cout << i << std::endl;
}
// The following loop will not produce a warning, because the ID-dependent
// variable is not used in the loop condition statement.
for (int i = 0; i < 100; ++i) {
std::cout << ThreadID << std::endl;
}
بر اساس Altera SDK for OpenCL: Best Practices Guide https://www.altera.com/en_US/pdfs/literature/hb/opencl-sdk/aocl_optimization_guide.pdf.
altera-kernel-name-restriction
فایلهای کرنل و دستورات include را که نام فایل آنها kernel.cl، Verilog.cl یا VHDL.cl است پیدا میکند. این بررسی به بزرگی و کوچکی حروف حساس نیست.
چنین نامهایی برای فایلهای کرنل باعث میشوند کامپایلر آفلاین فایلهای طراحی واسطی تولید کند که همنام برخی از فایلهای داخلی هستند، که این امر منجر به خطای کامپایل میشود.
بر اساس بخش Guidelines for Naming the Kernel در Intel FPGA SDK for OpenCL Pro Edition: Programming Guide https://www.intel.com/content/www/us/en/programmable/documentation/mwh1391807965224.html#ewa1412973930963.
altera-single-work-item-barrier
توابع کرنل OpenCL را که یک تابع سد محافظ (barrier) را فراخوانی میکنند اما هیچ تابع شناسهای (get_local_id، get_local_id، get_group_id یا get_local_linear_id) را فراخوانی نمیکنند، پیدا میکند.
این کرنلها ممکن است کرنلهای تک work-item قابل اجرایی باشند، اما در صورت استفاده از نسخههای جدیدتر Altera Offline Compiler (نسخه 17.01 یا بالاتر) مجبور به اجرا به صورت کرنلهای NDRange خواهند شد.
در صورت استفاده از نسخه قدیمیتر Altera Offline Compiler، این توابع کرنل به عنوان کرنلهای تک work-item در نظر گرفته میشوند، که اگر معناشناسی NDRange مد نظر بوده باشد، میتواند ناکارآمد بوده یا منجر به خطا شود.
بر اساس Altera SDK for OpenCL: Best Practices Guide https://www.altera.com/en_US/pdfs/literature/hb/opencl-sdk/aocl_optimization_guide.pdf.
مثالها:
// error: function calls barrier but does not call an ID function.
void __kernel barrier_no_id(__global int * foo, int size) {
for (int i = 0; i < 100; i++) {
foo[i] += 5;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
// ok: function calls barrier and an ID function.
void __kernel barrier_with_id(__global int * foo, int size) {
for (int i = 0; i < 100; i++) {
int tid = get_global_id(0);
foo[tid] += 5;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
// ok with AOC Version 17.01: the reqd_work_group_size turns this into
// an NDRange.
__attribute__((reqd_work_group_size(2,2,2)))
void __kernel barrier_with_id(__global int * foo, int size) {
for (int i = 0; i < 100; i++) {
foo[tid] += 5;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
گزینهها
- AOCVersion
- نسخه Altera Offline Compiler را مشخص میکند. مقدار پیشفرض 1600 است (متناظر با نسخه 16.00).
altera-struct-pack-align
ساختارهایی (structs) را که به شکلی ناکارآمد بستهبندی (pack) یا تراز (align) شدهاند پیدا کرده و در صورت نیاز، بستهبندی و/یا تراز کردن آنها را توصیه میکند.
ساختارهایی که بستهبندی نشدهاند فضای بیشتری نسبت به آنچه نیاز دارند اشغال میکنند، و دسترسی به ساختارهایی که به خوبی تراز نشدهاند ناکارآمد است.
راهکارهای اصلاحی (Fix-its) برای رفع هر دوی این مشکلات از طریق درج و/یا اصلاح ویژگیهای (attributes) مرتبط در ساختار ارائه شده است.
بر اساس Altera SDK for OpenCL: Best Practices Guide https://www.altera.com/en_US/pdfs/literature/hb/opencl-sdk/aocl_optimization_guide.pdf.
// The following struct is originally aligned to 4 bytes, and thus takes up
// 12 bytes of memory instead of 10. Packing the struct will make it use
// only 10 bytes of memory, and aligning it to 16 bytes will make it
// efficient to access.
struct example {
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
};
// The following struct is arranged in such a way that packing is not needed.
// However, it is aligned to 4 bytes instead of 8, and thus needs to be
// explicitly aligned.
struct implicitly_packed_example {
char a; // 1 byte
char b; // 1 byte
char c; // 1 byte
char d; // 1 byte
int e; // 4 bytes
};
// The following struct is explicitly aligned and packed.
struct good_example {
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
} __attribute__((packed)) __attribute__((aligned(16));
// Explicitly aligning a struct to the wrong value will result in a warning.
// The following example should be aligned to 16 bytes, not 32.
struct badly_aligned_example {
char a; // 1 byte
double b; // 8 bytes
char c; // 1 byte
} __attribute__((packed)) __attribute__((aligned(32)));
altera-unroll-loops
حلقههای داخلی را که باز (unroll) نشدهاند، و همچنین حلقههایی که به طور کامل باز شدهاند اما کرانهای نامشخص یا تعداد تکرار بالایی دارند را پیدا میکند.
باز کردن حلقههای داخلی میتواند کارایی هستههای (kernels) OpenCL را بهبود بخشد. با این حال، اگر آنها دارای کرانهای حلقهٔ نامشخص یا تعداد تکرار بالایی باشند، نمیتوان آنها را به طور کامل باز کرد و باید به صورت جزئی باز شوند.
نکات:
- این بررسی قادر به تعیین تعداد تکرارها در حلقههای while یا do..while نیست؛ بنابراین اگر چنین حلقهای به طور کامل باز شود، یادداشتی صادر میشود که به کاربر توصیه میکند به جای آن از باز کردن جزئی استفاده کند.
- در حلقههای for، بررسی ما تنها با گامهای محاسباتی ساده کار میکند (+، -، *، /). برای تمام گامهای دیگر، باز کردن جزئی توصیه میشود.
- بسته به شرط خروج، محاسبات مربوط به تعیین بزرگ بودن تعداد تکرارها ممکن است ۱ مقدار خطا داشته باشد. این نباید مشکلی ایجاد کند چرا که حد آستانه به طور کلی دلخواه است.
بر اساس راهنمای Altera SDK for OpenCL: Best Practices Guide https://www.altera.com/en_US/pdfs/literature/hb/opencl-sdk/aocl_optimization_guide.pdf.
for (int i = 0; i < 10; i++) { // ok: outer loops should not be unrolled
int j = 0;
do { // warning: this inner do..while loop should be unrolled
j++;
} while (j < 15);
int k = 0;
#pragma unroll
while (k < 20) { // ok: this inner loop is already unrolled
k++;
}
}
int A[1000];
#pragma unroll
// warning: this loop is large and should be partially unrolled
for (int a : A) {
printf("%d", a);
}
#pragma unroll 5
// ok: this loop is large, but is partially unrolled
for (int a : A) {
printf("%d", a);
}
#pragma unroll
// warning: this loop is large and should be partially unrolled
for (int i = 0; i < 1000; ++i) {
printf("%d", i);
}
#pragma unroll 5
// ok: this loop is large, but is partially unrolled
for (int i = 0; i < 1000; ++i) {
printf("%d", i);
}
#pragma unroll
// warning: << operator not supported, recommend partial unrolling
for (int i = 0; i < 1000; i<<1) {
printf("%d", i);
}
std::vector<int> someVector (100, 0);
int i = 0;
#pragma unroll
// note: loop may be large, recommend partial unrolling
while (i < someVector.size()) {
someVector[i]++;
}
#pragma unroll
// note: loop may be large, recommend partial unrolling
while (true) {
printf("In loop");
}
#pragma unroll 5
// ok: loop may be large, but is partially unrolled
while (i < someVector.size()) {
someVector[i]++;
}
گزینهها
- MaxLoopIterations
- حداکثر
تعداد
تکرار
حلقهای را
مشخص
میکند که
یک حلقه با
باز شدن
کامل
میتواند
داشته باشد.
بهطور
پیشفرض
روی 100
تنظیم شده
است.
در عمل، این به مقدار صحیح کران بالا در عبارت شرطی دستور حلقه اشاره دارد.
android-cloexec-accept
استفاده از accept() توصیه نمیشود، بهتر است از accept4() استفاده شود. بدون این پرچم، یک توصیفکننده فایل (file descriptor) حساس باز شده در حین یک فراخوانی fork+exec به یک دامنه با دسترسی کمتر در SELinux، همچنان باز خواهد ماند.
مثالها:
accept(sockfd, addr, addrlen); // becomes accept4(sockfd, addr, addrlen, SOCK_CLOEXEC);
android-cloexec-accept4
تابع accept4() باید SOCK_CLOEXEC را در آرگومان type خود بگنجاند تا از نشت توصیفکننده فایل جلوگیری شود. بدون این پرچم، یک فایل حساس باز شده در طول فراخوانی fork+exec به یک دامنه با دسترسی کمتر در SELinux باز خواهد ماند.
مثالها:
accept4(sockfd, addr, addrlen, SOCK_NONBLOCK); // becomes accept4(sockfd, addr, addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
android-cloexec-creat
استفاده از creat() توصیه نمیشود، بهتر است از open() استفاده شود.
مثالها:
int fd = creat(path, mode); // becomes int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
android-cloexec-dup
استفاده از dup() توصیه نمیشود؛ بهتر است از fcntl() استفاده شود که میتواند فلگ close-on-exec را تنظیم کند. در غیر این صورت، یک فایل حساسِ بازشده در طول یک فراخوانی fork+exec به یک دامنهٔ SELinux با سطح دسترسی پایینتر، همچنان باز باقی خواهد ماند.
مثالها:
int fd = dup(oldfd); // becomes int fd = fcntl(oldfd, F_DUPFD_CLOEXEC);
android-cloexec-epoll-create
استفاده از epoll_create() توصیه نمیشود؛ بهتر است از epoll_create1() استفاده شود که اجازهٔ close-on-exec را میدهد.
مثالها:
epoll_create(size); // becomes epoll_create1(EPOLL_CLOEXEC);
android-cloexec-epoll-create1
تابع epoll_create1() باید شامل EPOLL_CLOEXEC در آرگومان type خود باشد تا از نشت توصیفگر فایل (file descriptor) جلوگیری شود. بدون این فلگ، یک فایل حساس بازشده در طول فراخوانی fork+exec به یک دامنهٔ SELinux با دسترسی کمتر باز باقی خواهد ماند.
مثالها:
epoll_create1(0); // becomes epoll_create1(EPOLL_CLOEXEC);
android-cloexec-fopen
تابع fopen() باید شامل e در رشتهٔ mode خود باشد؛ بنابراین re معتبر خواهد بود. این کار معادل تنظیم فلگ FD_CLOEXEC روی آن توصیفگر است.
مثالها:
fopen("fn", "r");
// becomes
fopen("fn", "re");
android-cloexec-inotify-init
استفاده از inotify_init() توصیه نمیشود؛ بهتر است از inotify_init1() استفاده شود.
مثالها:
inotify_init(); // becomes inotify_init1(IN_CLOEXEC);
android-cloexec-inotify-init1
تابع inotify_init1() باید شامل IN_CLOEXEC در آرگومان type خود باشد تا از نشت توصیفگر فایل جلوگیری شود. بدون این فلگ، یک فایل حساس بازشده در طول یک فراخوانی fork+exec به یک دامنهٔ SELinux با سطح دسترسی پایینتر، باز باقی میماند.
مثالها:
inotify_init1(IN_NONBLOCK); // becomes inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
android-cloexec-memfd-create
تابع memfd_create() باید شامل MFD_CLOEXEC در آرگومان type خود باشد تا از نشت توصیفگر فایل جلوگیری شود. بدون این فلگ، یک فایل حساس بازشده در طول یک فراخوانی fork+exec به یک دامنهٔ SELinux با سطح دسترسی پایینتر باز خواهد ماند.
مثالها:
memfd_create(name, MFD_ALLOW_SEALING); // becomes memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC);
android-cloexec-open
یک منبع متداول از باگهای امنیتی، کدی است که فایلی را بدون استفاده از فلگ O_CLOEXEC باز میکند. بدون آن فلگ، یک فایل حساسِ بازشده در طول یک فراخوانی fork+exec به یک دامنهٔ SELinux با سطح دسترسی پایینتر همچنان باز مانده و آن دادههای حساس را افشا میکند. توابع همخانوادهٔ باز کردن فایل شامل open()، openat() و open64() باید شامل O_CLOEXEC در آرگومان flags خود باشند.
مثالها:
open("filename", O_RDWR);
open64("filename", O_RDWR);
openat(0, "filename", O_RDWR);
// becomes
open("filename", O_RDWR | O_CLOEXEC);
open64("filename", O_RDWR | O_CLOEXEC);
openat(0, "filename", O_RDWR | O_CLOEXEC);
android-cloexec-pipe
این بررسی استفاده از pipe() را تشخیص میدهد. استفاده از pipe() توصیه نمیشود، جایگزین پیشنهادی pipe2() است. این بررسی همچنین فلگ O_CLOEXEC را اضافه میکند که توصیفکننده فایل (file descriptor) را برای بسته شدن در فرآیندهای فرزند علامتگذاری میکند. بدون این فلگ، یک توصیفکننده فایل حساس ممکن است به یک فرآیند فرزند نشت کند، بهویژه به یک دامنه SELinux با سطح دسترسی پایینتر.
مثالها:
pipe(pipefd);
جایگزین پیشنهادی:
pipe2(pipefd, O_CLOEXEC);
android-cloexec-pipe2
این بررسی اطمینان حاصل میکند که pipe2() با فلگ O_CLOEXEC فراخوانی شود. این بررسی همچنین فلگ O_CLOEXEC را اضافه میکند که توصیفکننده فایل را برای بسته شدن در فرآیندهای فرزند علامتگذاری مینماید. بدون این فلگ، یک توصیفکننده فایل حساس ممکن است به یک فرآیند فرزند نشت کند، بهویژه به یک دامنه SELinux با سطح دسترسی پایینتر.
مثالها:
pipe2(pipefd, O_NONBLOCK);
جایگزین پیشنهادی:
pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);
android-cloexec-socket
تابع socket() باید شامل SOCK_CLOEXEC در آرگومان type خود باشد تا از نشت توصیفکننده فایل جلوگیری شود. بدون این فلگ، یک فایل حساس باز شده در حین fork+exec به یک دامنه SELinux با دسترسی کمتر، باز باقی میماند.
مثالها:
socket(domain, type, SOCK_STREAM); // becomes socket(domain, type, SOCK_STREAM | SOCK_CLOEXEC);
android-comparison-in-temp-failure-retry
مقایسههایی را که به نظر میرسد به اشتباه در آرگومان ماکروی TEMP_FAILURE_RETRY قرار گرفتهاند عیبیابی میکند. چنین استفادهای در اکثریت قریب به اتفاق موارد نادرست است و اغلب بدون هیچ هشداری هدف ماکروی TEMP_FAILURE_RETRY را بیاثر میکند.
جهت اطلاع، ماکروی TEMP_FAILURE_RETRY یک ماکروی کمکی https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html است که توسط هر دو کتابخانه glibc و Bionic ارائه میشود. هدف آن اجرای مکرر یک فراخوانی سیستمی (syscall) است تا زمانی که با موفقیت انجام شود، یا به دلایلی غیر از وقفهخوردن (interrupted) شکست بخورد.
نمونه استفاده دارای باگ بدین صورت است:
char cs[1];
while (TEMP_FAILURE_RETRY(read(STDIN_FILENO, cs, sizeof(cs)) != 0)) {
// Do something with cs.
}
چرا که TEMP_FAILURE_RETRY بررسی خواهد کرد که آیا نتیجه حاصل از مقایسه برابر با -1 است یا خیر، و در این صورت مجدداً تلاش میکند.
اگر با این مورد مواجه شدید، رفع آن ساده است: مقایسه را از داخل آرگومان TEMP_FAILURE_RETRY بیرون بکشید، به این صورت:
char cs[1];
while (TEMP_FAILURE_RETRY(read(STDIN_FILENO, cs, sizeof(cs))) != 0) {
// Do something with cs.
}
گزینهها
- RetryMacros
- فهرستی جداشده با کاما از نامهای ماکروهای تلاش مجدد (retry) جهت بررسی. پیشفرض TEMP_FAILURE_RETRY است.
boost-use-ranges
فراخوانیهای الگوریتمهای مبتنی بر تکرارکننده (iterator) کتابخانه استاندارد را که میتوانند با نسخه دامنههای Boost (Boost ranges) جایگزین شوند، شناسایی میکند.
مثال
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(), std::begin(Items2),
std::end(Items2));
تبدیل میشود به:
auto Iter1 = boost::range::find(Items, 0); auto AreSame = boost::range::equal(Items1, Items2);
الگوریتمهای پشتیبانیشده
فراخوانی الگوریتمهای زیر از کتابخانه std بررسی میشوند:
std::accumulate, std::adjacent_difference, std::adjacent_find, std::all_of, std::any_of, std::binary_search, std::copy_backward, std::copy_if, std::copy, std::count_if, std::count, std::equal_range, std::equal, std::fill, std::find_end, std::find_first_of, std::find_if_not, std::find_if, std::find, std::for_each, std::generate, std::includes, std::iota, std::is_partitioned, std::is_permutation, std::is_sorted_until, std::is_sorted, std::lexicographical_compare, std::lower_bound, std::make_heap, std::max_element, std::merge, std::min_element, std::mismatch, std::next_permutation, std::none_of, std::partial_sum, std::partial_sort_copy, std::partition_copy, std::partition_point, std::partition, std::pop_heap, std::prev_permutation, std::push_heap, std::random_shuffle, std::reduce, std::remove_copy_if, std::remove_copy, std::remove_if, std::remove, std::replace_copy_if, std::replace_copy, std::replace_if, std::replace, std::reverse_copy, std::reverse, std::search, std::set_difference, std::set_intersection, std::set_symmetric_difference, std::set_union, std::sort_heap, std::sort, std::stable_partition, std::stable_sort, std::transform, std::unique_copy, std::unique, std::upper_bound.
این بررسی همچنین توابع زیر را از فضای نام (namespace) boost::algorithm بررسی خواهد کرد:
all_of_equal, any_of_equal, any_of, apply_permutation, apply_reverse_permutation, clamp_range, copy_if_until, copy_if_while, copy_if, copy_until, copy_while, find_backward, find_if_backward, find_if_not_backward, find_if_not, find_not_backward, hex_lower, hex, iota, all_of, is_decreasing, is_increasing, is_palindrome, is_partitioned_until, is_partitioned, is_permutation, is_sorted_until, is_sorted, is_strictly_decreasing, is_strictly_increasing, none_of_equal, none_of, one_of_equal, one_of, partition_copy, partition_point, reduce, unhex.
پیمایش معکوس (Reverse Iteration)
اگر فراخوانیها با استفاده از تکرارکنندههای معکوس (reverse iterators) روی کانتینرها انجام شده باشند، کد با استفاده از تطبیقدهنده boost::adaptors::reverse اصلاح خواهد شد.
auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
std::crbegin(Items2), std::crend(Items2));
تبدیل میشود به:
auto AreSame = boost::range::equal(boost::adaptors::reverse(Items1),
boost::adaptors::reverse(Items2));
گزینهها
- IncludeStyle
- رشتهای که مشخص میکند کدام سبک include استفاده شود، llvm یا google. مقدار پیشفرض llvm است.
- IncludeBoostSystem
- اگر true باشد (مقدار پیشفرض)، هدرهای boost مانند هدرهای سیستمی با براکتهای زاویهدار گنجانده میشوند (#include <boost.hpp>)، در غیر این صورت از علامت نقلقول استفاده میشود (#include "boost.hpp").
- UseReversePipe
- هنگامی که true باشد (پیشفرض false است)، اصلاحاتی که شامل محدودههای معکوس هستند به جای ساختار تابعی، از ساختار تطبیقدهنده پایپ (pipe) استفاده خواهند کرد.
std::find(Items.rbegin(), Items.rend(), 0);
تبدیل میشود به:
boost::range::find(Items | boost::adaptors::reversed, 0);
boost-use-to-string
این بررسی تبدیل از انواع عدد صحیح مانند int به std::string یا std::wstring با استفاده از boost::lexical_cast را پیدا کرده، و آن را با فراخوانیهای std::to_string و std::to_wstring جایگزین میکند.
این بررسی با وجود سربارگذاریهای to_string، تبدیل از اعداد ممیز شناور (floating points) را جایگزین نمیکند، چرا که رفتار برنامه را تغییر میدهد.
auto str = boost::lexical_cast<std::string>(42); auto wstr = boost::lexical_cast<std::wstring>(2137LL); // Will be changed to auto str = std::to_string(42); auto wstr = std::to_wstring(2137LL);
bugprone-argument-comment
بررسی میکند که کامنتهای آرگومانها با نام پارامترها مطابقت داشته باشند.
این بررسی کامنتهای آرگومان را در قالب /*parameter_name=*/ که درست پیش از آرگومان قرار میگیرند، درک میکند.
void f(bool foo); ... f(/*bar=*/true); // warning: argument name 'bar' in comment does not match parameter name 'foo'
این بررسی تلاش میکند غلطهای املایی را تشخیص داده و اصلاحات خودکار برای آنها پیشنهاد دهد..SS گزینهها
- StrictMode
- هنگامی که false باشد، بررسی هنگام مقایسه نامها، زیرخطهای (underscore) ابتدا و انتها و بزرگی و کوچکی حروف را نادیده میگیرد -- در غیر این صورت آنها لحاظ میشوند. مقدار پیشفرض false است.
- IgnoreSingleArgument
- هنگامی که true باشد، بررسی از تکآرگومان صرفنظر خواهد کرد. مقدار پیشفرض false است.
- CommentBoolLiterals
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال بولی اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(bool TurnKey, bool PressButton); foo(true, false);
بعد:
void foo(bool TurnKey, bool PressButton); foo(/*TurnKey=*/true, /*PressButton=*/false);
- CommentIntegerLiterals
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال عدد صحیح اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(int MeaningOfLife); foo(42);
بعد:
void foo(int MeaningOfLife); foo(/*MeaningOfLife=*/42);
- CommentFloatLiterals
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال اعشاری float/double اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(float Pi); foo(3.14159);
بعد:
void foo(float Pi); foo(/*Pi=*/3.14159);
- CommentStringLiterals
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال رشتهای اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(const char *String);
void foo(const wchar_t *WideString);
foo("Hello World");
foo(L"Hello World");
بعد:
void foo(const char *String); void foo(const wchar_t *WideString); foo(/*String=*/"Hello World"); foo(/*WideString=*/L"Hello World");
- CommentCharacterLiterals
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال نویسهای اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(char *Character);
foo('A');
بعد:
void foo(char *Character); foo(/*Character=*/'A');
- CommentUserDefinedLiterals
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال تعریفشده توسط کاربر اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(double Distance); double operator"" _km(long double); foo(402.0_km);
بعد:
void foo(double Distance); double operator"" _km(long double); foo(/*Distance=*/402.0_km);
- CommentNullPtrs
- هنگامی که true باشد، بررسی یادداشتهای آرگومان را در قالب /*ParameterName=*/ درست پیش از لیترال nullptr اضافه میکند. مقدار پیشفرض false است.
قبل:
void foo(A* Value); foo(nullptr);
بعد:
void foo(A* Value); foo(/*Value=*/nullptr);
bugprone-assert-side-effect
موارد استفاده از assert() دارای اثر جانبی (side effect) را پیدا میکند.
شرط درون assert() تنها در ساختهای حالت اشکالزدایی (debug) ارزیابی میشود، بنابراین یک شرط دارای اثر جانبی میتواند باعث رفتارهای متفاوت در ساختهای debug و release شود.
گزینهها
- AssertMacros
- فهرستی از نامهای ماکروهای assert جداشده با کاما جهت بررسی شدن. پیشفرض assert,NSAssert,NSCAssert است.
- CheckFunctionCalls
- اینکه آیا با توابع عضو و غیرعضو غیر-const طوری رفتار شود که گویی اثرات جانبی تولید میکنند یا خیر. بهطور پیشفرض غیرفعال است زیرا میتواند تعداد هشدارهای مثبت کاذب را افزایش دهد.
- IgnoredFunctions
- فهرستی از نامهای توابع یا متدها جداشده با سمیکالن (نقطهویرگول) که باید فاقد اثر جانبی در نظر گرفته شوند. عبارات باقاعده (regex) پذیرفته میشوند، مانند [Rr]ef(erence)?$ که با هر نوعی با پسوند Ref، ref، Reference و reference مطابقت دارد. پیشفرض خالی است. اگر نامی در فهرست شامل توالی :: باشد، با نام نوع واجد شرایط کامل (یعنی namespace::Type) مطابقت داده میشود، در غیر این صورت تنها با نام نوع (یعنی Type) تطبیق داده میشود.
bugprone-assignment-in-if-condition
انتسابها (assignments) را درون شرطهای دستورات if پیدا میکند. چنین انتسابهایی مستعد باگ هستند زیرا ممکن است هدف از آنها بررسی تساوی بوده باشد.
این بررسی تمام انتسابهای درون شرطهای if را پیدا میکند، از جمله مواردی که بهدلیل وجود یک جفت پرانتز اضافه توسط -Wparentheses علامتگذاری نشدهاند، و همچنین انتسابهایی که یک operator=() بارگذاریمجدد شده را فراخوانی میکنند. انتسابهای شناساییشده، قانون «Rule 8.2.c» از گروه BARR را نقض میکنند https://barrgroup.com/embedded-systems/books/embedded-c-coding-standard/statement-rules/if-else-statements.
int f = 3;
if(f = 4) { // This is identified by both `Wparentheses` and this check - should it have been: `if (f == 4)` ?
f = f + 1;
}
if((f == 5) || (f = 6)) { // the assignment here `(f = 6)` is identified by this check, but not by `-Wparentheses`. Should it have been `(f == 6)` ?
f = f + 2;
}
bugprone-bad-signal-to-kill-thread
فراخوانیهای تابع pthread_kill را در مواردی پیدا میکند که یک ترد با ارسال سیگنال SIGTERM خاتمه مییابد و این سیگنال کل فرآیند را از بین میبرد، نه تنها همان ترد مجزا را. از هر سیگنالی به جز SIGTERM استفاده کنید.
pthread_kill(thread, SIGTERM);
این بررسی با قاعده استانداردهای کدنویسی CERT C تحت عنوان POS44-C. Do not use signals to terminate threads مطابقت دارد https://wiki.sei.cmu.edu/confluence/display/c/POS44-C.+Do+not+use+signals+to+terminate+threads.
بررسی cert-pos44-c بهعنوان نام مستعاری از این بررسی به اینجا تغییر مسیر میدهد.
bugprone-bitwise-pointer-cast
در مورد کدهایی که سعی دارند با استفاده از std::bit_cast یا memcpy بین اشارهگرها تبدیل نوع (cast) انجام دهند، هشدار میدهد.
انگیزه این است که std::bit_cast به عنوان جایگزینی امن برای تبدیل نوع با استفاده از reinterpret_cast در مدرن C++ تبلیغ میشود. با این حال، نباید کورکورانه reinterpret_cast را با std::bit_cast به صورت زیر جایگزین کرد:
int x{};
-float y = *reinterpret_cast<float*>(&x);
+float y = *std::bit_cast<float*>(&x);
این جایگزینی مستقیم دقیقاً رفتاری مشابه با reinterpret_cast دارد، و همچنان رفتار تعریفنشده (Undefined Behavior) رخ میدهد. std::bit_cast بایتهای اشارهگر ورودی را (و نه شیء اشارهشده را) درون یک اشارهگر خروجی از نوعی دیگر کپی میکند، که ممکن است قواعد strict aliasing را نقض کند. با این وجود، با نگاه کردن ساده به کد، به نظر «امن» میرسد، چرا که از std::bit_cast استفاده میکند که به عنوان امن معرفی شده است.
راهکار برای تبدیل نوع امن، اعمال std::bit_cast روی نوعهای مقداری (value types) است، نه روی نوعهای اشارهگر:
int x{};
float y = std::bit_cast<float>(x);
به این ترتیب، بایتهای شیء ورودی در شیء خروجی کپی میشوند که بسیار امنتر است. توجه داشته باشید که اگر مقداری از نوع To متناظر با نمایش مقداری تولیدشده وجود نداشته باشد، رفتار تعریفنشده هنوز میتواند رخ دهد. کامپایلرها ممکن است بتوانند این کپی را بهینهسازی کرده و کدهای اسمبلی همسانی با نسخه اولیه reinterpret_cast تولید کنند.
کدهای قبل از C++20 ممکن است std::bit_cast را با استفاده از memcpy شبیهسازی کرده، یا صرفاً مستقیماً memcpy را فراخوانی کنند که به همان اندازه مشکلساز است. این مورد نیز توسط این بررسی شناسایی میشود:
int* x{};
float* y{};
std::memcpy(&y, &x, sizeof(x));
همچنین، اگر واقعاً تبدیل نوع میان اشارهگرها مد نظر است، باید از reinterpret_cast استفاده شود تا هدف را به وضوح منتقل کرده و هشدارهای کامپایلرها و لینترها فعال شوند، که باید به درستی رفع گردند.
bugprone-bool-pointer-implicit-conversion
شرطهای مبتنی بر تبدیل نوع ضمنی (implicit conversion) از یک اشارهگر bool به bool را بررسی میکند.
مثال:
bool *p;
if (p) {
// Never used in a pointer-specific way.
}
bugprone-branch-clone
شاخه'های تکراری را در زنجیره'های if/else if/else، شاخه'های تکراری متوالی را در دستورهای switch و شاخه'های یکسان true و false را در عملگرهای شرطی بررسی می'کند.
if (test_value(x)) {
y++;
do_something(x, y);
} else {
y++;
do_something(x, y);
}
در این مثال ساده (که می'تواند مثلاً بر اثر خطای کپی-پیست رخ دهد)، شاخه'های then و else یکسان هستند و کد با کد کوتاهتر و تمیزتر زیر معادل است:
test_value(x); // can be omitted unless it has side effects y++; do_something(x, y);
اگر این رفتار مورد نظر باشد، دلیلی برای استفاده از یک عبارت شرطی وجود ندارد؛ در غیر این صورت، مشکل با اصلاح شاخه'ای که به نادرستی مدیریت شده است، برطرف می'شود.
این بررسی، شاخه'های تکراری را در زنجیره'های طولانیتر if/else if/else نیز تشخیص می'دهد؛ جاهایی که متوجه شدن مشکل در آنها حتی دشوارتر است.
این بررسی همچنین دستورهای تکراری if درونی و بیرونی را که ممکن است نتیجه خطای کپی-پیست باشند، تشخیص می'دهد. این بررسی در حال حاضر نمیتواند دستورهای if درونی و بیرونی یکسان را در صورتی که کدی بین شروط if قرار گرفته باشد، شناسایی کند. نمونهای از آن به شرح زیر است:
void test_warn_inner_if_1(int x) {
if (x == 1) { // warns, if with identical inner if
if (x == 1) // inner if is here
;
if (x == 1) { // does not warn, cannot detect
int y = x;
if (x == 1)
;
}
}
در دستورهای switch، این بررسی تنها زمانی شاخه'های تکراری را گزارش می'کند که متوالی باشند؛ زیرا نسبتاً معمول است که برچسبهای case: دارای یک ترتیب طبیعی باشند و مرتبسازی مجدد آنها خوانایی کد را کاهش دهد. برای مثال:
switch (ch) {
case 'a':
return 10;
case 'A':
return 10;
case 'b':
return 11;
case 'B':
return 11;
default:
return 10;
}
در اینجا، بررسی گزارش می'دهد که شاخه'های 'a' و 'A' یکسان هستند (و شاخه'های 'b' و 'B' نیز یکسان هستند)، اما گزارش نمی'کند که شاخه default: نیز با دو شاخه اول یکسان است. اگر این واقعاً رفتار درستی باشد، می'توان آن را به شکل زیر پیادهسازی کرد:
switch (ch) {
case 'a':
case 'A':
return 10;
case 'b':
case 'B':
return 11;
default:
return 10;
}
در اینجا، بررسی برای return 10; تکراری هشدار نمی'دهد، که اگر بخواهیم حفظ کنیم که 'a' قبل از 'b' باشد و default: آخرین شاخه باشد، مناسب است.
موارد switch که با صفت [[fallthrough]] مشخص شدهاند، نادیده گرفته میشوند.
در نهایت، این بررسی همچنین عملگرهای شرطی را بررسی کرده و کدهایی شبیه به مورد زیر را گزارش می'دهد:
return test_value(x) ? x : x;
برخلاف دستورهای if، این بررسی زنجیرههایی از عملگرهای شرطی را شناسایی نمی'کند.
نکته: این بررسی مواردی را نیز گزارش می'کند که شاخهها تنها پس از پیشپردازش یکسان می'شوند.
bugprone-capturing-this-in-member-variable
گرفتنهای لامبدا (lambda captures) که اشارهگر this را ضبط کرده و به عنوان اعضای کلاس ذخیره میکنند بدون اینکه سازندههای کپی و انتقال (copy/move constructors) و عملگرهای انتساب را مدیریت کنند، پیدا میکند.
گرفتن this در یک لامبدا و ذخیره آن به عنوان عضو کلاس خطرناک است زیرا طول عمر لامبدا میتواند بیشتر از شیئی باشد که آن را ضبط میکند. به ویژه هنگامی که شیء کپی یا جابهجا (move) میشود، اشارهگر ضبطشده this بهطور ضمنی به شیء جدید منتقل خواهد شد. اغلب اوقات، افراد تصور میکنند که اشارهگر ضبطشده this به شیء جدید اشاره دارد که این موضوع منجر به باگ خواهد شد.
struct C {
C() : Captured([this]() -> C const * { return this; }) {}
std::function<C const *()> Captured;
};
void foo() {
C v1{};
C v2 = v1; // v2.Captured capture v1's 'this' pointer
assert(v2.Captured() == v1.Captured()); // v2.Captured capture v1's 'this' pointer
assert(v2.Captured() == &v2); // assertion failed.
}
- راهحلهای ممکن:
- علامتگذاری سازندههای کپی و انتقال و عملگرهای انتساب به صورت حذفشده (deleted).
- استفاده از متد عضو کلاس به جای متغیر عضو کلاس با انواع شیء تابعی (function object).
- ارسال اشارهگر this به عنوان پارامتر.
گزینهها
- FunctionWrapperTypes
- فهرستی از نامهای انواع داده که با نقطهویرگول (semicolon) از هم جدا شدهاند. برای مشخص کردن بستهبندیهای تابعی (function wrapper) که میتوانند عبارات لامبدا را نگه دارند استفاده میشود. مقدار پیشفرض عبارت است از ::std::function;::std::move_only_function;::boost::function.
- BindFunctions
- فهرستی از نامهای کامل واجد شرایط (fully qualified) توابعی که میتوانند اشارهگر this را ضبط (capture) کنند، که با نقطهویرگول از هم جدا شدهاند. مقدار پیشفرض عبارت است از ::std::bind;::boost::bind;::std::bind_front;::std::bind_back; ::boost::compat::bind_front;::boost::compat::bind_back.
bugprone-casting-through-void
عملیات تبدیل نوع (cast) دو مرحلهای ناامن یا زائد که شامل void* میشوند را شناسایی میکند، که طبق استاندارد C++ در https://eel.is/c++draft/expr.reinterpret.cast#7 معادل reinterpret_cast است.
تبدیلهای نوع دو مرحلهای از طریق void* به دلایل متعددی توصیه نمیشوند.
- آنها کد را مبهم کرده و خوانایی و درک آن را مختل میکنند، که در نتیجه نگهداری کد را پیچیده میسازد.
- این تبدیلها پشتیبانیهای ارزشمند کامپایلر را دور میزنند و هشدارهای مربوط به همترازی اشارهگر (pointer alignment) را نادیده میگیرند. این امر ممکن است قاعده strict aliasing را نقض کرده و منجر به رفتار تعریفنشده (undefined behavior) شود.
- در سناریوهایی که شامل وراثت چندگانه (multiple inheritance) هستند، به دلیل از دست رفتن اطلاعات نوع، ممکن است ابهام و نتایج غیرمنتظرهای رخ دهد که مشکلات زمان اجرا ایجاد میکند.
به طور خلاصه، اجتناب از تبدیلهای نوع دو مرحلهای از طریق void* تضمین میکند که کد شفافتر باقی بماند، هشدارهای ضروری کامپایلر حفظ شوند، و از ابهام و خطاهای احتمالی زمان اجرا، بهویژه در سناریوهای پیچیده وراثت، جلوگیری شود. اگر چنین تبدیلی مد نظر است، باید از طریق reinterpret_cast انجام شود تا هدف به صورت واضحتری بیان گردد.
توجه: انتظار میرود پس از اعمال اصلاح پیشنهادی و استفاده از reinterpret_cast، بررسی cppcoreguidelines-pro-type-reinterpret-cast <> هشداری صادر کند. این رفتار عمدی است: reinterpret_cast عملیاتی خطرناک است که میتواند به راحتی قوانین strict aliasing را هنگام درک ارجاع (dereferencing) اشارهگر تبدیلشده نقض کرده و موجب رفتار تعریفنشده (Undefined Behavior) شود. این هشدار برای این قرار داده شده است که به کاربران گوشزد کند با دقت بررسی کنند که آیا استفاده از reinterpret_cast ایمن است یا خیر، که در صورت ایمن بودن میتوان این هشدار را نادیده گرفت (suppress کرد).
مثالها:
using IntegerPointer = int *;
double *ptr;
static_cast<IntegerPointer>(static_cast<void *>(ptr)); // WRONG
reinterpret_cast<IntegerPointer>(reinterpret_cast<void *>(ptr)); // WRONG
(IntegerPointer)(void *)ptr; // WRONG
IntegerPointer(static_cast<void *>(ptr)); // WRONG
reinterpret_cast<IntegerPointer>(ptr); // OK, clearly expresses intent.
// NOTE: dereferencing this pointer violates
// the strict aliasing rules, invoking
// Undefined Behavior.
bugprone-chained-comparison
این بررسی عملگرهای مقایسهای زنجیرهای را که میتوانند منجر به رفتارهای ناخواسته یا خطاهای منطقی شوند، شناسایی میکند.
مقایسههای زنجیرهای عباراتی هستند که از چندین عملگر مقایسه برای مقایسه سه مقدار یا بیشتر استفاده میکنند. به عنوان مثال، عبارت a < b < c مقادیر a، b و c را مقایسه میکند. با این حال، این عبارت به صورت (a < b) && (b < c) که احتمالاً مورد نظر توسعهدهنده بوده ارزیابی نمیشود. در عوض، به شکل (a < b) < c ارزیابی میشود که ممکن است نتایج ناخواستهای به همراه داشته باشد، به ویژه زمانی که انواع داده a، b و c متفاوت باشند.
برای جلوگیری از چنین خطاهایی، هنگام شناسایی عملگر مقایسه زنجیرهای، این بررسی هشداری صادر کرده و پیشنهاد میکند که از پرانتز برای مشخص کردن اولویت ارزیابی یا از یک عملگر منطقی برای جداسازی عبارات مقایسهای استفاده شود.
مثالهای زیر را در نظر بگیرید:
int a = 2, b = 6, c = 4;
if (a < b < c) {
// This block will be executed
}
در این مثال، قصد توسعهدهنده بررسی این بود که آیا a از b کوچکتر است و b از c کوچکتر است یا خیر. با این حال، عبارت a < b < c معادل (a < b) < c است. از آنجا که a < b برابر true است، عبارت (a < b) < c به عنوان 1 < c ارزیابی میشود که معادل true < c است و در این حالت نادرست است، زیرا b < c مقدار false دارد.
حتی با وجود اینکه مشکل بالا را میتوان به عنوان مقایسه int با bool شناسایی کرد، مثال خطرناکتری نیز وجود دارد:
bool a = false, b = false, c = true;
if (a == b == c) {
// This block will be executed
}
در این مثال، قصد توسعهدهنده این بوده است که بررسی کند آیا a، b و c همگی برابر هستند یا خیر. با این وجود، عبارت a == b == c به صورت (a == b) == c ارزیابی میشود. از آنجا که a == b درست (true) است، عبارت (a == b) == c به عنوان true == c ارزیابی میشود که معادل true == true است. این مقایسه نتیجه true میدهد، با وجود اینکه مقادیر a و b برابر false بوده و با c برابر نیستند.
برای جلوگیری از این مشکل، توسعهدهنده میتواند از یک عملگر منطقی برای جداسازی عبارات مقایسه استفاده کند، مانند زیر:
if (a == b && b == c) {
// This block will not be executed
}
همچنین، استفاده از پرانتز در عبارات مقایسهای میتواند قصد توسعهدهنده را واضحتر بیان کرده و به جلوگیری از سوءتفاهم کمک کند.
if ((a == b) == c) {
// This block will be executed
}
گزینهها
- IgnoreMacros
- اگر true باشد، این بررسی در مورد مقایسههای زنجیرهای داخل ماکروها هشدار نخواهد داد. پیشفرض false است.
bugprone-command-processor
فراخوانیهای تابعهای system()، popen() و _popen() را که یک پردازشگر دستور (command processor) را اجرا میکنند، علامتگذاری میکند. این بررسی فراخوانیهای system() با آرگومان اشارهگر null را علامتگذاری نمیکند، زیرا چنین فراخوانی صرفاً وجود یک پردازشگر دستور را بررسی میکند و در عمل تلاشی برای اجرای یک دستور انجام نمیدهد.
مراجع
این بررسی با قاعدهٔ استاندارد کدنویسی CERT C زیر مطابقت دارد: ENV33-C. Do not call system() <https://www.securecoding.cert.org/confluence/display/c/ENV33-C.+Do+not+call+system()>.
bugprone-compare-pointer-to-member-virtual-function
رفتارهای مشخصنشده (unspecified behavior) مربوط به مقایسهٔ برابری بین یک اشارهگر به تابع مجازی عضو و هر چیزی غیر از یک مقدار ثابت اشارهگر پوچ (null-pointer-constant) را شناسایی میکند.
struct A {
void f1();
void f2();
virtual void f3();
virtual void f4();
void g1(int);
};
void fn() {
bool r1 = (&A::f1 == &A::f2); // ok
bool r2 = (&A::f1 == &A::f3); // bugprone
bool r3 = (&A::f1 != &A::f3); // bugprone
bool r4 = (&A::f3 == nullptr); // ok
bool r5 = (&A::f3 == &A::f4); // bugprone
void (A::*v1)() = &A::f3;
bool r6 = (v1 == &A::f1); // bugprone
bool r6 = (v1 == nullptr); // ok
void (A::*v2)() = &A::f2;
bool r7 = (v2 == &A::f1); // false positive, but potential risk if assigning other value to v2.
void (A::*v3)(int) = &A::g1;
bool r8 = (v3 == &A::g1); // ok, no virtual function match void(A::*)(int) signature.
}
هشدارهایی را دربارهٔ مقایسههای برابری شامل اشارهگرها به توابع مجازی عضو یا متغیرهایی که احتمالاً اشارهگری به یک تابع مجازی عضو هستند با هر موجودیتی غیر از یک مقدار ثابت اشارهگر null ارائه میدهد.
در برخی از کامپایلرها، آدرسهای توابع مجازی اشارهگرهای معمولی نیستند بلکه از آفستها و اندیسهایی در داخل جدول توابع مجازی (vtable) تشکیل شدهاند. در نتیجه، این اشارهگرها ممکن است بین کلاسهای پایه و مشتقشده متفاوت باشند که باعث بروز رفتارهای غیرقابلپیشبینی هنگام مقایسهٔ مستقیم آنها میشود. این مسئله بهویژه هنگام کار با اشارهگرها به توابع مجازی محض (pure virtual) دشوارتر میشود، زیرا آنها ممکن است حتی یک آدرس معتبر نیز نداشته باشند که این امر مقایسهها را پیچیدهتر میکند.
به جای آن، توصیه میشود از عملگر typeid یا سایر سازوکارهای مناسب برای مقایسهٔ اشیاء استفاده کنید تا از رفتار پایدار و قابلپیشبینی در کد خود اطمینان حاصل نمایید. با توجه به این هشدار و بهکارگیری روش مقایسهٔ مطمئنتر، میتوانید مشکلات بالقوهٔ مربوط به رفتارهای مشخصنشده را بهویژه هنگام کار با اشارهگر به توابع مجازی عضو یا توابع مجازی محض کاهش داده و در نتیجه پایداری و نگهداریپذیری کلی کد خود را بهبود بخشید. در سناریوهای مربوط به اشارهگر به توابع مجازی عضو، فقط استفاده از nullptr برای مقایسه توصیه میشود.
محدودیتها
مقادیر ذخیرهشده در یک متغیر را تحلیل نمیکند. برای متغیرها، تنها تمامی متدهای مجازی در همان class یا struct را بررسی میکند و زمانی که انتساب یک اشارهگر به تابع مجازی عضو به این متغیر امکانپذیر باشد، عیبیابی را انجام میدهد.
bugprone-copy-constructor-init
سازندههای رونوشتی (copy constructors) را پیدا میکند که در آنها سازنده، سازندهٔ رونوشت کلاس پایه را فراخوانی نمیکند.
class Copyable {
public:
Copyable() = default;
Copyable(const Copyable &) = default;
int memberToBeCopied = 0;
};
class X2 : public Copyable {
X2(const X2 &other) {} // Copyable(other) is missing
};
همچنین سازندههای رونوشتی را پیدا میکند که در آنها سازندهٔ کلاس پایه بدون پارامتر فراخوانی شده است.
class X3 : public Copyable {
X3(const X3 &other) : Copyable() {} // other is missing
};
عدم مقداردهی اولیهٔ صحیح زیراشیاء (sub-objects) کلاس پایه در حین ساخت رونوشت میتواند منجر به رفتار تعریفنشده (undefined behavior)، کرش کردن برنامه، خرابی دادهها یا سایر پیامدهای غیرمنتظره شود. این بررسی اطمینان حاصل میکند که سازندهٔ رونوشت یک کلاس مشتقشده، سازندهٔ رونوشت کلاس پایه را بهدرستی فراخوانی کند و بدین ترتیب از باگها جلوگیری کرده و کیفیت کد را بهبود میبخشد.
محدودیتها
- برای کلاسهای خالی هشداری تولید نمیکند، زیرا اعضایی در کلاس (شامل زیراشیاء کلاس پایه) وجود ندارد که نگران آنها بود.
- برای کلاسهای پایهای که سازندهٔ رونوشت آنها private یا deleted است هشداری ایجاد نمیکند.
- برای کلاسهای پایهای که با استفاده از سازندههای غیراصلی (non-default) دیگر مقداردهی اولیه شدهاند هشداری تولید نمیکند، چرا که این موضوع ممکن است عمدی باشد.
این بررسی همچنین در برخی موارد پیشنهادهایی برای اصلاح خودکار (fix-its) ارائه میدهد.
bugprone-copy-constructor-mutates-argument
انتسابها به شیء کپیشده و اعضای مستقیم یا غیرمستقیم آن را در سازندههای رونوشت و عملگرهای انتساب رونوشت پیدا میکند.
این بررسی با قاعدهٔ استاندارد کدنویسی CERT C زیر مطابقت دارد: OOP58-CPP. Copy operations must not mutate the source object https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP58-CPP.+Copy+operations+must+not+mutate+the+source+object.
bugprone-crtp-constructor-accessibility
استفادههای مستعد خطای الگوی تکرارشوندهٔ عجیب قالب (Curiously Recurring Template Pattern یا CRTP) را در صورتی که نمونهسازی از CRTP در خارج از خود آن و کلاس مشتقشده امکانپذیر باشد، تشخیص میدهد.
الگوی CRTP اصطلاحی است که در آن یک کلاس از یک کلاس قالب مشتق میشود، در حالی که خودش آرگومان آن قالب است. باید اطمینان حاصل شود که اگر کلاسی قرار است به عنوان یک کلاس پایه در این الگو به کار رود، تنها در صورتی بتوان از آن نمونهسازی کرد که کلاس مشتقشده آرگومان قالب آن باشد.
مثال:
template <typename T> class CRTP {
private:
CRTP() = default;
friend T;
};
class Derived : CRTP<Derived> {};
در زیر برخی از اشتباهات رایج که امکان نقض این الگو را فراهم میکنند، مشاهده میشود.
اگر سازندهٔ کلاسی که قرار است در CRTP استفاده شود به صورت public باشد، به کاربران اجازه میدهد آن کلاس را به طور مستقل نمونهسازی کنند.
مثال:
template <typename T> class CRTP {
public:
CRTP() = default;
};
class Good : CRTP<Good> {};
Good GoodInstance;
CRTP<int> BadInstance;
اگر سازنده به صورت protected باشد، از احتمال نمونهسازی تصادفی جلوگیری میشود، اما ممکن است خطایی را پنهان کند که در آن کلاسی متفاوت به جای کلاس مشتقشده به عنوان پارامتر قالب استفاده شود.
مثال:
template <typename T> class CRTP {
protected:
CRTP() = default;
};
class Good : CRTP<Good> {};
Good GoodInstance;
class Bad : CRTP<Good> {};
Bad BadInstance;
برای اطمینان از عدم وقوع نمونهسازی تصادفی، بهترین روش این است که سازنده private تعریف شود و کلاس مشتقشده به عنوان friend اعلام گردد. توجه داشته باشید که به عنوان یک مصالحه، این کار به کلاس مشتقشده دسترسی به سایر اعضای خصوصی (private) کلاس CRTP را نیز اعطا میکند. با این حال، سازندهها همچنان میتوانند در صورت حذف شدن (deleted)، به صورت public یا protected باشند.
مثال:
template <typename T> class CRTP {
CRTP() = default;
friend T;
};
class Good : CRTP<Good> {};
Good GoodInstance;
class Bad : CRTP<Good> {};
Bad CompileTimeError;
CRTP<int> AlsoCompileTimeError;
محدودیتها
- این بررسی در نسخههای پایینتر از C++11 پشتیبانی نمیشود
- این بررسی مواردی را که کلاس مشتقشده به عنوان یک آرگومان قالب متغیر (variadic template) ارسال میشود مدیریت نمیکند
- توابع در دسترس که میتوانند CRTP را بسازند، مانند توابع کارخانهای (factory functions)، بررسی نمیشوند
این بررسی همچنین در برخی موارد اصلاحات خودکار (fix-its) پیشنهاد میدهد.
bugprone-dangling-handle
ارجاعهای معلق (dangling references) را در هندلهای مقداری مانند std::string_view شناسایی میکند. این ارجاعهای معلق میتوانند نتیجه ساختن هندلها از مقادیر موقت باشند، جایی که مقدار موقت اندکی پس از ایجاد هندل از بین میرود.
مثالها:
string_view View = string(); // View will dangle.
string A;
View = A + "A"; // still dangle.
vector<string_view> V;
V.push_back(string()); // V[0] is dangling.
V.resize(3, string()); // V[1] and V[2] will also dangle.
string_view f() {
// All these return values will dangle.
return string();
string S;
return S;
char Array[10]{};
return Array;
}
span<int> g() {
array<int, 1> V;
return {V};
int Array[10]{};
return {Array};
}
گزینهها
- HandleClasses
- یک فهرست جدا شده با نقطه-ویرگول از نام کلاسهایی که باید به عنوان هندل در نظر گرفته شوند. به طور پیشفرض فقط std::basic_string_view، std::experimental::basic_string_view و std::span در نظر گرفته میشوند.
bugprone-default-operator-new-on-overaligned-type
استفاده از operator new پیشفرض را در مواردی که نوع داده دارای تراز گسترشیافته (ترازی بزرگتر از تراز بنیادی / fundamental alignment) است علامتگذاری میکند.
در صورتی که تراز درخواستی کمتر یا مساوی با تراز بنیادی باشد، عملکرد operator new پیشفرض تضمینکننده ارائه تراز صحیح است. تنها مواردی شناسایی میشوند (بر اساس طراحی) که در آنها operator new توسط کاربر تعریف نشده و از نوع placement new نباشد (دلیل آن این است که در این موارد فرض بر این است که کاربر تخصیص حافظه صحیح را فراهم کرده است).
مراجع
این بررسی با قاعده MEM57-CPP از استاندارد کدنویسی CERT C++ مطابقت دارد: Avoid using default operator new for over-aligned types https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM57-CPP.+Avoid+using+default+operator+new+for+over-aligned+types.
bugprone-derived-method-shadowing-base-method
متدهای کلاس مشتقشده را که یک متد (غیر مجازی) از کلاس پایه را پنهان (shadow) میکنند، پیدا میکند.
برای اینکه متدها «پنهانکننده» (shadowing) در نظر گرفته شوند، باید امضای یکسانی داشته باشند (یعنی نام یکسان، تعداد پارامترهای یکسان، انواع پارامتر یکسان و غیره). فقط متدهای عمومی (public) و غیر قالبی (non-templated) بررسی میشوند.
مثال زیر مستعد باگ است زیرا استفادهکنندگان از کلاس Derived انتظار دارند متد reset علاوه بر کارهای اضافه مورد نیاز برای بازنشانی کلاس Derived، کار Base::reset() را نیز انجام دهد. اصلاحات رایج عبارتند از:
- چندریختی (polymorphic) کردن متد reset
- تغییر نام Derived::reset در صورتی که قرار نیست با Base::reset تلاقی داشته باشد
- استفاده از using Base::reset برای تغییر مشخصکننده دسترسی (access specifier)
این موضوع همچنین نقض اصل جایگزینی لیسکوف (Liskov Substitution Principle) است.
struct Base {
void reset() {/* reset the base class */};
};
struct Derived : public Base {
void reset() {/* reset the derived class, but not the base class */};
};
bugprone-dynamic-static-initializers
نمونههایی از متغیرهای ایستا (static) را که به صورت پویا در فایلهای هدر مقداردهی اولیه میشوند، پیدا میکند.
این امر میتواند در برخی زمینههای چندنخی مشکلاتی ایجاد کند. به عنوان مثال، هنگام غیرفعال کردن دستورالعملهای همگامسازی تولید شده توسط کامپایلر برای متغیرهای ایستا که در زمان اجرا مقداردهی میشوند (مثلاً با -fno-threadsafe-statics)، حتی اگر یک پروژه خاص اقدامات احتیاطی لازم را برای جلوگیری از شرایط مسابقه (race conditions) در طول مقداردهی اولیه با ارائه همگامسازی اختصاصی خود انجام دهد، فایلهای هدر گنجاندهشده از پروژههای دیگر ممکن است این کار را نکنند. بنابراین، چنین بررسیای برای اطمینان از اینکه غیرفعال کردن همگامسازی تولید شده توسط کامپایلر برای مقداردهی متغیرهای ایستا مشکلی ایجاد نمیکند، مفید است.
کد زیر را در نظر بگیرید:
int foo() {
static int k = bar();
return k;
}
هنگامی که همگامسازی مقداردهی اولیه متغیرهای ایستا غیرفعال باشد، اگر دو نخ هر دو تابع foo را برای اولین بار فراخوانی کنند، این احتمال وجود دارد که k دو بار مقداردهی اولیه شود و یک شرایط مسابقه ایجاد گردد.
bugprone-easily-swappable-parameters
تعاریف توابعی را پیدا میکند که در آنها پارامترهایی از انواع قابل تبدیل مستقیماً پشت سر هم قرار گرفتهاند، که این امر فراخوانیها را مستعد ارسال آرگومانها با ترتیب جابهجا شده (یا نادرست) میکند.
void drawPoint(int X, int Y) { /* ... */ }
FILE *open(const char *Dir, const char *Name, Flags Mode) { /* ... */ }
یک فراخوانی احتمالی مانند drawPoint(-2, 5) یا openPath("a.txt", "tmp", Read) از نظر زبان کاملاً معتبر است، اما ممکن است مد نظر توسعهدهنده تابع نبوده باشد.
برای جلوگیری از ترتیب اشتباه آرگومانها، باید از ساختارهای دقیقتر و نوعامنتر (type-safe)، مانند opaque typedefها یا انواع قوی (strong types) استفاده شود.
struct Coord2D { int X; int Y; };
void drawPoint(const Coord2D Pos) { /* ... */ }
FILE *open(const Path &Dir, const Filename &Name, Flags Mode) { /* ... */ }
به دلیل بازسازی کد (refactoring) بالقوه پیچیده و شکستن احتمالی API که برای تقویت ایمنی انواع در یک پروژه ضروری است، هیچ اصلاح خودکاری (fix-its) ارائه نمیشود.
گزینهها
گزینههای گسترش/تسهیل
گزینههای تسهیل (یا گسترش) میتوانند برای وسیعتر کردن دامنهٔ تحلیل و تنظیم دقیق فعالسازی ترکیبهای بیشتر میان انواع داده به کار روند. برخی از ترکیبها ممکن است به شیوهٔ کدنویسی یا ترجیحات خاص یک پروژه وابسته باشند، با این حال باید توجه داشت که فعال کردن تمام این تسهیلها بیشترین انطباق را با شیوهٔ جابهجایی در فراخوانیها مدلسازی میکند. انتظار میرود این گزینهها موجب شوند بررسی برای توابع بیشتری گزارش دهد و دامنههای قابل جابهجایی طولانیتری را پوشش دهد.
- QualifiersMix
- آیا
پارامترهای
دارای یک
نوع با
مشخصههای
cvr-qualified و نوع T
با
مشخصههای
cvr-qualified متفاوت
(مثلاً T و const
T، const T و volatile T و
غیره) قابل
جابهجایی
با یکدیگر
در نظر
گرفته شوند
یا خیر. اگر
false باشد،
بررسی،
انواع
دارای
مشخصههای
متفاوت را
غیرقابل
جابهجایی
در نظر
میگیرد.
مقدار True
هشدارها را
فعال
میکند.
پیشفرض false
است.
مثال زیر تنها در صورتی یک گزارش عیبیابی تولید میکند که QualifiersMix فعال باشد:
void *memcpy(const void *Destination, void *Source, std::size_t N) { /* ... */ }
- ModelImplicitConversions
- آیا
پارامترهایی
از نوع T و U
در صورتی که
تبدیل ضمنی
(implicit conversion) از T به U
و از U به T
وجود داشته
باشد، قابل
جابهجایی
در نظر
گرفته شوند
یا خیر. اگر
false باشد،
بررسی
انواع
دارای
قابلیت
تبدیل ضمنی
را برای
جابهجایی
در نظر
نمیگیرد.
مقدار True
هشدارها را
برای
تبدیلهای
ضمنی فعال
میکند.
پیشفرض true
است.
مثالهای زیر تنها در صورتی یک گزارش عیبیابی تولید میکنند که ModelImplicitConversions فعال باشد:
void fun(int Int, double Double) { /* ... */ }
void compare(const char *CharBuf, std::string String) { /* ... */ }
نکته:
برای نمونه، قطعهکد زیر تنها در صورتی یک پیام عیبیابی تولید میکند که هر دوی گزینههای QualifiersMix و ModelImplicitConversions فعال باشند:
void fun2(int Int, const double Double) { /* ... */ }
گزینههای فیلترسازی
گزینههای فیلترسازی میتوانند برای کاهش حجم گزارشهای عیبیابی ارائهشده توسط بررسیکننده به کار روند؛ خواه هدف نادیده گرفتن ساختارهای خاص باشد یا کاستن از هشدارهای ناخواسته (نویز).
- MinimumLength
- حداقل طول
مورد نیاز
برای یک
توالی از
پارامترهای
مجاور جهت
قرار گرفتن
در گزارش
عیبیابی.
پیشفرض 2
است.
میتواند
هر عدد صحیح
مثبت
بزرگتر یا
مساوی با 2
باشد. اگر 0
یا 1 مشخص
شود، مقدار
پیشفرض 2
به جای آن
استفاده
خواهد شد.
برای نمونه، اگر 3 تعیین شود، مثالهای بالا مطابقت داده نخواهند شد.
- IgnoredParameterNames
- فهرستی از
نامهای
پارامتر که
هرگز نباید
بخشی از یک
توالی
پارامترهای
مجاور قابل
جابهجایی
در نظر
گرفته شوند.
این مقدار،
فهرستی از
نامهاست
که با ; جدا
شدهاند.
برای
نادیده
گرفتن
پارامترهای
بدون نام،
"" را به
صورت لفظی
به فهرست
اضافه کنید
(نه رشتهٔ
خالی، بلکه
دو علامت
نقلقول،
احتمالاً
با نویسهٔ
گریز!). این
گزینه به
حروف کوچک و
بزرگ حساس
است!
به صورت پیشفرض، نامهای پارامتر زیر و نگارشهای آنها با حروف آغازین بزرگ نادیده گرفته میشوند: "" (پارامترهای بدون نام)، iterator، begin، end، first، last، lhs، rhs.
- IgnoredParameterTypeSuffixes
- فهرستی از
پسوندهای
نام نوع
پارامتر که
هرگز نباید
بخشی از یک
توالی
پارامترهای
مجاور قابل
جابهجایی
در نظر
گرفته شوند.
پارامترهایی
که نوع
آنها،
همانطور
که در کد
منبع نوشته
شده، با یکی
از موارد
این گزینه
پایان
یابد،
نادیده
گرفته
خواهند شد.
این مقدار،
فهرستی از
نامهاست
که با ; جدا
شدهاند.
این گزینه
به حروف
کوچک و بزرگ
حساس است!
به صورت پیشفرض، موارد زیر و نگارشهای آنها با حروف آغازین کوچک نادیده گرفته میشوند: bool، It، Iterator، InputIt، ForwardIt، BidirIt، RandomIt، random_iterator، ReverseIt، reverse_iterator، reverse_const_iterator، RandomIt، random_iterator، ReverseIt، reverse_iterator، reverse_const_iterator، Const_Iterator، ConstIterator، const_reverse_iterator، ConstReverseIterator. علاوه بر این، _Bool (اما نه _bool) نیز بخشی از مقدار پیشفرض است.
- SuppressParametersUsedTogether
- هشدارهای
عیبیابی
مربوط به
پارامترهایی
را که در
بدنهٔ تابع
همراه با
یکدیگر یا
به شیوهای
مشابه به
کار
رفتهاند،
سرکوب
میکند.
پیشفرض true
است. مشخص
کردن false این
قواعد
تجربی (heuristics) را
غیرفعال
میکند.
در حال حاضر، قواعد تجربی زیر پیادهسازی شدهاند که هشدار مربوط به جفت پارامتر مربوطه را متوقف خواهند کرد:
- پارامترها در یک عبارت مشابه استفاده شده باشند؛ مثلاً f(a, b) یا a < b.
- پارامترها
مجدداً به
همان تابع و
به پارامتر
یکسانی از
آن تابع، با
همان
بارگذاری
بیشازحد
(overload) یکسان
ارسال شده
باشند.
مثلاً f(a, 1) و f(b,
2) به تابعی
مانند f(T, int).
نکته:
- فیلد عضو یکسانی مورد دسترسی قرار گیرد، یا متد عضو یکسانی از آن دو پارامتر فراخوانی شود؛ مثلاً a.foo() و b.foo().
- دستورهای return جداگانه هر یک از پارامترها را در مسیرهای مختلف کد بازگردانند.
- NamePrefixSuffixSilenceDissimilarityThreshold
- تعداد
نویسههایی
که دو نام
پارامتر
ممکن است در
ابتدا یا
انتها با
یکدیگر
تفاوت
داشته
باشند در
حالی که
بقیهٔ نام
یکسان
باشد، به
گونهای که
هشدار
مربوط به آن
دو پارامتر
متوقف شود.
پیشفرض 1
است.
میتواند
هر عدد صحیح
مثبت باشد.
اگر 0 باشد،
قاعدهٔ
تجربی
فیلترسازی
بر اساس نام
پارامترها
غیرفعال
میشود.
این گزینه میتواند برای متوقف کردن هشدارهای مربوط به پارامترهایی استفاده شود که روش نامگذاری آنها نشان میدهد ترتیب قرارگیری آنها اهمیتی ندارد.
برای نمونه، پارامترهای LHS و RHS پسوندهای با ۱ نویسه غیرمشابه نسبت به یکدیگر هستند: L و R نویسههای متفاوت هستند، در حالی که HS پسوند مشترک است. به طور مشابه، پارامترهای text1, text2, text3 پیشوندهایی با ۱ نویسه تفاوت نسبت به یکدیگرند، که اعداد انتهایی بخش غیرمشابه را تشکیل میدهند. اگر این مقدار حداقل 1 باشد، چنین مواردی گزارش نخواهند شد.
محدودیتها
این بررسی برای بررسی امضای توابع (function signatures) طراحی شده است!
این بررسی توابعی را که توسط کامپایلر در زمینهای تولید میشوند که فقط از محل فراخوانی مشخص است، مورد بررسی قرار نمیدهد. این موارد شامل توابع چندآرگومانی (variadic)، توابع در کد C که فاقد فهرست آرگومان هستند، و نمونهسازیهای قالب C++ میشوند. بیشتر این موارد، که در غیر این صورت از دید فراخواننده قابل جابجایی هستند، هیچ راهی برای "اصلاح شدن" در نقطه تعریف ندارند. در مورد قالبهای C++، فقط تعاریف اصلی قالب و تخصیصهای صریح (explicit specializations) مطابقت داده شده و تحلیل میشوند.
هیچیک از موارد زیر هشداری (diagnostic) ایجاد نمیکنند:
int printf(const char *Format, ...) { /* ... */ }
int someOldCFunction() { /* ... */ }
template <typename T, typename U>
int add(T X, U Y) { return X + Y };
void theseAreNotWarnedAbout() {
printf("%d %d\n", 1, 2); // Two ints passed, they could be swapped.
someOldCFunction(1, 2, 3); // Similarly, multiple ints passed.
add(1, 2); // Instantiates 'add<int, int>', but that's not a user-defined function.
}
با توجه به محدودیت بالا، پارامترهایی که نوع آنها برای اثبات اختلاط با نوع پارامتر دیگر همچنان به نمونهسازیهای قالب وابسته است، بررسی نمیشوند.
template <typename T>
struct Vector {
typedef T element_type;
};
// Diagnosed: Explicit instantiation was done by the user, we can prove it
// is the same type.
void instantiated(int A, Vector<int>::element_type B) { /* ... */ }
// Diagnosed: The two parameter types are exactly the same.
template <typename T>
void exact(typename Vector<T>::element_type A,
typename Vector<T>::element_type B) { /* ... */ }
// Skipped: The two parameters are both 'T' but we cannot prove this
// without actually instantiating.
template <typename T>
void falseNegative(T A, typename Vector<T>::element_type B) { /* ... */ }
در زمینه تبدیلهای ضمنی (هنگامی که ModelImplicitConversions فعال است)، مدلسازی انجامشده توسط این بررسی در صورتی هشدار میدهد که پارامترها قابل جابجایی باشند و ترتیب جابجاشده با تبدیلهای ضمنی مطابقت داشته باشد. این بررسی مدلسازی نمیکند که آیا نوع سوم نامرتبطی وجود دارد که بتوان هر دو پارامتر را در یک فراخوانی تابع از آن نوع ارائه داد یا خیر. این بدان معناست که در مثال زیر، حتی با اینکه strs() به وضوح امکان فراخوانی با آرگومانهای جابجاشده را دارد (تا زمانی که آرگومانها رشتههای تحتاللفظی باشند)، هشداری درباره آن داده نخواهد شد.
struct String {
String(const char *Buf);
};
struct StringView {
StringView(const char *Buf);
operator const char *() const;
};
// Skipped: Directly swapping expressions of the two type cannot mix.
// (Note: StringView -> const char * -> String would be **two**
// user-defined conversions, which is disallowed by the language.)
void strs(String Str, StringView SV) { /* ... */ }
// Diagnosed: StringView implicitly converts to and from a buffer.
void cStr(StringView SV, const char *Buf() { /* ... */ }
bugprone-empty-catch
مشکلات مربوط به دستورات catch خالی را شناسایی کرده و راهکارهایی برای رفع آنها پیشنهاد میدهد.
try {
// Some code that can throw an exception
} catch(const std::exception&) {
}
وجود دستورات catch خالی در پایگاه کد میتواند مشکل جدی باشد که توسعهدهندگان باید از آن آگاه باشند. دستورات catch برای مدیریت استثناهایی استفاده میشوند که در طول اجرای برنامه پرتاب میشوند. هنگامی که استثنایی رخ میدهد، برنامه به نزدیکترین دستور catch که با نوع استثنا مطابقت دارد پرش میکند.
دستورات catch خالی، که با عنوان "بلعیدن" (swallowing) استثناها نیز شناخته میشوند، استثنا را دریافت میکنند اما کاری با آن انجام نمیدهند. این امر به این معنی است که استثنا بهدرستی مدیریت نشده و برنامه طوری به اجرای خود ادامه میدهد که گویی هیچ اتفاقی نیفتاده است. این وضعیت میتواند به چندین مشکل منجر شود، از جمله:
- باگهای پنهان: اگر استثنایی دریافت و نادیده گرفته شود، میتواند منجر به باگهای پنهانی شود که عیبیابی و رفع آنها دشوار است. ممکن است علت اصلی مشکل آشکار نباشد و برنامه به رفتارهای غیرمنتظره ادامه دهد.
- mسائل امنیتی: نادیده گرفتن استثناها میتواند منجر به مسائل امنیتی شود، مانند سرریز بافر یا ارجاع به اشارهگر تهی (null pointer dereferences). نفوذگران میتوانند از این آسیبپذیریها برای دستیابی به دادههای حساس یا اجرای کدهای مخرب سوءاستفاده کنند.
- کیفیت پایین کد: دستورات catch خالی میتوانند نشاندهنده کیفیت پایین کد و عدم توجه به جزئیات باشند. این مسئله نگهداری و بهروزرسانی پایگاه کد را دشوار میسازد و به چرخههای طولانیتر توسعه و افزایش هزینهها میانجامد.
- کد غیرقابل اعتماد: کدی که استثناها را نادیده میگیرد اغلب غیرقابل اعتماد است و میتواند منجر به رفتار غیرقابل پیشبینی شود. این امر میتواند موجب نارضایتی کاربران شده و اعتماد به نرمافزار را تضعیف کند.
برای جلوگیری از این مشکلات، توسعهدهندگان باید همیشه استثناها را بهدرستی مدیریت کنند. این کار یا از طریق رفع مشکل اساسیِ ایجادکننده استثنا انجام میگیرد یا با انتشار (propagating) استثنا در پشته فراخوانی به یک مدیریتکننده سطح بالاتر. اگر استثنایی اهمیت چندانی ندارد، همچنان باید ثبت (log) شده یا به نوعی گزارش داده شود تا بتوان بعداً آن را پیگیری و برطرف نمود.
اگر استثنا چیزی است که میتوان آن را به صورت محلی مدیریت کرد، باید درون همان بلوک catch مدیریت شود. این اقدام میتواند شامل لاگ کردن استثنا یا انجام عملیات مناسب دیگر باشد تا اطمینان حاصل شود که استثنا نادیده گرفته نشده است.
در اینجا یک مثال آورده شده است:
try {
// Some code that can throw an exception
} catch (const std::exception& ex) {
// Properly handle the exception, e.g.:
std::cerr << "Exception caught: " << ex.what() << std::endl;
}
اگر استثنا نمیتواند به صورت محلی مدیریت شود و باید در پشته فراخوانی به بالا منتشر شود، باید مجدداً پرتاب (re-thrown) شود یا استثنای جدیدی پرتاب گردد.
در اینجا یک مثال آورده شده است:
try {
// Some code that can throw an exception
} catch (const std::exception& ex) {
// Re-throw the exception
throw;
}
در برخی موارد، دریافت استثنا در این سطح ممکن است ضروری نباشد و مناسبتر باشد که اجازه داده شود استثنا در پشته فراخوانی به بالا منتشر شود. این کار به سادگی با استفاده نکردن از بلوک try/catch قابل انجام است.
در اینجا یک مثال آورده شده است:
void function() {
// Some code that can throw an exception
}
void callerFunction() {
try {
function();
} catch (const std::exception& ex) {
// Handling exception on higher level
std::cerr << "Exception caught: " << ex.what() << std::endl;
}
}
راهکار بالقوه دیگر برای جلوگیری از دستورات catch خالی، تغییر کد برای پیشگیری از پرتاب استثنا از همان ابتدا است. این هدف را میتوان با استفاده از یک API متفاوت، بررسی شرایط خطا از پیش، یا مدیریت خطاها به روشی دیگر که شامل استثناها نباشد محقق کرد. با حذف نیاز به بلوکهای try-catch، کد سادهتر شده و کمتر مستعد خطا خواهد بود.
در اینجا یک مثال آورده شده است:
// Old code:
try {
mapContainer["Key"].callFunction();
} catch(const std::out_of_range&) {
}
// New code
if (auto it = mapContainer.find("Key"); it != mapContainer.end()) {
it->second.callFunction();
}
در نتیجه، دستورات catch خالی رویهای نادرست هستند که میتوانند به باگهای پنهان، مشکلات امنیتی، کیفیت پایین کد و کدهای غیرقابل اعتماد منجر شوند. توسعهدهندگان با مدیریت صحیح استثناها میتوانند مطمئن شوند که کد آنها پایدار، امن و قابل نگهداری است.
گزینهها
- IgnoreCatchWithKeywords
- این گزینه میتواند برای نادیده گرفتن دستورهای catch خاص حاوی کلیدواژههای مشخص استفاده شود. اگر بدنه یک دستور catch (بدون حساسیت به حروف کوچک و بزرگ) شامل هر یک از کلیدواژههای ذکر شده در این گزینه جداشده با نقطهویرگول (semicolon) باشد، آنگاه catch نادیده گرفته شده و هیچ هشداری صادر نخواهد شد. مقدار پیشفرض: @TODO;@FIXME.
- AllowEmptyCatchForExceptions
- این گزینه میتواند برای نادیده گرفتن دستورهای catch خالی برای انواع استثنای مشخص استفاده شود. بهطور پیشفرض، این بررسی در صورت شناسایی یک دستور catch خالی، بدون در نظر گرفتن نوع استثنای دریافتشده، یک هشدار صادر میکند. با این حال، در شرایط خاص، مانند زمانی که توسعهدهنده میخواهد عمداً برخی استثناها را نادیده بگیرد یا آنها را به روش دیگری مدیریت کند، ممکن است مجاز دانستن دستورهای catch خالی برای انواع استثنای خاص مطلوب باشد. برای پیکربندی این گزینه، باید فهرستی از نامهای انواع استثنا که با نقطهویرگول جدا شدهاند ارائه شود. اگر نوع یک استثنا در این فهرست در یک دستور catch خالی دریافت شود، هیچ هشداری صادر نخواهد شد. مقدار پیشفرض: رشته خالی.
bugprone-exception-copy-constructor-throws
بررسی میکند که آیا سازنده رونوشت (copy constructor) یک شیء پرتابشده میتواند استثنا پرتاب کند یا خیر.
اشیاء استثنا در C++ ملزم هستند که قابل ساخت با رونوشت (copy constructible) باشند. با این حال، سازنده رونوشت یک استثنا برای جلوگیری از مشکلات احتمالی هنگام بازپیچی پشته (stack unwinding) نباید استثنا پرتاب کند. اگر در حین بازپیچی پشته استثنایی پرتاب شود (مانند سازنده رونوشت یک شیء استثنا)، برنامه از طریق std::terminate خاتمه مییابد.
class SomeException {
public:
SomeException() = default;
SomeException(const SomeException&) { /* may throw */ }
};
void f() {
throw SomeException(); // warning: thrown exception type's copy constructor can throw
}
مراجع
این بررسی مطابق با قاعده استاندارد کدنویسی CERT C++ به شماره ERR60-CPP است: Exception objects must be nothrow copy constructible https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible.
bugprone-exception-escape
توابعی را پیدا میکند که ممکن است بهطور مستقیم یا غیرمستقیم استثنا پرتاب کنند، در حالی که نباید این کار را انجام دهند. توابعی که نباید استثنا پرتاب کنند به شرح زیر هستند:
- مخربها (Destructors)
- سازندههای انتقال (Move constructors)
- عملگرهای انتساب انتقال (Move assignment operators)
- توابع main()
- توابع swap()
- توابع iter_swap()
- توابع iter_move()
- توابع علامتگذاریشده با throw() یا noexcept
- سایر توابع مشخصشده به عنوان گزینه
پرتاب استثنا توسط یک مخرب ممکن است منجر به رفتار تعریفنشده (undefined behavior)، نشت منابع یا خاتمه غیرمنتظره برنامه شود. سازنده انتقال یا انتساب انتقال پرتابکننده استثنا نیز ممکن است منجر به رفتار تعریفنشده یا نشت منابع شود. عملیاتهای swap() در اکثر موارد انتظار میرود که بدون پرتاب استثنا باشند و همیشه پیادهسازی آنها به صورت بدون پرتاب استثنا امکانپذیر است. از عملیاتهای swap() بدون پرتاب استثنا برای ساخت عملیاتهای انتقال نیز استفاده میشود. یک تابع main() پرتابکننده استثنا نیز منجر به خاتمه غیرمنتظره میشود.
توابعی که صراحتاً با noexcept(false) یا throw(exception) تعریف شدهاند، از تحلیل کنار گذاشته میشوند، زیرا اگرچه برای توابعی مانند swap()، main()، سازندههای انتقال، عملگرهای انتساب انتقال و مخربها توصیه نمیشود، اما نشاندهنده قصد صریح توسعهدهنده است و باید مورد احترام قرار گیرد.
هشدار! این بررسی ممکن است روی فایلهای منبع بزرگ پرهزینه باشد.
گزینهها
- CheckDestructors
- وقتی برابر با true باشد، مخربها تحلیل میشوند تا استثنا پرتاب نکنند. مقدار پیشفرض true است.
- CheckMoveMemberFunctions
- وقتی برابر با true باشد، سازندههای انتقال و عملگرهای انتساب انتقال تحلیل میشوند تا استثنا پرتاب نکنند. مقدار پیشفرض true است.
- CheckMain
- وقتی برابر با true باشد، تابع main() تحلیل میشود تا استثنا پرتاب نکند. مقدار پیشفرض true است.
- CheckNothrowFunctions
- وقتی برابر با true باشد، توابع علامتگذاریشده با مشخصات استثنای noexcept یا throw() تحلیل میشوند تا استثنا پرتاب نکنند. مقدار پیشفرض true است.
- CheckedSwapFunctions
- فهرستی جداشده با کاما از نامهای توابع swap که نباید استثنا پرتاب کنند. مقدار پیشفرض swap,iter_swap,iter_move است.
- FunctionsThatShouldNotThrow
- فهرستی جداشده با کاما شامل نامهای توابعی که نباید استثنا پرتاب کنند. یک مقدار نمونه برای این پارامتر میتواند WinMain باشد که تابع WinMain() در Windows API را به فهرست توابعی که نباید استثنا پرتاب کنند اضافه میکند. مقدار پیشفرض یک رشته خالی است.
- IgnoredExceptions
- فهرستی جداشده با کاما شامل نامهای انواعی که در بررسی به عنوان استثناهای پرتابشده به حساب نمیآیند. مقدار پیشفرض یک رشته خالی است.
bugprone-float-loop-counter
حلقههای for که عبارت استقرای (induction expression) آنها از نوع ممیز شناور (floating-point) است را نشانهگذاری میکند.
مراجع
این بررسی مطابق با قاعده استاندارد کدنویسی CERT C به شماره FLP30-C است: Do not use floating-point variables as loop counters https://www.securecoding.cert.org/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters.
bugprone-fold-init-type
این بررسی عدم تطابق نوع را در عملیاتهای فولد <https://en.wikipedia.org/wiki/Fold_(higher-order_function)> مانند std::accumulate که ممکن است منجر به از دست رفتن دقت شود، نشانهگذاری میکند. تابع std::accumulate یک محدوده ورودی را با استفاده از نوع مقدار اولیه به یک مقدار با کمک operator+ بهطور پیشفرض تبدیل (fold) میکند. این کار میتواند منجر به از دست رفتن دقت از راههای زیر شود:
- •
- کوتاهسازی (Truncation): کد زیر از یک محدوده ممیز شناور و یک مقدار اولیه int استفاده میکند، بنابراین کوتاهسازی در هر بار اعمال operator+ رخ میدهد و نتیجه 0 خواهد بود که ممکن است با انتظار کاربر مطابقت نداشته باشد.
auto a = {0.5f, 0.5f, 0.5f, 0.5f};
return std::accumulate(std::begin(a), std::end(a), 0);
- •
- سرریز (Overflow): کد زیر نیز مقدار 0 را برمیگرداند.
auto a = {65536LL * 65536 * 65536};
return std::accumulate(std::begin(a), std::end(a), 0);
bugprone-forward-declaration-namespace
بررسی میکند که آیا یک اعلان پیشاپیش (forward declaration) استفادهنشده در یک فضای نام (namespace) اشتباه قرار دارد یا خیر.
این بررسی تمام اعلانهای پیشاپیش استفادهنشده را بازرسی کرده و وجود هرگونه اعلان/تعریف همنام دیگر را بررسی میکند؛ موضوعی که میتواند نشاندهنده آن باشد که اعلان پیشاپیش در یک فضای نام بالقوه اشتباه قرار گرفته است.
namespace na { struct A; }
namespace nb { struct A {}; }
nb::A a;
// warning : no definition found for 'A', but a definition with the same name
// 'A' found in another namespace 'nb::'
این بررسی تنها میتواند هشدار تولید کند، اما در حال حاضر نمیتواند اصلاح خودکاری پیشنهاد دهد.
bugprone-forwarding-reference-overload
این بررسی به دنبال سازندههای ارجاع بازپخش کامل (perfect forwarding) میگردد که میتوانند سازندههای رونوشت (copy) یا انتقال (move) را پنهان (hide) کنند. اگر یک ارجاع lvalue غیر const به سازنده ارسال شود، پارامتر ارجاع بازپخش تطابق بهتری نسبت به پارامتر ارجاع const سازنده رونوشت خواهد داشت؛ بنابراین سازنده perfect forwarding فراخوانی میشود که میتواند سردرگمکننده باشد. برای توضیحات دقیق درباره این موضوع، به این منبع مراجعه کنید: Scott Meyers, Effective Modern C++, بخش ۲۶.
مثال زیر را در نظر بگیرید:
class Person {
public:
// C1: perfect forwarding ctor
template<typename T>
explicit Person(T&& n) {}
// C2: perfect forwarding ctor with parameter default value
template<typename T>
explicit Person(T&& n, int x = 1) {}
// C3: perfect forwarding ctor guarded with enable_if
template<typename T, typename X = enable_if_t<is_special<T>, void>>
explicit Person(T&& n) {}
// C4: variadic perfect forwarding ctor guarded with enable_if
template<typename... A,
enable_if_t<is_constructible_v<tuple<string, int>, A&&...>, int> = 0>
explicit Person(A&&... a) {}
// C5: perfect forwarding ctor guarded with requires expression
template<typename T>
requires requires { is_special<T>; }
explicit Person(T&& n) {}
// C6: perfect forwarding ctor guarded with concept requirement
template<Special T>
explicit Person(T&& n) {}
// (possibly compiler generated) copy ctor
Person(const Person& rhs);
};
این بررسی برای سازندههای C1 و C2 هشدار صادر میکند، زیرا این موارد میتوانند سازندههای رونوشت و انتقال را پنهان کنند. در صورتی که هر دو سازنده رونوشت و انتقال غیرفعال باشند (حذفشده یا private)، هشدارها متوقف میشوند، زیرا در این حالت چیزی وجود ندارد که سازنده perfect forwarding بتواند آن را پنهان کند. همچنین هشدارها برای سازندههایی مانند C3-C6 که توسط enable_if یا یک concept محافظت شدهاند متوقف میشوند، با این فرض که برنامهنویس از پنهانسازی احتمالی آگاه بوده است.
Background
برای تصمیمگیری در مورد اینکه آیا یک سازنده با enable_if محافظت شده است یا خیر، انواع پارامترهای سازنده، مقادیر پیشفرض پارامترهای نوع قالب و انواع پارامترهای غیرنوعی قالب با یک مقدار لفظی (literal) پیشفرض در نظر گرفته میشوند. اگر هر بخشی از این انواع std::enable_if یا std::enable_if_t باشد، فرض میکنیم که سازنده محافظت شده است.
bugprone-implicit-widening-of-multiplication-result
این بررسی مواردی را عیبیابی میکند که در آنها نتیجه یک ضرب بهصورت ضمنی عریضتر (widen) میشود، و (همراه با fix-it) پیشنهاد میدهد که یا با صریح کردن تبدیل عریضتر، هشدار کد بیاثر شود، یا عمل ضرب در یک نوع داده عریضتر انجام گیرد تا از عریضسازی پس از آن جلوگیری شود.
این بررسی عمدتاً هنگام کار با بافرهای بسیار بزرگ مفید است. به عنوان مثال، قطعه کد زیر را در نظر بگیرید:
void zeroinit(char* base, unsigned width, unsigned height) {
for(unsigned row = 0; row != height; ++row) {
for(unsigned col = 0; col != width; ++col) {
char* ptr = base + row * width + col;
*ptr = 0;
}
}
}
این کد بهطور کلی مشکلی ندارد، اما اگر حاصل width * height سرریز (overflow) کند، به جای پردازش کل بافر درخواستی، اشارهگر مجدداً به ابتدای base برمیگردد (wrap back رخ میدهد).
در واقع، این موضوع تنها برای بافرهای بسیار بزرگ (۴ گیگابایت به بالا) اهمیت دارد، اما این حالت میتواند بهراحتی مثلاً در پردازش تصویر رخ دهد؛ جایی که برای وقوع آن «تنها» به یک تصویر حدوداً ۲۶۹ مگاپیکسلی نیاز است.
گزینهها
- UseCXXStaticCastsInCppSources
- هنگام پیشنهاد اصلاحات (fix-it) برای کدهای C++، آیا تبدیلهای نوع به سبک C++ یعنی static_cast<>() پیشنهاد شوند یا تبدیلهای نوع به سبک C. مقدار پیشفرض: true.
- UseCXXHeadersInCppSources
- هنگام پیشنهاد الحاق هدر مناسب در کد C++، آیا هدر <cstddef> پیشنهاد شود یا <stddef.h>. مقدار پیشفرض: true.
- IgnoreConstantIntExpr
- اگر عملوندهای ضرب از نوع ثوابت زمان کامپایل باشند (مانند مقادیر مستقیم یا عبارات constexpr) و در نوع عبارت مبدأ جا شوند، هیچ عیبیابی یا پیشنهاد اصلاحی ارائه نکن. این گزینه تنها عباراتی را در نظر میگیرد که عبارت مبدأ در آنها یک نوع عدد صحیح علامتدار باشد. مقدار پیشفرض: false.
مثالها:
long mul(int a, int b) {
return a * b; // warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
}
char* ptr_add(char *base, int a, int b) {
return base + a * b; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
}
char ptr_subscript(char *base, int a, int b) {
return base[a * b]; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
}
bugprone-inaccurate-erase
استفادههای نادرست از متد erase() را بررسی میکند.
الگوریتمهایی مانند remove() در عمل هیچ عنصری را از نگهدارنده (container) حذف نمیکنند، بلکه یک تکرارکننده (iterator) به اولین عنصر مازاد در انتهای نگهدارنده برمیگردانند. این عناصر مازاد باید با استفاده از متد erase() حذف شوند. این بررسی زمانی هشدار میدهد که به دلیل استفاده از یک سربارگذاری (overload) نامناسب، تمام عناصر حذف نشوند.
برای مثال، کد زیر فقط یک عنصر را حذف میکند:
std::vector<int> xs; ... xs.erase(std::remove(xs.begin(), xs.end(), 10));
برای حذف این زیربازه، سربارگذاریِ دوآرگومانیِ erase() را فراخوانی کنید:
std::vector<int> xs; ... xs.erase(std::remove(xs.begin(), xs.end(), 10), xs.end());
bugprone-inc-dec-in-conditions
مواردی را شناسایی میکند که یک متغیر درون یک شرط پیچیده هم افزایش/کاهش یافته و هم به آن ارجاع داده شده است، و پیشنهاد میدهد برای جلوگیری از ابهام در مقدار متغیر، این عملیات به بیرون منتقل شود.
هنگامی که یک متغیر تغییر داده شده و همزمان در یک شرط پیچیده استفاده میشود، میتواند رفتارهای غیرمنتظرهای به همراه داشته باشد. اثر جانبی تغییر مقدار متغیر درون شرط میتواند تحلیل و درک کد را دشوار سازد. علاوه بر این، زمانبندی مد نظر توسعهدهنده برای اصلاح متغیر ممکن است نامشخص باشد و منجر به سوءتفاهم و خطا شود. این موضوع بهویژه زمانی که شرط شامل عملگرهای منطقی مانند && و || باشد مشکلسازتر میشود، جایی که ترتیب ارزیابی شرایط را پیچیدهتر میکند.
مثال زیر را در نظر بگیرید:
int i = 0;
// ...
if (i++ < 5 && i > 0) {
// do something
}
در این مثال، ممکن است نتیجه عبارت آن چیزی نباشد که توسعهدهنده قصد داشته است. قصد اولیه توسعهدهنده ممکن است افزایش i پس از ارزیابی کل شرط بوده باشد، اما در واقعیت، i پیش از اجرای i > 0 افزایش خواهد یافت. این موضوع میتواند به رفتار غیرمنتظره و باگها در کد منجر شود. برای رفع این مشکل، توسعهدهنده باید عملیات افزایش را از شرط جدا کرده و بهطور مستقل انجام دهد. بهعنوان مثال، میتواند i را در یک دستور جداگانه قبل یا بعد از ارزیابی شرط افزایش دهد. این کار تضمین میکند که مقدار i در سراسر کد قابل پیشبینی و پایدار باشد.
int i = 0;
// ...
i++;
if (i <= 5 && i > 0) {
// do something
}
یک مشکل رایج دیگر زمانی رخ میدهد که چندین عمل افزایش یا کاهش روی همان متغیر درون یک شرط پیچیده انجام شود. به عنوان مثال:
int i = 4;
// ...
if (i++ < 5 || --i > 2) {
// do something
}
به دلیل ترتیب ارزیابی در C++ یک مشکل بالقوه در این کد وجود دارد. عملگر || استفادهشده در عبارت شرط تضمین میکند که اگر عملوند اول برابر با true ارزیابی شود، عملوند دوم ارزیابی نخواهد شد. این بدان معناست که اگر i در ابتدا 4 باشد، عملوند اول یعنی i < 5 مقدار true خواهد داشت و عملوند دوم یعنی i > 2 ارزیابی نخواهد شد. در نتیجه، عملیات کاهش --i اجرا نخواهد شد و i مقدار 5 را نگه میدارد، که ممکن است رفتار مورد نظر توسعهدهنده نباشد.
برای جلوگیری از این مشکل بالقوه، هر دو عملیات افزایش و کاهش روی i باید به خارج از عبارت شرط منتقل شوند.
bugprone-incorrect-enable-if
استفادههای نادرست از std::enable_if را که نوع تودرتوی type را نام نمیبرند، تشخیص میدهد.
در C++11 ویژگی std::enable_if به عنوان روشی مناسب برای بهرهگیری از SFINAE معرفی شد. یکی از روشهای استفاده از std::enable_if، تعریف یک پارامتر نوع قالب بینام با نوع پیشفرض برابر با typename std::enable_if<condition>::type است. اگر نویسنده نام بردن نوع تودرتوی type را فراموش کند، کد همیشه قالب کاندید را مد نظر قرار میدهد، حتی اگر شرط برقرار نباشد.
در زیر نمونههایی از کدهایی که به درستی از std::enable_if استفاده میکنند و نمونههای نادرستی که توسط این بررسی پرچمگذاری میشوند، آمده است.
template <typename T, typename = typename std::enable_if<T::some_trait>::type>
void valid_usage() { ... }
template <typename T, typename = std::enable_if_t<T::some_trait>>
void valid_usage_with_trait_helpers() { ... }
// The below code is not a correct application of SFINAE. Even if
// T::some_trait is not true, the function will still be considered in the
// set of function candidates. It can either incorrectly select the function
// when it should not be a candidates, and/or lead to hard compile errors
// if the body of the template does not compile if the condition is not
// satisfied.
template <typename T, typename = std::enable_if<T::some_trait>>
void invalid_usage() { ... }
// The tool suggests the following replacement for 'invalid_usage':
template <typename T, typename = typename std::enable_if<T::some_trait>::type>
void fixed_invalid_usage() { ... }
در C++14 ویژگی کمکی std::enable_if_t معرفی شد که احتمال بروز این خطا را کاهش میدهد. C++20 قیود (constraints) را معرفی میکند که بهطور کلی جایگزین استفاده از std::enable_if میشوند. برای ابزار دیگری که std::enable_if را با std::enable_if_t جایگزین میکند به modernize-type-traits <> مراجعه کنید، و برای ابزاری که std::enable_if را با قیود C++20 جایگزین میسازد به modernize-use-constraints <> رجوع کنید. در صورت امکان استفاده از این سازوکارهای جدیدتر را مد نظر قرار دهید.
bugprone-incorrect-enable-shared-from-this
کلاسها یا ساختارهایی را که بهصورت عمومی (public) از std::enable_shared_from_this ارثبری نمیکنند شناسایی میکند، چرا که در غیر این صورت هنگام فراخوانی shared_from_this رفتارهای ناخواستهای رخ خواهد داد.
کد زیر را در نظر بگیرید:
#include <memory>
// private inheritance
class BadExample : std::enable_shared_from_this<BadExample> {
// ``shared_from_this``` unintended behaviour
// `libstdc++` implementation returns uninitialized ``weak_ptr``
public:
BadExample* foo() { return shared_from_this().get(); }
void bar() { return; }
};
void using_not_public() {
auto bad_example = std::make_shared<BadExample>();
auto* b_ex = bad_example->foo();
b_ex->bar();
}
با استفاده از پیادهسازی libstdc++، فراخوانی shared_from_this استثنای std::bad_weak_ptr را صادر میکند. هنگام فراخوانی using_not_public()، این کد در صورت عدم مدیریت استثنا، کرش خواهد کرد.
bugprone-incorrect-roundings
استفاده از الگوهایی را بررسی میکند که به ایجاد گرد کردنهای نادرست شناخته شدهاند. برنامهنویسان اغلب از الگوی زیر استفاده میکنند:
(int)(double_expression + 0.5)
تا عبارت ممیز شناور مضاعف (double) را به یک عدد صحیح گرد کنند. مشکل این روش عبارت است از:
- 1.
- بهطور غیرضروری کُند است.
- 2.
- نادرست است. عدد 0.499999975 (کوچکترین عدد شناور قابل بازنمایی کمتر از 0.5) به 1.0 گرد میشود. رفتار برای اعداد منفی حتی بدتر است، بهطوری که هر دو مقدار -0.5f و -1.4f به 0.0 گرد میشوند.
bugprone-infinite-loop
حلقههای بینهایت آشکار را پیدا میکند (حلقههایی که در آنها متغیر شرط حلقه اصلاً تغییر نمیکند).
مشخص است که یافتن کلی حلقههای بینهایت از نظر تئوری غیرممکن است (مسئله توقف یا Halting Problem). با این حال، میتوان برخی از حلقههای بینهایت واضح را شناسایی کرد، برای نمونه زمانی که شرط حلقه تغییر نمیکند. این بررسی چنین حلقههایی را شناسایی میکند. یک حلقه در صورتی بینهایت در نظر گرفته میشود که فاقد هرگونه دستور خروج از حلقه (break، continue، goto، return، throw یا فراخوانی تابعی با ویژگی [[noreturn]]) باشد و تمامی شرایط زیر برای تکتک متغیرهای موجود در شرط برقرار باشد:
- یک متغیر محلی باشد.
- فاقد هرگونه نام مستعار ارجاعی (reference) یا اشارهگری (pointer) باشد.
- عضو یک ساختار (structure) یا کلاس نباشد.
علاوه بر این، برای اینکه حلقه بینهایت تلقی شود، شرط نباید حاوی فراخوانی تابع باشد چرا که توابع ممکن است در فراخوانیهای مختلف مقادیر متفاوتی برگردانند.
به عنوان مثال، در حلقه زیر متغیر i در بدنه حلقه تغییری نمیکند و بینهایت تلقی میشود:
int i = 0, j = 0;
while (i < 10) {
++j;
}
bugprone-integer-division
مواردی را پیدا میکند که در آنها تقسیم عدد صحیح در یک زمینه ممیز شناور (floating point) به احتمال زیاد باعث از دست رفتن ناخواسته دقت محاسبات میشود.
در صورتی که تقسیمها بخشی از عبارات زیر باشند، هیچ هشداری گزارش نمیشود:
- عملوندهای عملگرهایی که انتظار انواع عددی صحیح یا bool را دارند،
- عبارات فراخوانی با انواع عددی صحیح یا bool، و
- عبارات تبدیل نوع صریح (explicit cast) به انواع عددی صحیح یا bool،
زیرا این موارد به عنوان نشانههایی از عمدی بودن عمل برنامهنویس تفسیر میشوند.
مثالها:
float floatFunc(float); int intFunc(int); double d; int i = 42; // Warn, floating-point values expected. d = 32 * 8 / (2 + i); d = 8 * floatFunc(1 + 7 / 2); d = i / (1 << 4); // OK, no integer division. d = 32 * 8.0 / (2 + i); d = 8 * floatFunc(1 + 7.0 / 2); d = (double)i / (1 << 4); // OK, there are signs of deliberateness. d = 1 << (i / 2); d = 9 + intFunc(6 * i / 32); d = (int)(i / 32) - 8;
bugprone-invalid-enum-default-initialization
مقداردهی اولیه پیشفرض (به مقدار 0) متغیرهایی از نوع enum را شناسایی میکند که در آنها شمارشگر فاقد مقداری با ارزش 0 است.
در زبان ++C، اگر متغیری با فهرست مقداردهی (initializer list) یا روشهای ضمنی دیگر مقداردهی شود و هیچ مقداری در زمان مقداردهی تعیین نگردد، مقداردهی اولیه پیشفرض انجام میشود. در چنین مواردی از مقدار 0 برای مقداردهی اولیه استفاده میشود. این موضوع حتی اگر شمارشگر مقداری با ارزش 0 نداشته باشد نیز برای شمارشها اعمال میگردد. در نتیجه، یک متغیر از نوع enum ممکن است در ابتدا حاوی مقداری نامعتبر باشد (اگر برنامه انتظار داشته باشد که تنها شامل مقادیر شمارشگر فهرستشده باشد).
این بررسی تنها در صورتی هشدار صادر میکند که یک متغیر enum بهصورت پیشفرض مقداردهی اولیه شده باشد (نه اینکه بدون مقداردهی رها شده باشد) و آن enum فاقد شمارشگری با مقدار 0 باشد. این نوع میتواند یک enum دامنهدار (scoped) یا بدون دامنه (non-scoped) باشد. اجتماعها (unions) توسط این بررسی مدیریت نمیشوند (در صورتی که عضوی از نوع شمارشی داشته باشند).
توجه داشته باشید که enum مربوط به std::errc همیشه نادیده گرفته میشود زیرا انتظار میرود بهصورت پیشفرض مقداردهی اولیه شود، با وجود اینکه هیچ شمارشگری با مقدار 0 تعریف نمیکند.
enum class Enum1: int {
A = 1,
B
};
enum class Enum0: int {
A = 0,
B
};
void f() {
Enum1 X1{}; // warn: 'X1' is initialized to 0
Enum1 X2 = Enum1(); // warn: 'X2' is initialized to 0
Enum1 X3; // no warning: 'X3' is not initialized
Enum0 X4{}; // no warning: type has an enumerator with value of 0
}
struct S1 {
Enum1 A;
S(): A() {} // warn: 'A' is initialized to 0
};
struct S2 {
int A;
Enum1 B;
};
S2 VarS2{}; // warn: member 'B' is initialized to 0
این بررسی برای مقداردهی اولیه آرایهها یا ساختارها با فهرستهای مقداردهی در کد C نیز اعمال میشود. در این موارد، عناصری که در فهرست مشخص نشدهاند (و از نوع enum هستند) بر روی 0 تنظیم میشوند.
enum Enum1 {
Enum1_A = 1,
Enum1_B
};
struct Struct1 {
int a;
enum Enum1 b;
};
enum Enum1 Array1[2] = {Enum1_A}; // warn: omitted elements are initialized to 0
enum Enum1 Array2[2][2] = {{Enum1_A}, {Enum1_A}}; // warn: last element of both nested arrays is initialized to 0
enum Enum1 Array3[2][2] = {{Enum1_A, Enum1_A}}; // warn: elements of second array are initialized to 0
struct Struct1 S1 = {1}; // warn: element 'b' is initialized to 0
گزینهها
- IgnoredEnums
- فهرستی از عبارتهای منظم (regex) که با نقطه-ویرگول از هم جدا شدهاند و enumهایی را مشخص میکنند که این بررسی روی آنها اعمال نخواهد شد. پیشفرض ::std::errc است.
bugprone-lambda-function-name
تلاشها برای دریافت نام یک تابع از درون یک عبارت لامبدا (lambda) را بررسی میکند. نام یک لامبدا همیشه چیزی شبیه به operator() است که تقریباً هیچگاه مقصود اصلی برنامهنویس نبوده است.
مثال:
void FancyFunction() {
[] { printf("Called from %s\n", __func__); }();
[] { printf("Now called from %s\n", __FUNCTION__); }();
}
خروجی:
Called from operator() Now called from operator()
خروجی احتمالاً مورد نظر:
Called from FancyFunction Now called from FancyFunction
گزینهها
- IgnoreMacros
- مقدار true مشخص میکند که تلاش برای دریافت نام تابع از درون یک ماکرو نباید عیبیابی شود. مقدار پیشفرض false است.
bugprone-macro-parentheses
ماکروهایی را مییابد که به دلیل نبود پرانتز ممکن است رفتارهای غیرمنتظره داشته باشند.
ماکروها توسط پیشپردازنده دقیقاً همانطور که هستند بسط مییابند. در نتیجه، ممکن است رفتارهای غیرمنتظرهای رخ دهد؛ عملگرها ممکن است با ترتیبی غیرمنتظره ارزیابی شوند و عملگرهای یگانی ممکن است به عملگرهای دوتایی تبدیل شوند، و غیره.
هنگامی که فهرست جایگزینی شامل یک عبارت باشد، توصیه میشود آن را در پرانتز قرار دهید. این کار تضمین میکند که نتیجه ماکرو قبل از استفاده کاملاً ارزیابی شود.
همچنین توصیه میشود آرگومانهای ماکرو در فهرست جایگزینی در پرانتز قرار گیرند. این کار اطمینان میدهد که مقدار آرگومان به درستی محاسبه میشود.
این بررسی با قاعده PRE02-C از استاندارد کدنویسی CERT C مطابقت دارد. فهرستهای جایگزینی ماکرو باید پرانتزگذاری شوند: https://wiki.sei.cmu.edu/confluence/display/c/PRE02-C.+Macro+replacement+lists+should+be+parenthesized
bugprone-macro-repeated-side-effects
آرگومانهای تکراری دارای اثرات جانبی (side effects) را در ماکروها بررسی میکند.
bugprone-misleading-setter-of-reference
توابع عضو شبیه به setter را مییابد که یک پارامتر اشارهگر دریافت کرده و یک عضو مرجع (reference) از همان کلاس را با مقدار اشارهشده تنظیم میکنند.
این بررسی توابع عضوی را تشخیص میدهد که یک پارامتر اشارهگر تکی میگیرند و شامل یک دستور عبارت منفرد هستند که پارامتر را dereference کرده و نتیجه را به یک عضو دادهای با نوع مرجع اختصاص میدهد.
این واقعیت که یک تابع setter اشارهگر میگیرد ممکن است این باور اشتباه را ایجاد کند که یک مرجع داخلی (در صورتی که اشارهگر میبود) تغییر میکند، به جای مقداری که به آن اشاره شده (یا ارجاع داده شده) است.
مثال:
class MyClass {
int &InternalRef; // non-const reference member
public:
MyClass(int &Value) : InternalRef(Value) {}
// Warning: This setter could lead to unintended behaviour.
void setRef(int *Value) {
InternalRef = *Value; // This assigns to the referenced value, not changing what InternalRef references.
}
};
int main() {
int Value1 = 42;
int Value2 = 100;
MyClass X(Value1);
// This might look like it changes what InternalRef references to,
// but it actually modifies Value1 to be 100.
X.setRef(&Value2);
}
- اصلاحات ممکن:
- تغییر نوع پارامتر تابع "set" به نوع غیر اشارهگر (برای مثال، یک const reference).
- تغییر نوع متغیر عضو به یک اشارهگر و اختصاص یک مقدار به اشارهگر در تابع "set" (بدون dereference).
bugprone-misplaced-operator-in-strlen-in-alloc
مواردی را مییابد که در آنها مقدار 1 به جای نتیجه، به رشته موجود در آرگومان strlen()، strnlen()، strnlen_s()، wcslen()، wcsnlen() و wcsnlen_s() اضافه شده است و این مقدار به عنوان آرگومان یک تابع تخصیص حافظه (malloc()، calloc()، realloc()، alloca()) یا عملگر new[] در C++ استفاده شده است. این بررسی موارد خطا را حتی در صورتی که یکی از این توابع (به جز عملگر new[]) توسط یک اشارهگر تابع ثابت فراخوانی شده باشد، شناسایی میکند. مواردی که در آنها 1 هم به پارامتر و هم به نتیجه تابع شبه-strlen() اضافه شده است نادیده گرفته میشوند، و همچنین مواردی که کل عمل جمع درون پرانتزهای اضافی قرار گرفته باشد.
کد نمونه C:
void bad_malloc(char *str) {
char *c = (char*) malloc(strlen(str + 1));
}
اصلاح پیشنهادی این است که مقدار 1 به مقدار بازگشتی strlen() اضافه شود نه به آرگومان آن. در مثال بالا اصلاح به شکل زیر خواهد بود
char *c = (char*) malloc(strlen(str) + 1);
کد نمونه C++:
void bad_new(char *str) {
char *c = new char[strlen(str + 1)];
}
مشابه کد C با تابع malloc()، اصلاح پیشنهادی افزودن 1 به مقدار بازگشتی strlen() است و نه به آرگومان آن. در مثال بالا اصلاح به صورت زیر خواهد بود
char *c = new char[strlen(str) + 1];
مثال برای فرونشاندن (silence) این عیبیابی:
void bad_malloc(char *str) {
char *c = (char*) malloc(strlen((str + 1)));
}
bugprone-misplaced-pointer-arithmetic-in-alloc
مواردی را مییابد که در آنها یک عبارت عددی صحیح بهجای آرگومان توابع تخصیص حافظه (malloc()، calloc()، realloc()، alloca())، به نتیجهٔ خروجی آنها اضافه یا از آن کم شده است. این بررسی خطاهای مربوطه را حتی در صورتی که یکی از این توابع توسط یک اشارهگر تابع ثابت فراخوانی شود، تشخیص میدهد.
کد نمونه:
void bad_malloc(int n) {
char *p = (char*) malloc(n) + 10;
}
اصلاح پیشنهادی این است که عبارت عددی صحیح به آرگومان malloc اضافه شود و نه به نتیجهٔ آن. در مثال بالا اصلاح به شکل زیر خواهد بود:
char *p = (char*) malloc(n + 10);
bugprone-misplaced-widening-cast
این بررسی زمانی هشدار میدهد که تبدیل نوع (cast) حاصل یک محاسبه به یک نوع بزرگتر انجام شده باشد. اگر هدف از این تبدیل نوع جلوگیری از افت دقت باشد، در این صورت تبدیل در محل نادرستی قرار گرفته و ممکن است افت دقت رخ دهد. در غیر این صورت، تبدیل نوع بیاثر و بیفایده است.
کد نمونه:
long f(int x) {
return (long)(x * 1000);
}
نتیجهٔ x * 1000 ابتدا با دقت int محاسبه میشود. اگر نتیجه فراتر از دقت نوع int برود، افت دقت رخ میدهد. سپس نتیجه به نوع long تبدیل میشود.
اگر هیچ افت دقتی وجود ندارد، میتوان تبدیل نوع را حذف کرد یا بهطور صریح به نوع int تبدیل نمود.
اگر میخواهید از افت دقت جلوگیری کنید، تبدیل نوع را در جای مناسب قرار دهید؛ برای نمونه:
long f(int x) {
return (long)x * 1000;
}
Implicit casts
فراموش کردن قرار دادن تبدیل نوع به همان اندازهٔ قرار دادن آن در جای نادرست خطرناک بوده و حداقل به همان میزان نیز شایع است. اگر CheckImplicitCasts فعال باشد، این بررسی چنین مواردی را نیز شناسایی میکند؛ برای نمونه:
long f(int x) {
return x * 1000;
}
Floating point
در حال حاضر این هشدارها فقط برای تبدیل اعداد صحیح ثبت میشوند. هیچ هشداری برای این کد ثبت نخواهد شد:
double f(float x) {
return (double)(x * 10.0f);
}
Options
- CheckImplicitCasts
- اگر مقدار آن true باشد، تشخیص تبدیلهای ضمنی (implicit casts) فعال میشود. پیشفرض false است.
bugprone-move-forwarding-reference
اگر تابع std::move روی یک مرجع ارجاعی (forwarding reference) فراخوانی شود، هشدار میدهد؛ برای نمونه:
template <typename T>
void foo(T&& t) {
bar(std::move(t));
}
مراجع ارجاعی (forwarding references) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf معمولاً باید بهجای std::move به تابع std::forward ارسال شوند، و این همان اصلاحی است که پیشنهاد خواهد شد.
(یک مرجع ارجاعی، ارجاع rvalue از نوعی است که یک آرگومان قالب تابع استنباطشده میباشد.)
در این مثال، اصلاح پیشنهادی به این صورت خواهد بود:
bar(std::forward<T>(t));
Background
کدهایی مانند مثال بالا گاهی با این انتظار نوشته میشوند که T&& صرفنظر از نوعی که برای T استنباط میشود همواره یک مرجع rvalue باقی خواهد ماند، و بنابراین امکان ارسال یک lvalue به foo() وجود ندارد. با این حال، این موضوع درست نیست. این مثال را در نظر بگیرید:
std::string s = "Hello, world"; foo(s);
این کد کامپایل میشود و پس از فراخوانی تابع foo()، متغیر s در یک وضعیت نامشخص (indeterminate state) باقی میماند زیرا دادههای آن منتقل شدهاند (moved from). این موضوع ممکن است برای فراخوانندهٔ foo() غافلگیرکننده باشد زیرا هنگام فراخوانی foo() از هیچ std::move استفاده نشده است.
دلیل این رفتار در قاعدهٔ خاص استنباط آرگومان قالب روی قالبهای تابعی مانند foo() نهفته است -- یعنی روی قالبهای تابعی که یک آرگومان با ارجاع rvalue از نوعی که آرگومان قالب تابع استنباطشده است، دریافت میکنند. (بخش [temp.deduct.call]/3 را در استاندارد C++11 ببینید.)
اگر foo() روی یک lvalue فراخوانی شود (مانند مثال بالا)، آنگاه نوع T به عنوان یک مرجع lvalue استنباط میشود. در این مثال، T به صورت std::string & استنباط میشود. در نتیجه نوع آرگومان t به std::string& && تبدیل میشود؛ طبق قوانین انحلال ارجاع (reference collapsing)، این ساختار به std::string& فروپاشی میشود.
این بدان معناست که فراخوانی foo(s) متغیر s را به عنوان یک مرجع lvalue ارسال میکند، و foo() در نهایت عملیات move را روی s انجام داده و در نتیجه آن را در وضعیتی نامشخص قرار میدهد.
bugprone-multi-level-implicit-pointer-conversion
تبدیلهای ضمنی میان اشارهگرهایی با سطوح غیرمستقیم (indirection) متفاوت را تشخیص میدهد.
تبدیل بین انواع اشارهگر با سطوح ارجاع غیرمستقیم متفاوت میتواند خطرناک باشد و ممکن است به رفتار تعریفنشده (undefined behavior) منجر شود، بهویژه اگر اشارهگر تبدیلشده بعداً به نوعی با سطح ارجاع غیرمستقیم متفاوتی تبدیل نوع (cast) شود. برای مثال، تبدیل اشارهگر به اشارهگری از نوع int (int**) به یک void* میتواند منجر به از دست رفتن اطلاعات درباره سطح اصلی ارجاع غیرمستقیم شود، که این موضوع هنگام تلاش برای استفاده از اشارهگر تبدیلشده مشکلساز خواهد بود. اگر اشارهگر تبدیلشده بعداً به نوعی با سطح متفاوتی از ارجاع غیرمستقیم cast شده و عمل dereference روی آن انجام شود، ممکن است باعث خطاهای دسترسی به حافظه (access violation)، خرابی حافظه (memory corruption) یا سایر رفتارهای تعریفنشده شود.
مثال زیر را در نظر بگیرید:
void foo(void* ptr);
int main() {
int x = 42;
int* ptr = &x;
int** ptr_ptr = &ptr;
foo(ptr_ptr); // warning will trigger here
return 0;
}
در این مثال، foo() با آرگومان ptr_ptr فراخوانی شده است. با این حال، ptr_ptr یک اشارهگر از نوع int** است، در حالی که foo() یک اشارهگر void* را انتظار دارد. این امر موجب یک تبدیل ضمنی در سطح اشارهگر میشود که چنانچه foo() با فرض اینکه اشارهگر از نوع int* است آن را dereference کند، میتواند مشکلاتی به بار آورد.
استفاده از تبدیل صریح (explicit cast) راهحلی توصیهشده برای جلوگیری از مشکلات ناشی از تبدیل ضمنی سطح اشارهگر است، چرا که به توسعهدهنده امکان میدهد قصد و استدلال خود را برای تبدیل نوع صراحتاً بیان کند. علاوه بر این، توصیه میشود که توسعهدهندگان پیش از استفاده از تبدیل نوع صریح، ایمنی تبدیل را بهدقت بررسی و تایید کنند. این سطح احتیاط بیشتر میتواند به شناسایی زودهنگام مشکلات احتمالی در فرآیند توسعه کمک کند و پایداری و نگهداریپذیری کلی کد را بهبود بخشد.
گزینهها
- EnableInC
- اگر مقدار آن true باشد، این بررسی را در کدهای C فعال میکند (در کدهای C++ همواره فعال است). پیشفرض true است.
bugprone-multiple-new-in-one-expression
فراخوانیهای متعدد عملگر new را در یک عبارت واحد پیدا میکند، جایی که اگر تخصیص دوم با شکست مواجه شده و یک استثنا (exception) پرتاب کند، حافظه تخصیصیافته توسط new اول ممکن است دچار نشت (leak) شود.
زبان C++ اغلب ترتیب دقیق ارزیابی عملوندهای یک عملگر یا آرگومانهای یک تابع را مشخص نمیکند. بنابراین اگر اولین تخصیص با موفقیت انجام شود و دومی شکست بخورد، در یک مدیریتکننده استثنا (exception handler) نمیتوان تشخیص داد که کدام تخصیص با شکست مواجه شده تا حافظه آن آزاد شود. حتی اگر ترتیب مشخص باشد، ممکن است نتیجه اولین new در یک مکان موقت ذخیره شود که در زمان شکست تخصیص دوم قابل دسترسی نباشد. بهتر است در صورتی که برای بررسی خطاهای تخصیص از مدیریت استثنا استفاده میشود، از هرگونه عبارتی که شامل بیش از یک فراخوانی operator new است اجتناب شود.
قوانین متفاوتی برای عملگرهای اتصال کوتاه (short-circuit) مانند || و && و عملگر , اعمال میشود، که در آنها ارزیابی یک سمت باید پیش از شروع ارزیابی سمت دیگر کامل شود. عبارات یک راهاندازی لیستی (list-initialization یا مقداردهی اولیه یا ساخت شیء با استفاده از نویسههای { و }) با ترتیب ثابت ارزیابی میشوند. به همین ترتیب، شرط عملگر سهتایی ? پیش از ارزیابی شاخهها ارزیابی میشود.
این بررسی در صورتی هشدار میدهد که دو فراخوانی new در دو سمت مختلف یک عملگر در یک عبارت ظاهر شوند، یا اگر فراخوانیهای new در آرگومانهای متفاوتی از یک فراخوانی تابع (که میتواند ساخت شیء با نحو () باشد) ظاهر شوند. این فراخوانیهای new میتوانند در هر سطحی تودرتو باشند. برای صدور هشدار، فراخوانیهای new باید در یک بلوک کد باشند که در آن از مدیریت استثنا همراه با catch برای std::bad_alloc یا std::exception استفاده شده باشد. برای عملگرهای ||، &&، , و ? (شرط و یک شاخه) هشداری صادر نمیشود. همچنین در صورتی که هر دو تخصیص حافظه به یک متغیر انتساب نیافته یا مستقیماً به یک تابع ارسال نشده باشند، هشداری صادر نخواهد شد. دلیل این امر آن است که در این حالت ممکن است حافظه عمداً آزاد نشده باشد یا اشیاء تخصیصیافته ممکن است اشیائی خودتخریبگر (self-destructing) باشند.
مثالها:
struct A {
int Var;
};
struct B {
B();
B(A *);
int Var;
};
struct C {
int *X1;
int *X2;
};
void f(A *, B *);
int f1(A *);
int f1(B *);
bool f2(A *);
void foo() {
A *PtrA;
B *PtrB;
try {
// Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated.
f(new A, new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// List (aggregate) initialization is used.
C C1{new int, new int}; // no warning
// Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated but not yet passed to function 'f1'.
int X = f1(new A) + f1(new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// Allocation of 'B' may fail after memory for 'A' was allocated.
// From C++17 on memory for 'B' is allocated first but still may leak if allocation of 'A' fails.
PtrB = new B(new A); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception
// 'new A' and 'new B' may be performed in any order.
// 'new B'/'new A' may fail after memory for 'A'/'B' was allocated but not assigned to 'PtrA'/'PtrB'.
(PtrA = new A)->Var = (PtrB = new B)->Var; // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// Evaluation of 'f2(new A)' must be finished before 'f1(new B)' starts.
// If 'new B' fails the allocated memory for 'A' is supposedly handled correctly because function 'f2' could take the ownership.
bool Z = f2(new A) || f1(new B); // no warning
X = (f2(new A) ? f1(new A) : f1(new B)); // no warning
// No warning if the result of both allocations is not passed to a function
// or stored in a variable.
(new A)->Var = (new B)->Var; // no warning
// No warning if at least one non-throwing allocation is used.
f(new(std::nothrow) A, new B); // no warning
} catch(std::bad_alloc) {
}
// No warning if the allocation is outside a try block (or no catch handler exists for std::bad_alloc).
// (The fact if exceptions can escape from 'foo' is not taken into account.)
f(new A, new B); // no warning
}
bugprone-multiple-statement-macro
ماکروهای چنددستوری که در عبارات شرطی بدون آکولاد استفاده میشوند را شناسایی میکند. تنها اولین دستور ماکرو درون شرط قرار میگیرد و سایر دستورها بدون قید و شرط اجرا خواهند شد.
مثال:
#define INCREMENT_TWO(x, y) (x)++; (y)++ if (do_increment) INCREMENT_TWO(a, b); // (b)++ will be executed unconditionally.
بررسی cppcoreguidelines-narrowing-conversions به عنوان یک نام مستعار برای این بررسی، به اینجا تغییر مسیر میدهد.
تبدیلهای باریککننده (narrowing conversions) خاموش را بررسی میکند، به عنوان مثال: int i = 0; i += 0.1;. در حالی که مشکل در این مثال اولیه آشکار است، ممکن است در حالت زیر چندان واضح نباشد: void MyClass::f(double d) { int_member_ += d; }.
- ما تبدیلهای باریککننده را در موارد زیر علامتگذاری میکنیم:
- یک عدد صحیح به یک عدد صحیح باریکتر (مانند char به unsigned char) در صورتی که گزینه WarnOnIntegerNarrowingConversion فعال باشد،
- یک عدد صحیح به یک عدد ممیز شناور باریکتر (مانند uint64_t به float) در صورتی که گزینه WarnOnIntegerToFloatingPointNarrowingConversion فعال باشد،
- یک عدد ممیز شناور به یک عدد صحیح (مانند double به int)،
- یک عدد ممیز شناور به یک عدد ممیز شناور باریکتر (مانند double به float) در صورتی که گزینه WarnOnFloatingPointNarrowingConversion فعال باشد.
- این بررسی موارد زیر را علامتگذاری خواهد کرد:
- تمام تبدیلهای باریککنندهای که با یک تبدیل صریح (به سبک c یا static_cast) مشخص نشدهاند. به عنوان مثال: int i = 0; i += 0.1;، void f(int); f(0.1);،
- تمام کاربردهای عملگرهای دودویی همراه با تبدیلهای باریککننده. به عنوان مثال: int i; i+= 0.1;.
عملیات ریاضی با انواع عدد صحیح کوچکتر از int موجب تبدیل ضمنی (implicit conversions) میشوند، همانطور که در بخش "Integral Promotion" در وبسایت cppreference.com https://en.cppreference.com/w/cpp/language/implicit_conversion توضیح داده شده است. این بررسی موارد بیشتری از باریکسازی را نسبت به هشدار کامپایلر -Wconversion شناسایی و عیبیابی میکند. مثال زیر این رفتار را نشان میدهد.
// The following function definition demonstrates usage of arithmetic with
// integer types smaller than `int` and how the narrowing conversion happens
// implicitly.
void computation(short argument1, short argument2) {
// Arithmetic written by humans:
short result = argument1 + argument2;
// Arithmetic actually performed by C++:
short result = static_cast<short>(static_cast<int>(argument1) + static_cast<int>(argument2));
}
void recommended_resolution(short argument1, short argument2) {
short result = argument1 + argument2;
// ^ warning: narrowing conversion from 'int' to signed type 'short' is implementation-defined
// The cppcoreguidelines recommend to resolve this issue by using the GSL
// in one of two ways. Either by a cast that throws if a loss of precision
// would occur.
short result = gsl::narrow<short>(argument1 + argument2);
// Or it can be resolved without checking the result risking invalid results.
short result = gsl::narrow_cast<short>(argument1 + argument2);
// A classical `static_cast` will silence the warning as well if the GSL
// is not available.
short result = static_cast<short>(argument1 + argument2);
}
گزینهها (Options)
- WarnOnIntegerNarrowingConversion
- در صورت فعال بودن (true)، این بررسی روی تبدیلهای باریککننده عدد صحیح هشدار میدهد (مانند int به size_t). به طور پیشفرض true است.
- WarnOnIntegerToFloatingPointNarrowingConversion
- در صورت فعال بودن (true)، این بررسی روی تبدیلهای باریککننده عدد صحیح به ممیز شناور هشدار میدهد (مانند size_t به double). به طور پیشفرض true است.
- WarnOnFloatingPointNarrowingConversion
- در صورت فعال بودن (true)، این بررسی روی تبدیلهای باریککننده ممیز شناور هشدار میدهد (مانند double به float). به طور پیشفرض true است.
- WarnWithinTemplateInstantiation
- در صورت فعال بودن (true)، این بررسی در نمونهسازیهای قالب (template instantiations) روی تبدیلهای باریککننده هشدار میدهد. به طور پیشفرض false است.
- WarnOnEquivalentBitWidth
- در صورت فعال بودن (true)، این بررسی روی تبدیلهای باریککنندهای که ناشی از تبدیل بین انواع با پهنای بیت یکسان هستند، هشدار میدهد. (مانند int n = uint(0); یا long long n = double(0);). به طور پیشفرض true است.
- IgnoreConversionFromTypes
- تبدیلهای باریککننده از هر نوع موجود در این لیستِ جدا شده با نقطه-ویرگول نادیده گرفته میشوند. این گزینه میتواند برای حذف انتسابهای بسیار رایج اما کمتر مشکلساز مانند int n = std::vector<char>().size(); یا int n = std::difference(it1, it2); مفید باشد. لیست پیشفرض خالی است، اما یک لیست پیشنهادی برای کدهای قدیمی size_t;ptrdiff_t;size_type;difference_type است.
- PedanticMode
- در صورت فعال بودن (true)، این بررسی هنگام انتساب یک مقدار ثابت ممیز شناور به یک متغیر عدد صحیح هشدار میدهد، حتی اگر مقدار ممیز شناور در نوع مقصد دقیقاً قابل نمایش باشد (مانند int i = 1.0;). به طور پیشفرض false است.
پرسشهای متداول (FAQ)
- •
- عبارت "narrowing conversion from 'int' to 'float'" به چه معناست؟
یک عدد ممیز شناور با استاندارد IEEE754 میتواند تمامی مقادیر صحیح در محدوده [-2^PrecisionBits, 2^PrecisionBits] را نمایش دهد، که در آن PrecisionBits تعداد بیتهای مانتیس است.
برای نوع float این محدوده [-2^23, 2^23] خواهد بود، در حالی که int میتواند مقادیر را در محدوده [-2^31, 2^31-1] نمایش دهد.
- •
- عبارت "implementation-defined" به چه معناست؟
ممکن است با پیامهایی مانند "narrowing conversion from 'unsigned int' to signed type 'int' is implementation-defined" مواجه شده باشید. استاندارد C/C++ قالب متمم دو (two's complement) را برای اعداد صحیح علامتدار اجباری نمیکند، بنابراین کامپایلر آزاد است معناشناسی و رفتار تبدیل یک عدد صحیح بدون علامت به عدد صحیح علامتدار را تعیین کند. پیادهسازی Clang از فرمت متمم دو استفاده میکند.
bugprone-no-escape
اشارهگرهایی با مشخصهٔ noescape را پیدا میکند که توسط یک بلوک با اجرای ناهمگام (asynchronously-executed) تسخیر (capture) شدهاند. آرگومانهای بلوک در dispatch_async() و dispatch_after() تضمین شده است که فرار (escape) میکنند، بنابراین اگر اشارهگری با صفت noescape توسط یکی از این بلوکها تسخیر شود، یک خطا محسوب میشود.
در ادامه نمونهای از استفادهٔ نامعتبر از مشخصهٔ noescape آورده شده است.
void foo(__attribute__((noescape)) int *p) {
dispatch_async(queue, ^{
*p = 123;
});
});
bugprone-non-zero-enum-to-bool-conversion
تبدیلهای نوع (cast) ضمنی و صریح از نوع enum به bool را در جایی که نوع enum دارای شمارندهای با مقدار صفر نباشد، شناسایی میکند. اگر enum تنها برای نگهداری مقادیر برابر با شمارندههای خود استفاده شود، در این صورت تبدیل به bool همیشه منجر به مقدار true خواهد شد. این موضوع میتواند منجر به کدهای غیرضروری شود که خوانایی و نگهداریپذیری را کاهش داده و باعث ایجاد باگ شوند.
اگر از enum برای ذخیرهٔ مقادیر دیگر استفاده شود (استفاده به عنوان ماسک بیتی یا مقداردهی اولیهٔ عمدی با صفر)، ممکن است موارد مثبت کاذب (false positive) ایجاد کند. برای مقابله با آنها، میتوان از // NOLINT یا تبدیل نوع ابتدا به نوع زیربنایی پیش از تبدیل به bool استفاده کرد.
توجه به این نکته مهم است که اگر تعریف نوع شمارشی در دسترس نباشد، این بررسی هیچ هشداری تولید نخواهد کرد. علاوه بر این، کلاسهای شمارشی C++11 نیز توسط این بررسی پشتیبانی میشوند.
به طور کلی، این بررسی با شناسایی و علامتگذاری مواردی که در آنها تبدیل نوعهای ضمنی یا صریح از انواع شمارشی به بولی میتواند مشکلات احتمالی ایجاد کند، به بهبود کیفیت و خوانایی کد کمک میکند.
مثال
enum EStatus {
OK = 1,
NOT_OK,
UNKNOWN
};
void process(EStatus status) {
if (!status) {
// this true-branch won't be executed
return;
}
// proceed with "valid data"
}
گزینهها
- EnumIgnoreList
- این گزینه برای نادیده گرفتن انواع enum خاص هنگام بررسی تبدیلهای ضمنی/صریح به bool استفاده میشود. این گزینه فهرستی جدا شده با نقطه-ویرگول از نامهای نوع enum (به صورت fully qualified) یا عبارات منظم منطبق بر نامهای نوع enum را میپذیرد. مقدار پیشفرض یک رشتهٔ خالی است، به این معنی که هیچ enumای نادیده گرفته نخواهد شد.
bugprone-nondeterministic-pointer-iteration-order
استفادههای غیرقطعی (nondeterministic) از اشارهگرها در کانتینرهای بدون ترتیب (unordered containers) را پیدا میکند.
یک نمونهٔ بارز، پیمایش روی کانتینری از اشارهگرها است.
{
int a = 1, b = 2;
std::unordered_set<int *> UnorderedPtrSet = {&a, &b};
for (auto i : UnorderedPtrSet)
f(i);
}
نمونهٔ دیگر، مرتبسازی کانتینری از اشارهگرها است.
{
int a = 1, b = 2;
std::vector<int *> VectorOfPtr = {&a, &b};
std::sort(VectorOfPtr.begin(), VectorOfPtr.end());
}
پیمایش کانتینری از اشارهگرها ممکن است ترتیب اشارهگرهای مختلف را در اجراهای گوناگون یک برنامه به شکل متفاوتی ارائه دهد. در برخی موارد ممکن است این رفتاری قابل قبول باشد، و در موارد دیگر میتواند رفتاری غیرمنتظره به بار آورد. این بررسی به همین دلیل جنبهٔ مشورتی دارد.
این بررسی فقط حلقههای for مبتنی بر بازه (range-based for loops) را روی setها و mapهای unordered تشخیص میدهد. همچنین فراخوانی الگوریتمهای شبیه به مرتبسازی را روی کانتینرهای حاوی اشارهگر شناسایی میکند. سایر استفادههای مشابه پیدا نخواهند شد و به عنوان منفی کاذب (false negative) در نظر گرفته میشوند.
محدودیتها
- این بررسی در حال حاضر بررسی نمیکند که آیا ترتیب پیمایش غیرقطعی احتمالاً یک اشتباه است یا خیر، بلکه در عوض تمامی این پیمایشها را به عنوان مستعد خطا (bugprone) علامتگذاری میکند.
- نوع std::reference_wrapper هنوز در نظر گرفته نشده است.
- تنها حلقههای for مد نظر قرار میگیرند؛ سایر تکرارکنندهها (iterators) میتوانند در بهبودهای بعدی گنجانده شوند.
bugprone-not-null-terminated-result
فراخوانیهای تابعی را پیدا میکند که در آنها امکان ایجاد نتیجهای بدون پایاندهندهٔ تهی (not null-terminated) وجود دارد. معمولاً طول مناسب یک رشته strlen(src) + 1 یا طولی برابر با این عبارت است، زیرا پایاندهندهٔ تهی به فضای اضافی نیاز دارد. بدون پایاندهندهٔ تهی، خواندن رشته ممکن است منجر به رفتار تعریفنشده (undefined behavior) شود.
توابع زیر و توابع معادل مبتنی بر wchar_t آنها بررسی میشوند:
memcpy, memcpy_s, memchr, memmove, memmove_s, strerror_s, strncmp, strxfrm
در ادامه یک مثال واقعی آورده شده است که در آن برنامهنویس فراموش کرده آرگومان سوم ارسالی را، یعنی size_t length، افزایش دهد. به همین دلیل طول حافظهٔ تخصیصیافته برای نگهداری پایاندهندهٔ تهی کافی نیست.
static char *stringCpy(const std::string &str) {
char *result = reinterpret_cast<char *>(malloc(str.size()));
memcpy(result, str.data(), str.size());
return result;
}
علاوه بر صدور هشدارها، قابلیت اصلاح خودکار (fix-it) تمام کدهای لازم را بازنویسی میکند. همچنین تلاش میکند ظرفیت آرایهٔ مقصد را تنظیم نماید:
static char *stringCpy(const std::string &str) {
char *result = reinterpret_cast<char *>(malloc(str.size() + 1));
strcpy(result, str.data());
return result;
}
نکته: هیچ تضمینی وجود ندارد که تمامی تخصیصهای حافظهٔ حساس به مسیر (path-sensitive) بازنویسی شوند.
قواعد تبدیل 'memcpy()'
امکان بازنویسی فراخوانیهای memcpy() و memcpy_s() به چهار تابع زیر وجود دارد: strcpy()، strncpy()، strcpy_s() و strncpy_s()، که دو مورد آخر نسخههای امنتر دو مورد اول هستند. این بررسی توابع مدیریت حافظه مبتنی بر wchar_t را نیز به همین ترتیب بازنویسی میکند.
بازنویسی بر اساس آرایه مقصد
- اگر کپی در آرایه مقصد نتواند دچار سرریز شود [1]، تابع جدید باید تابع کپی قدیمیتر (با پسوند cpy) باشد، زیرا کارآمدتر از نسخه امن است.
- اگر کپی در آرایه مقصد بتواند دچار سرریز شود [1] و WantToUseSafeFunctions روی true تنظیم شده باشد و به دست آوردن ظرفیت آرایه مقصد امکانپذیر باشد، تابع جدید میتواند نسخه امن (با پسوند cpy_s) باشد.
- اگر تابع جدید بتواند نسخه امن باشد و فایلهای ++C در حال تحلیل باشند و آرایه مقصد یک char/wchar_t ساده بدون un/signed باشد، میتوان طول آرایه مقصد را حذف کرد.
- اگر تابع جدید بتواند نسخه امن باشد و آرایه مقصد از نوع un/signed باشد، باید به char */wchar_t * ساده تبدیل (cast) شود.
- [1] سرریز در این حالتها ممکن است:
- اگر ظرفیت آرایه مقصد ناشناخته باشد.
- اگر طول دادهشده برابر با ظرفیت آرایه مقصد باشد.
بازنویسی بر اساس طول رشته مبدا
- اگر طول دادهشده برابر با strlen(source) یا طولی معادل با این عبارت باشد، تابع جدید باید تابع کپی قدیمیتر (با پسوند cpy) باشد، زیرا کارآمدتر از نسخه امن (با پسوند cpy_s) است.
- در غیر این صورت، فرض میکنیم برنامهنویس قصد داشته است که 'N' کاراکتر را کپی کند، بنابراین تابع جدید از نوع شبه ncpy خواهد بود که 'N' کاراکتر را کپی میکند.
تبدیلها با 'strlen()' یا طول معادل این عبارت
این بررسی توابع مدیریت حافظه و رشته مبتنی بر wchar_t را به همین ترتیب تبدیل میکند (که در این میان فقط strerror_s نام مستعار مبتنی بر wchar_t ندارد).
توابع مدیریت حافظه
memcpy لطفاً بخش قواعد تبدیل 'memcpy()' را بررسی کنید.
memchr معمولاً یک C-style cast وجود دارد که باید حذف شود، زیرا نوع بازگشتی تابع جدید strchr صحیح است. طول دادهشده حذف خواهد شد.
memmove اگر توابع امن در دسترس باشند، تابع جدید memmove_s خواهد بود که آرگومان دوم جدیدی شامل طول آرایه مقصد دارد؛ این آرگومان تنظیم شده و طول رشته مبدا یک واحد افزایش مییابد. اگر توابع امن در دسترس نباشند، طول دادهشده یک واحد افزایش مییابد.
memmove_s طول دادهشده یک واحد افزایش مییابد.
توابع مدیریت رشته
strerror_s طول دادهشده یک واحد افزایش مییابد.
strncmp اگر آرگومان سوم برابر با length + 1 آرگومان اول یا دوم باشد، باید بدون عملیات + 1 کوتاه شود.
strxfrm طول دادهشده یک واحد افزایش مییابد.
گزینهها
- WantToUseSafeFunctions
- مقدار true مشخص میکند که محیط هدف پیادهسازیکننده توابع مدیریت حافظه و رشته با پسوند '_s' در نظر گرفته میشود که امنتر از نسخههای قدیمیتر هستند (مانند 'memcpy_s()'). مقدار پیشفرض true است.
bugprone-optional-value-conversion
تبدیلهای بالقوه ناخواسته و زائدی را شناسایی میکند که در آنها مقداری از یک نوع اختیاریمانند (optional-like) استخراج شده و سپس برای ایجاد نمونه جدیدی از همان نوع اختیاریمانند استفاده میشود.
این تبدیلها ممکن است ناشی از سهلانگاری توسعهدهنده، بقایای بازنویسی و بازآفرینی کد (refactoring)، یا شرایط دیگری باشند که میتوانند منجر به استثناهای ناخواسته شوند یا مواردی ایجاد کنند که اختیاری حاصل همواره مقداردهی اولیه شده باشد، که ممکن است رفتاری غیرمنتظره باشد.
برای تشریح این موضوع، قطعه کد مشکلدار زیر را در نظر بگیرید:
#include <optional>
void print(std::optional<int>);
int main()
{
std::optional<int> opt;
// ...
// Unintentional conversion from std::optional<int> to int and back to
// std::optional<int>:
print(opt.value());
// ...
}
رویکرد بهتر این است که بدون استخراج مقدار، opt را مستقیماً به تابع print پاس دهید:
#include <optional>
void print(std::optional<int>);
int main()
{
std::optional<int> opt;
// ...
// Proposed code: Directly pass the std::optional<int> to the print
// function.
print(opt);
// ...
}
با پاس دادن مستقیم opt به تابع print، از تبدیلهای غیرضروری جلوگیری شده و رفتارها یا استثناهای بالقوه ناخواسته به حداقل میرسند.
استخراج مقدار با استفاده از operator * بهصورت پیشفرض تطبیق داده میشود. پشتیبانی از انواع غیراستاندارد اختیاری مانند boost::optional یا absl::optional ممکن است محدود باشد.
گزینهها:
- OptionalTypes
- فهرستی جداشده با نقطهویرگول (semicolon) از نامهای کامل (fully qualified) انواع اختیاری یا عبارات باقاعدهای که با انواع اختیاری تطابق دارند. مقدار پیشفرض ::std::optional;::absl::optional;::boost::optional است.
- ValueMethods
- فهرستی جداشده با نقطهویرگول (semicolon) از نامهای کامل متدها یا عبارات باقاعدهای که با متدها تطابق دارند. مقدار پیشفرض ::value$;::get$ است.
bugprone-parent-virtual-call
فراخوانی متدهای مجازی کلاسهای نیا-...-والد را بهجای فراخوانی متدهای مجازی بازنویسیشدهٔ (overridden) والد شناسایی و تصحیح میکند.
struct A {
int virtual foo() {...}
};
struct B: public A {
int foo() override {...}
};
struct C: public B {
int foo() override { A::foo(); }
// ^^^^^^^^
// warning: qualified name A::foo refers to a member overridden in subclass; did you mean 'B'? [bugprone-parent-virtual-call]
};
bugprone-pointer-arithmetic-on-polymorphic-object
محاسبات اشارهگر (pointer arithmetic) انجامشده روی کلاسهایی که حاوی تابع مجازی هستند را پیدا میکند.
محاسبات اشارهگر روی اشیاء چندریختی (polymorphic) هنگامی که نوع ایستا (static type) اشارهگر با نوع پویا (dynamic type) آن متفاوت باشد، رفتار تعریفنشده (undefined behavior) است؛ زیرا این دو نوع ممکن است اندازههای متفاوتی داشته باشند و در نتیجه اشارهگر جدول متدهای مجازی (vtable) ممکن است به یک آدرس نامعتبر اشاره کند.
یافتن اشارهگرهایی که نوع ایستای آنها شامل یک تابع عضو مجازی است، یک روش مکاشفهای (heuristic) مناسب است، زیرا این احتمال وجود دارد که اشارهگر به یک شیء مشتقشدهٔ متفاوت اشاره کند.
مثال:
struct Base {
virtual ~Base();
int i;
};
struct Derived : public Base {};
void foo(Base* b) {
b += 1;
// warning: pointer arithmetic on class that declares a virtual function can
// result in undefined behavior if the dynamic type differs from the
// pointer type
}
int bar(const Derived d[]) {
return d[1].i; // warning due to pointer arithmetic on polymorphic object
}
// Making Derived final suppresses the warning
struct FinalDerived final : public Base {};
int baz(const FinalDerived d[]) {
return d[1].i; // no warning as FinalDerived is final
}
گزینهها
- IgnoreInheritedVirtualFunctions
- هنگامی که true باشد، اشیایی که صرفاً یک تابع مجازی را به ارث میبرند بررسی نمیشوند. کلاسهایی که تابع مجازی جدیدی اعلان نمیکنند بهطور پیشفرض نادیده گرفته میشوند، زیرا بیشترین سهم را در هشدارهای کاذب (false positives) دارند. پیشفرض: false است.
void bar(Base b[], Derived d[]) {
b += 1; // warning, as Base declares a virtual destructor
d += 1; // warning only if IgnoreVirtualDeclarationsOnly is set to false
}
مراجع
این بررسی با قاعدهٔ SEI Cert به شمارهٔ CTR56-CPP مطابقت دارد: Do not use pointer arithmetic on polymorphic objects https://wiki.sei.cmu.edu/confluence/display/cplusplus/CTR56-CPP.+Do+not+use+pointer+arithmetic+on+polymorphic+objects.
bugprone-posix-return
بررسی میکند که آیا فراخوانی توابع pthread_* یا posix_* (بهجز posix_openpt) انتظار مقادیر بازگشتی منفی را دارد یا خیر. این توابع در صورت موفقیت مقدار 0 و در صورت شکست یک errno را برمیگردانند که تنها مقداری مثبت است.
نمونهای از کاربرد باگدار بدین شکل است:
if (posix_fadvise(...) < 0) {
این حالت هرگز رخ نخواهد داد زیرا مقدار بازگشتی همواره غیرمنفی است. یک اصلاح ساده میتواند بهصورت زیر باشد:
if (posix_fadvise(...) > 0) {
bugprone-random-generator-seed
تمامی موتورهای تولید اعداد شبهتصادفی، نمونهسازیهای مبدل موتور (engine adaptor) و تابع srand() را در صورتی که با آرگومان پیشفرض، عبارت ثابت (constant expression) یا هر نوع قابلپیکربندی توسط کاربر مقداردهی اولیه یا دانهگذاری (seed) شوند نشانهگذاری میکند. موتورهای اعداد شبهتصادفی که با مقداری قابلپیشبینی دانهگذاری شوند ممکن است منجر به آسیبپذیریهایی، برای مثال در پروتکلهای امنیتی، گردند.
مثالها:
void foo() {
std::mt19937 engine1; // Diagnose, always generate the same sequence
std::mt19937 engine2(1); // Diagnose
engine1.seed(); // Diagnose
engine2.seed(1); // Diagnose
std::time_t t;
engine1.seed(std::time(&t)); // Diagnose, system time might be controlled by user
int x = atoi(argv[1]);
std::mt19937 engine3(x); // Will not warn
}
گزینهها
- DisallowedSeedTypes
- فهرستی جداشده با کاما از نام انواع دادهای که غیرمجاز هستند. مقدار پیشفرض time_t,std::time_t است.
مراجع
این بررسی با قوانین استاندارد کدنویسی CERT C++ مطابقت دارد: MSC51-CPP. Ensure your random number generator is properly seeded https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC51-CPP.+Ensure+your+random+number+generator+is+properly+seeded و MSC32-C. Properly seed pseudorandom number generators https://wiki.sei.cmu.edu/confluence/display/c/MSC32-C.+Properly+seed+pseudorandom+number+generators.
bugprone-raw-memory-call-on-non-trivial-type
استفاده از توابع کتابخانه استاندارد C شامل memset، memcpy و memcmp و مشتقات مشابه آنها را روی انواع غیربدیهی (non-trivial types) نشانهگذاری میکند.
این بررسی توابع زیر را تشخیص خواهد داد: memset، std::memset، std::memcpy، memcpy، std::memmove، memmove، std::strcpy، strcpy، memccpy، stpncpy، strncpy، std::memcmp، memcmp، std::strcmp، strcmp، strncmp.
گزینهها
- MemSetNames
- مشخص کردن توابع اضافی برای نشانهگذاری که عملکردی مشابه memset دارند. نامها را در یک فهرست جداشده با نقطهویرگول (semicolon) مشخص کنید. پیشفرض یک رشته خالی است.
- MemCpyNames
- مشخص کردن توابع اضافی برای نشانهگذاری که عملکردی مشابه memcpy دارند. نامها را در یک فهرست جداشده با نقطهویرگول مشخص کنید. پیشفرض یک رشته خالی است.
- MemCmpNames
- مشخص کردن توابع اضافی برای نشانهگذاری که عملکردی مشابه memcmp دارند. نامها را در یک فهرست جداشده با نقطهویرگول مشخص کنید. پیشفرض یک رشته خالی است.
این بررسی با قاعده استانداردهای کدنویسی CERT C++ مطابقت دارد: OOP57-CPP. Prefer special member functions and overloaded operators to C Standard Library functions https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP57-CPP.+Prefer+special+member+functions+and+overloaded+operators+to+C+Standard+Library+functions.
bugprone-redundant-branch-condition
متغیرهای شرطی را در دستورهای if تودرتو پیدا میکند که در دستور if بیرونی نیز بررسی شدهاند و مقداری در آنها تغییر نکرده است.
مثال ساده:
bool onFire = isBurning();
if (onFire) {
if (onFire)
scream();
}
در اینجا onFire هم در دستور if بیرونی و هم در دستور if درونی بدون امکان تغییر میان دو بررسی، چک شده است. این بررسی برای این کد هشدار داده و حذف بررسی دوم متغیر onFire را پیشنهاد میکند.
بررسیکننده همچنین بررسیهای شرطی زائد را در صورتی که متغیر شرط عملوند یک عملگر منطقی «و» (&&) یا «یا»ی منطقی (||) باشد تشخیص میدهد:
bool onFire = isBurning();
if (onFire) {
if (onFire && peopleInTheBuilding > 0)
scream();
}
bool onFire = isBurning();
if (onFire) {
if (onFire || isCollapsing())
scream();
}
در حالت اول («و»ی منطقی) اصلاح پیشنهادی حذف متغیر شرط زائد و نگهداشتن طرف دیگر && است. در حالت دوم («یا»ی منطقی) کل if مشابه مثال ساده بالا حذف میشود.
شرط دستور if بیرونی نیز میتواند یک عبارت «و»ی منطقی (&&) باشد:
bool onFire = isBurning();
if (onFire && fireFighters < 10) {
if (someOtherCondition()) {
if (onFire)
scream();
}
}
همچنین اگر هر دو دستور بیرونی و درونی به صورت عبارات منطقی باشند خطا تشخیص داده میشود (دستور بیرونی یک «و»ی منطقی (&&) و دستور درونی یک «و»ی منطقی (&&) یا «یا»ی منطقی (||)). دستور if درونی لزومی ندارد فرزند مستقیم دستور بیرونی باشد.
در صورتی که متغیر شرط احتمالاً بین دو بررسی تغییر کرده باشد، هیچ خطایی تشخیص داده نمیشود:
bool onFire = isBurning();
if (onFire) {
tryToExtinguish(onFire);
if (onFire && peopleInTheBuilding > 0)
scream();
}
هرگونه تغییر احتمالی مد نظر قرار میگیرد، بنابراین اگر متغیر شرط یک متغیر محلی تابع نباشد، volatile باشد یا دارای یک نام مستعار (اشارهگر یا ارجاع) باشد، هیچ هشداری صادر نمیشود.
محدودیتها
شاخه else در حال حاضر برای متغیر شرط منفیشده بررسی نمیشود:
bool onFire = isBurning();
if (onFire) {
scream();
} else {
if (!onFire) {
continueWork();
}
}
بررسیکننده در حال حاضر تنها بررسی زائد متغیرهای شرطی منفرد را تشخیص میدهد. عبارتهای پیچیدهتر بررسی نمیشوند:
if (peopleInTheBuilding == 1) {
if (peopleInTheBuilding == 1) {
doSomething();
}
}
bugprone-reserved-identifier
نامهای cert-dcl37-c و cert-dcl51-cpp به عنوان نام مستعار به این بررسی هدایت میشوند.
استفاده از شناسههای رزروشده برای استفاده توسط پیادهسازی (implementation) را بررسی میکند.
استانداردهای C و C++ هر دو نامهای زیر را برای چنین استفادهای رزرو کردهاند:
- شناسههایی که با یک خط زیر (underscore) و به دنبال آن یک حرف بزرگ انگلیسی آغاز میشوند؛
- شناسههایی در فضاینام عمومی (global namespace) که با یک خط زیر آغاز میشوند.
استاندارد C علاوهبر این، نامهایی که با دو خط زیر متوالی آغاز میشوند را رزرو میکند، در حالی که استاندارد C++ این محدودیت را تشدید کرده و نامهایی که در هر کجای آنها دو خط زیر متوالی وجود داشته باشد را رزرو میکند.
نقض قواعد نامگذاری بالا به رفتار تعریفنشده (undefined behavior) منجر میشود.
namespace NS {
void __f(); // name is not allowed in user code
using _Int = int; // same with this
#define cool__macro // also this
}
int _g(); // disallowed in global namespace only
این بررسی همچنین میتواند معکوس شود، یعنی طوری پیکربندی شود که هر شناسه که یک شناسه رزروشده نیست را علامتگذاری کند. این حالت برای مثال توسط توسعهدهندگان کتابخانه استاندارد استفاده میشود تا اطمینان حاصل کنند به فضاینام کاربر تعدی نکنند.
این بررسی (هنوز) سایر نامهای رزروشده، مانند نامهای ماکرو که با کلمات کلیدی زبان یکسان هستند، و نامهایی که بهطور خاص توسط استانداردهای زبان رزرو شدهاند، مانند 'zombie names' در C++ و جهتگیریهای آینده کتابخانه C را بررسی نمیکند.
این بررسی مطابق با قانون DCL37-C از استاندارد کدنویسی CERT C تحت عنوان Do not declare or define a reserved identifier https://wiki.sei.cmu.edu/confluence/display/c/DCL37-C.+Do+not+declare+or+define+a+reserved+identifier و همچنین معادل C++ آن، یعنی DCL51-CPP تحت عنوان Do not declare or define a reserved identifier https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL51-CPP.+Do+not+declare+or+define+a+reserved+identifier است.
گزینهها
- Invert
- اگر مقدار آن true باشد، بررسی را معکوس میکند، یعنی نامهایی را که رزرو نشدهاند علامتگذاری میکند. پیشفرض false است.
- AllowedIdentifiers
- فهرستی از عبارات باقاعده (regular expressions) جداشده با نقطهویرگول که بررسی از آنها صرفنظر میکند. پیشفرض یک فهرست خالی است.
bugprone-return-const-ref-from-parameter
دستورهای return را که یک پارامتر ارجاع ثابت (constant reference) را به عنوان ارجاع ثابت بازمیگردانند، شناسایی میکند. این امر در صورتی که فراخواننده از xvalueها به عنوان آرگومان استفاده کند، ممکن است موجب خطاهای استفاده پس از آزادسازی (use-after-free) شود.
در C++، پارامترهای ارجاع ثابت میتوانند xvalueها را بپذیرند که پس از فراخوانی تخریب خواهند شد. هنگامی که تابع چنین پارامتری را نیز به عنوان ارجاع ثابت بازمیگرداند، ارجاع بازگرداندهشده ممکن است پس از نابود شدن شیئی که به آن اشاره دارد، مورد استفاده قرار گیرد.
مثال
struct S {
int v;
S(int);
~S();
};
const S &fn(const S &a) {
return a;
}
const S& s = fn(S{1});
s.v; // use after free
این مشکل را میتوان با اعلان یک سربارگذاری (overload) از تابع مشکلساز برطرف کرد که در آن پارامتر const & به صورت && اعلان شده باشد. توسعهدهنده باید اطمینان حاصل کند که پیادهسازی آن تابع باعث ایجاد use-after-free نمیشود؛ همان خطایی که این بررسی نسبت به آن هشدار میدهد. همچنین نشانهگذاری چنین سربارگذاری && به صورت deleted نیز هشدار را برطرف میکند. در صورتی که بسته به جریان کنترل تابع، پارامترهای مختلف const & بازگردانده شوند، یک سربارگذاری که در آن تمام پارامترهای مشکلساز const & به صورت && اعلان شده باشند، مشکل را حل خواهد کرد.
این مشکل همچنین میتواند با افزودن ویژگی [[clang::lifetimebound]] حل شود. Clang به صورت پیشفرض هشدار -Wdangling را فعال میکند که میتواند سوءاستفاده از تابع حاشیهنویسیشده را شناسایی کند. برای جزئیات به ویژگی lifetimebound در https://clang.llvm.org/docs/AttributeReference.html#lifetimebound مراجعه کنید.
const int &f(const int &a [[clang::lifetimebound]]) { return a; } // no warning
const int &v = f(1); // warning: temporary bound to local reference 'v' will be destroyed at the end of the full-expression [-Wdangling]
bugprone-shared-ptr-array-mismatch
مقداردهیهای اولیه اشارهگرهای هوشمند اشتراکی C++ (از نوع shared pointer) از نوع غیرآرایهای را که با یک آرایه مقداردهی اولیه شدهاند، پیدا میکند.
اگر یک اشارهگر اشتراکی std::shared_ptr<T> با عبارت جدید new T[] مقداردهی شود، حافظه به طور صحیح آزادسازی نمیشود. در این حالت اشارهگر برای آزادسازی حافظه هدف از delete معمولی استفاده میکند، در حالی که به فراخوانی delete[] نیاز است. عبارت std::shared_ptr<T[]> عملگر delete صحیح را فراخوانی میکند.
این بررسی در صورتی که در یک اعلان متغیر تکی استفاده شده باشد (یک متغیر در یک دستور)، پیشنهاد جایگزینی shared_ptr<T> با shared_ptr<T[]> را ارائه میدهد.
مثال:
std::shared_ptr<Foo> x(new Foo[10]); // -> std::shared_ptr<Foo[]> x(new Foo[10]);
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
std::shared_ptr<Foo> x1(new Foo), x2(new Foo[10]); // no replacement
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
std::shared_ptr<Foo> x3(new Foo[10], [](const Foo *ptr) { delete[] ptr; }); // no warning
struct S {
std::shared_ptr<Foo> x(new Foo[10]); // no replacement in this case
// ^ warning: shared pointer to non-array is initialized with array [bugprone-shared-ptr-array-mismatch]
};
این بررسی به طور جزئی قانون MEM51-CPP از استاندارد کدنویسی CERT C++ تحت عنوان Properly deallocate dynamically allocated resources https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM51-CPP.+Properly+deallocate+dynamically+allocated+resources را پوشش میدهد. با این حال، فقط حالت std::shared_ptr توسط این بررسی شناسایی میشود.
bugprone-signal-handler
ساختارهای خاصی را در توابع مدیر سیگنال (signal handler) که میتوانند باعث رفتار تعریفنشده شوند، پیدا میکند. قوانین مربوط به آنچه مجاز است بین نسخههای مختلف زبان C++ تفاوت دارد.
قوانین بررسیشده مدیر سیگنال برای C:
- •
- فراخوانی توابعی که از نظر ناهمگام ایمن نیستند (non-asynchronous-safe) مجاز نیست.
قوانین بررسیشده مدیر سیگنال تا و شامل C++14:
- فراخوانی توابعی که از نظر ناهمگام ایمن نیستند مجاز نیست.
- ساختارهای کدی اختصاصی C++ در توابع مدیر سیگنال مجاز نیستند. به عبارت دیگر، فقط استفاده از زیرمجموعه مشترک C و C++ مجاز است.
- فراخوانی توابعی با پیوند غیر C (از جمله خود تابع مدیر سیگنال) مجاز نیست.
این بررسی در C++17 و نسخههای بعدی غیرفعال است.
ایمن بودن از نظر ناهمگام (Asynchronous-safety) از طریق مقایسه نام تابع با مجموعهای از توابع شناختهشده تعیین میشود. علاوه بر این، تابع باید از فایل سرآیند سیستم (system header) گنجانده شده و در فضاینام سراسری باشد. آرگومانهای (احتمالی) ارسالشده به تابع بررسی نمیشوند. هر تابعی که نتوان ایمن بودن ناهمگام آن را مشخص کرد، توسط این بررسی ناامن فرض میشود، از جمله توابع کاربر که فقط اعلان آنها قابل مشاهده است. فراخوانی توابع تعریفشده توسط کاربر با تعاریف قابل مشاهده، به صورت بازگشتی بررسی میشوند.
این بررسی قانون SIG30-C از استاندارد کدنویسی CERT C تحت عنوان Call only asynchronous-safe functions within signal handlers https://www.securecoding.cert.org/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers و همچنین قانون MSC54-CPP تحت عنوان A signal handler must be a plain old function https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC54-CPP.+A+signal+handler+must+be+a+plain+old+function را پیادهسازی میکند. این بررسی دارای نامهای مستعار cert-sig30-c و cert-msc54-cpp است.
گزینهها
- AsyncSafeFunctionSet
- مجموعه توابعی که به عنوان ایمن در برابر ناهمگامی (asynchronous-safe) در نظر گرفته میشوند (و بنابراین در گردانندههای سیگنال یا همان signal handlerها مجاز هستند) را مشخص میکند. این گزینه میتواند روی مقادیر زیر تنظیم شود:
- •
- minimal
- مجموعه حداقلی تعریفشده در قاعده CERT SIG30-C را انتخاب میکند و شامل توابع abort()، _Exit()، quick_exit() و signal() است.
- •
- POSIX
- مجموعه
بزرگتری
از توابع
فهرستشده
در POSIX.1-2017 را
انتخاب
میکند
(برای
اطلاعات
بیشتر
پیوند
https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
را ببینید).
توابع زیر
شامل آن
هستند: _Exit،
_exit، abort، accept،
access، aio_error، aio_return،
aio_suspend، alarm، bind،
cfgetispeed، cfgetospeed،
cfsetispeed، cfsetospeed، chdir،
chmod، chown، clock_gettime،
close، connect، creat،
dup، dup2، execl،
execle، execv، execve،
faccessat، fchdir، fchmod،
fchmodat، fchown، fchownat،
fcntl، fdatasync، fexecve،
ffs، fork، fstat،
fstatat، fsync، ftruncate،
futimens، getegid، geteuid،
getgid، getgroups، getpeername،
getpgrp، getpid، getppid،
getsockname، getsockopt، getuid،
htonl، htons، kill،
link، linkat، listen،
longjmp، lseek، lstat،
memccpy، memchr، memcmp،
memcpy، memmove، memset،
mkdir، mkdirat، mkfifo،
mkfifoat، mknod، mknodat،
ntohl، ntohs، open،
openat، pause، pipe،
poll، posix_trace_event،
pselect، pthread_kill،
pthread_self، pthread_sigmask،
quick_exit، raise، read،
readlink، readlinkat، recv،
recvfrom، recvmsg، rename،
renameat، rmdir، select،
sem_post، send، sendmsg،
sendto، setgid، setpgid،
setsid، setsockopt، setuid،
shutdown، sigaction، sigaddset،
sigdelset، sigemptyset،
sigfillset، sigismember،
siglongjmp، signal، sigpause،
sigpending، sigprocmask،
sigqueue، sigset، sigsuspend،
sleep، sockatmark، socket،
socketpair، stat، stpcpy،
stpncpy، strcat، strchr،
strcmp، strcpy، strcspn،
strlen، strncat، strncmp،
strncpy، strnlen، strpbrk،
strrchr، strspn، strstr،
strtok_r، symlink، symlinkat،
tcdrain، tcflow، tcflush،
tcgetattr، tcgetpgrp،
tcsendbreak، tcsetattr،
tcsetpgrp، time،
timer_getoverrun، timer_gettime،
timer_settime، times، umask،
uname، unlink، unlinkat،
utime، utimensat، utimes،
wait، waitpid، wcpcpy،
wcpncpy، wcscat، wcschr،
wcscmp، wcscpy، wcscspn،
wcslen، wcsncat، wcsncmp،
wcsncpy، wcsnlen، wcspbrk،
wcsrchr، wcsspn، wcsstr،
wcstok، wmemchr، wmemcmp،
wmemcpy، wmemmove، wmemset،
write
تابع quick_exit در فهرست POSIX گنجانده نشده است اما در اینجا در مجموعه توابع ایمن قرار دارد.
مقدار پیشفرض POSIX است.
bugprone-signed-char-misuse
بررسی cert-str34-c به عنوان یک نام مستعار (alias) برای این بررسی به اینجا هدایت میشود. برای نام مستعار CERT، گزینه DiagnoseSignedUnsignedCharComparisons روی false تنظیم شده است.
تبدیلهای نوع signed char -> integer را که ممکن است نشاندهنده خطای برنامهنویسی باشند، پیدا میکند. مشکل اساسی با نوع داده signed char این است که ممکن است نویسههای غیر ASCII را به عنوان مقادیر منفی ذخیره کند. این رفتار میتواند چه در هنگام تبدیل صریح (explicit) و چه در هنگام تبدیل ضمنی (implicit)، منجر به برداشت نادرست از کد نوشتهشده شود.
هنگامی که کد حاوی یک تبدیل صریح signed char -> integer است، برنامهنویس احتمالاً انتظار دارد که مقدار تبدیلشده با کد نویسه (مقداری در بازه [0..255]) مطابقت داشته باشد، اما مقدار واقعی در بازه [-128..127] خواهد بود. برای جلوگیری از این نوع سوءبرداشت، روش مطلوب تبدیل از signed char به یک مقدار عدد صحیح این است که ابتدا به unsigned char تبدیل شود، که تمام نویسهها را در بازه مثبت [0..255] مطابق با کدهای نویسه شناختهشده ذخیره میکند.
در صورت تبدیل ضمنی، برنامهنویس ممکن است اصلاً متوجه رخ دادن تبدیل و استفاده از مقدار char به عنوان یک عدد صحیح نباشد. مواردی وجود دارد که این ناآگاهی ممکن است منجر به کدی با نقص عملکردی شود. به عنوان مثال، بررسی برابری متغیرهای signed char و unsigned char کاری است که باید در کد C++ از آن اجتناب کرد. در حین این مقایسه، هر دو متغیر به اعداد صحیحی تبدیل میشوند که دامنههای مقادیر متفاوتی دارند. برای signed char، نویسههای غیر ASCII به عنوان مقداری در بازه [-128..-1] ذخیره میشوند، در حالی که همان نویسهها برای یک unsigned char در بازه [128..255] ذخیره میشوند.
اینکه نوع داده ساده char به صورت پیشفرض به عنوان signed char در نظر گرفته شود و در نتیجه توسط این بررسی شناسایی شود یا خیر، به پلتفرم واقعی بستگی دارد. برای تغییر رفتار پیشفرض میتوانید از گزینههای کامپایل -funsigned-char و -fsigned-char استفاده کنید.
در حال حاضر، این بررسی در موارد زیر هشدار میدهد:
- مقدار signed char به یک متغیر عدد صحیح (integer) انتساب داده شود
- انواع signed char و unsigned char با عملگر برابری/نابرابری مقایسه شوند
- نوع signed char در اندیس آرایه به یک عدد صحیح تبدیل شود
همچنین ببینید: STR34-C. Cast characters to unsigned char before converting to larger integer sizes https://wiki.sei.cmu.edu/confluence/display/c/STR34-C.+Cast+characters+to+unsigned+char+before+converting+to+larger+integer+sizes
یک مثال خوب از توضیحات CERT زمانی است که از متغیر char برای خواندن از فایلی استفاده میشود که ممکن است حاوی نویسههای غیر ASCII باشد. مشکل زمانی بروز میکند که کد از مقدار صحیح -1 به عنوان EOF استفاده میکند، در حالی که کد نویسه 255 نیز در نمایش متمم دو (two's complement) از نوع داده char به صورت -1 ذخیره میشود. نمونه سادهای از این مورد را در زیر مشاهده کنید. این کد نه تنها با رسیدن به انتهای فایل، بلکه هنگام دریافت نویسهای با کد 255 نیز متوقف میشود.
#define EOF (-1)
int read(void) {
char CChar;
int IChar = EOF;
if (readChar(CChar)) {
IChar = CChar;
}
return IChar;
}
یک روش مناسب برای اصلاح کد بالا، تبدیل متغیر char به یک مقدار unsigned char در ابتدا است.
#define EOF (-1)
int read(void) {
char CChar;
int IChar = EOF;
if (readChar(CChar)) {
IChar = static_cast<unsigned char>(CChar);
}
return IChar;
}
مورد استفاده دیگر، بررسی برابری دو متغیر char با علامتداری (signedness) متفاوت است. در محدوده مقادیر غیر ASCII، این مقایسه بین یک signed char و یک unsigned char همواره مقدار false را برمیگرداند.
bool compare(signed char SChar, unsigned char USChar) {
if (SChar == USChar)
return true;
return false;
}
سادهترین راه برای تصحیح این نوع مقایسه، تبدیل نوع (cast) یکی از آرگومانها است تا هر دو آرگومان نوع یکسانی داشته باشند.
bool compare(signed char SChar, unsigned char USChar) {
if (static_cast<unsigned char>(SChar) == USChar)
return true;
return false;
}
گزینهها
- CharTypedefsToIgnore
- فهرستی از نامهای typedef که با نقطه-ویرگول (semicolon) از یکدیگر جدا شدهاند. در این فهرست میتوان typedefهایی برای char یا signed char مشخص کرد که توسط بررسی نادیده گرفته خواهند شد. این ویژگی زمانی مفید است که یک typedef یک نام مستعار صحیح مانند sal_Int8 یا int8_t معرفی میکند. در چنین حالتی، اشتباه در برداشت انسانی مشکلی ایجاد نمیکند. مقدار پیشفرض یک رشتهٔ خالی است.
- DiagnoseSignedUnsignedCharComparisons
- هنگامی که true باشد، بررسی روی مقایسههای بین signed char و unsigned char هشدار میدهد؛ در غیر این صورت این مقایسهها نادیده گرفته میشوند. بهطور پیشفرض، این گزینه روی true تنظیم شده است.
bugprone-sizeof-container
این بررسی کاربردهای sizeof بر روی عبارتهایی از نوع کانتینرهای STL را پیدا میکند. به احتمال زیاد کاربر قصد داشته به جای آن از .size() استفاده کند.
تمامی انواع class/struct اعلانشده در فضای نام std:: که دارای متد const size() هستند، به عنوان کانتینر در نظر گرفته میشوند، به استثنای std::bitset و std::array.
مثالها:
std::string s; int a = 47 + sizeof(s); // warning: sizeof() doesn't return the size of the container. Did you mean .size()? int b = sizeof(std::string); // no warning, probably intended. std::string array_of_strings[10]; int c = sizeof(array_of_strings) / sizeof(array_of_strings[0]); // no warning, definitely intended. std::array<int, 3> std_array; int d = sizeof(std_array); // no warning, probably intended.
bugprone-sizeof-expression
این بررسی کاربردهایی از عبارتهای sizeof را پیدا میکند که به احتمال زیاد خطا هستند.
عملگر sizeof اندازه (بر حسب بایت) عملوند خود را برمیگرداند، که ممکن است یک عبارت یا نام پرانتزگذاریشدهٔ یک نوع داده باشد. استفادهٔ نادرست از این عملگر ممکن است منجر به خطاها و آسیبپذیریهای احتمالی نرمافزاری شود.
استفادهٔ مشکوک از 'sizeof(K)'
یک اشتباه رایج، پرسوجوی sizeof برای یک لیترال عدد صحیح است. این کار معادل استعلام اندازهٔ نوع دادهٔ آن (احتمالاً int) است. هدف برنامهنویس احتمالاً صرفاً بهدست آوردن خود عدد صحیح بوده است، نه اندازهٔ آن.
#define BUFLEN 42 char buf[BUFLEN]; memset(buf, 0, sizeof(BUFLEN)); // sizeof(42) ==> sizeof(int)
استفادهٔ مشکوک از 'sizeof(expr)'
در مواردی که یک enum یا عدد صحیح برای نمایش یک نوع استفاده میشود، یک اشتباه رایج اجرای sizeof روی عدد صحیح یا enum نمایانگر آن نوع است که باید توسط sizeof استفاده میشد. این امر منجر به دریافت اندازهٔ عدد صحیح میشود و نه نوعی که آن عدد صحیح نمایش میدهد:
enum data_type {
FLOAT_TYPE,
DOUBLE_TYPE
};
struct data {
data_type type;
void* buffer;
data_type get_type() {
return type;
}
};
void f(data d, int numElements) {
// should be sizeof(float) or sizeof(double), depending on d.get_type()
int numBytes = numElements * sizeof(d.get_type());
...
}
استفادهٔ مشکوک از 'sizeof(this)'
کلمهٔ کلیدی this به اشارهگری به یک شیء از نوع مشخص ارزیابی میشود. عبارت sizeof(this) اندازهٔ یک اشارهگر را برمیگرداند. برنامهنویس به احتمال زیاد اندازهٔ خود شیء را میخواسته است و نه اندازهٔ اشارهگر را.
class Point {
[...]
size_t size() { return sizeof(this); } // should probably be sizeof(*this)
[...]
};
استفادهٔ مشکوک از 'sizeof(char*)'
تفاوت ظریفی میان اعلان یک رشتهٔ متنی با char* A = "" و char A[] = "" وجود دارد. حالت اول به جای نوع تجمعی char[]، دارای نوع char* است. استفاده از sizeof روی شیئی که با نوع char* اعلان شده است، به جای تعداد نویسهها (بایتهای) موجود در لیترال رشته، اندازهٔ یک اشارهگر را برمیگرداند.
const char* kMessage = "Hello World!"; // const char kMessage[] = "...";
void getMessage(char* buf) {
memcpy(buf, kMessage, sizeof(kMessage)); // sizeof(char*)
}
استفادهٔ مشکوک از 'sizeof(A*)'
یک اشتباه رایج، محاسبهٔ اندازهٔ یک اشارهگر به جای اشارهشوندهٔ آن است. این حالتها ممکن است به دلیل تبدیل صریح (explicit cast) یا تبدیل ضمنی (implicit conversion) رخ دهند.
int A[10]; memset(A, 0, sizeof(A + 0)); struct Point point; memset(point, 0, sizeof(&point));
استفادهٔ مشکوک از 'sizeof(...)/sizeof(...)'
تقسیم عبارتهای sizeof معمولاً برای بازیابی تعداد عناصر یک نوع تجمعی استفاده میشود. این بررسی در موارد ناسازگار یا مشکوک هشدار میدهد.
در مثال زیر، موجودیت ۱۰ بایتی است و با نوع int که ۴ بایتی است ناسازگار میباشد.
char buf[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // sizeof(buf) => 10
void getMessage(char* dst) {
memcpy(dst, buf, sizeof(buf) / sizeof(int)); // sizeof(int) => 4 [incompatible sizes]
}
در مثال زیر، عبارت sizeof(Values) اندازهٔ char* را برمیگرداند. اعلان آن ممکن است بهراحتی گمراهکننده باشد، اما در اعلان پارامتر، اندازهٔ '10' نادیده گرفته شده و تابع یک char* دریافت میکند.
char OrderedValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return CompareArray(char Values[10]) {
return memcmp(OrderedValues, Values, sizeof(Values)) == 0; // sizeof(Values) ==> sizeof(char*) [implicit cast to char*]
}
عبارت مشکوک 'sizeof' در 'sizeof'
ضرب عبارتهای sizeof معمولاً بیمعنی است و احتمالاً یک خطای منطقی محسوب میشود. در مثال زیر، برنامهنویس به جای / از * استفاده کرده است.
const char kMessage[] = "Hello World!";
void getMessage(char* buf) {
memcpy(buf, kMessage, sizeof(kMessage) * sizeof(char)); // sizeof(kMessage) / sizeof(char)
}
این بررسی ممکن است روی کدهایی که از ماکروی arraysize استفاده میکنند فعال شود. کد زیر به درستی کار میکند، اما باید با استفاده صرف از عملگر sizeof سادهسازی شود.
extern Object objects[100];
void InitializeObjects() {
memset(objects, 0, arraysize(objects) * sizeof(Object)); // sizeof(objects)
}
استفاده مشکوک از 'sizeof(sizeof(...))'
گرفتن sizeof از یک sizeof دیگر بیمعنی است و معمولاً خطایی است که پشت ماکروها پنهان شده است.
#define INT_SZ sizeof(int)
int buf[] = { 42 };
void getInt(int* dst) {
memcpy(dst, buf, sizeof(INT_SZ)); // sizeof(sizeof(int)) is suspicious.
}
استفادههای مشکوک از 'sizeof(...)' در محاسبات اشارهگر
عملگرهای محاسباتی روی اشارهگرها به طور خودکار نتیجه را بر اساس اندازه نوع داده مورد اشاره مقیاسبندی میکنند. استفاده بیشتر از sizeof در محاسبات اشارهگر معمولاً به نتیجهای ناخواسته منجر میشود.
مقیاسبندی نتیجه تفاضل اشارهگرها
تفریق دو اشارهگر منجر به یک عبارت عدد صحیح (از نوع ptrdiff_t) میشود که فاصله بین دو شیء مورد اشاره را بر حسب "تعداد اشیاء بین آنها" بیان میکند. یک اشتباه رایج این است که تصور شود نتیجه "تعداد بایتهای بین آنها" است و تفاضل با sizeof مقیاسبندی شود؛ مانند P1 - P2 == N * sizeof(T) (به جای P1 - P2 == N) یا (P1 - P2) / sizeof(T) به جای P1 - P2.
void splitFour(const Obj* Objs, size_t N, Obj Delimiter) {
const Obj *P = Objs;
while (P < Objs + N) {
if (*P == Delimiter) {
break;
}
}
if (P - Objs != 4 * sizeof(Obj)) { // Expecting a distance multiplied by sizeof is suspicious.
error();
}
}
void iterateIfEvenLength(int *Begin, int *End) {
auto N = (Begin - End) / sizeof(int); // Dividing by sizeof() is suspicious.
if (N % 2)
return;
// ...
}
گام برداشتن اشارهگر با یک عدد صحیح مقیاسبندیشده
برعکس، هنگام انجام محاسبات اشارهگر برای جمع یا تفریق از یک اشارهگر، عملگر محاسباتی به طور ضمنی مقداری را که در واقع به اشارهگر اضافه میشود با اندازه شیء اشارهشده مقیاسبندی میکند؛ چرا که در Ptr + N انتظار میرود N به معنای "تعداد اشیاء برای گام برداشتن" باشد، نه "تعداد بایتها برای گام برداشتن".
دیدن محاسبه یک اشارهگر که در آن sizeof وجود دارد مشکوک است و نتیجه آن معمولاً ناخواسته و اغلب خارج از محدوده (out of bounds) است. عبارت Ptr + sizeof(T) اشارهگر را به اندازه sizeof(T) عنصر جابهجا میکند که عملاً فاکتور مقیاسبندی را به توان ۲ میرساند.
به طور مشابه، ضرب یا تقسیم یک مقدار عددی با sizeof یک عنصر یا کل بافر مشکوک است، زیرا رابطه ابعادی بین مقدار عددی و نتیجه واقعی sizeof همیشه قابل استنتاج نیست. در حالی که افزایش مقیاس یک عدد صحیح (ضرب) با sizeof احتمالاً همیشه یک مشکل است، کاهش مقیاس (تقسیم) در صورتی که توسعهدهنده آگاه باشد که تقسیم بین تعداد مناسبی از _بایتها_ و یک مقدار sizeof انجام میشود، ذاتاً خطرناک نیست. غیرفعال کردن WarnOnOffsetDividedBySizeOf هشدارها را فقط به حالت ضرب محدود میکند.
این بررسی همچنین موارد مشکوک استفاده از alignof و offsetof در محاسبات اشارهگر را بررسی میکند، زیرا هر دو "اندازه" را بر حسب بایت برمیگردانند و نه عنصر، که بالقوه منجر به آفستهای با مقیاسبندی مضاعف میشود.
struct Message { /* ... */; char Flags[8]; };
void clearFlags(Message *Array, size_t N) {
const Message *End = Array + N;
while (Array < End) {
memset(Array + offsetof(Message, Flags), // Suspicious pointer arithmetic using offsetof()!
0, sizeof(Message::Flags));
++Array;
}
}
برای این الگوی نادرست بررسیشده، cert-arr39-c به عنوان یک نام مستعار از این بررسی به اینجا هدایت میشود.
این بررسی با قاعده استانداردهای کدنویسی CERT C مطابقت دارد: ARR39-C. Do not add or subtract a scaled integer to a pointer http://wiki.sei.cmu.edu/confluence/display/c/ARR39-C.+Do+not+add+or+subtract+a+scaled+integer+to+a+pointer.
محدودیتها
مواردی که نوعِ اشارهشونده (pointee) اندازهای برابر با 1 بایت دارد (مانند، و مهمتر از همه، char) مستثنی هستند.
گزینهها
- WarnOnSizeOfConstant
- زمانی که true باشد، این بررسی روی عبارتی مانند sizeof(CONSTANT) هشدار میدهد. مقدار پیشفرض true است.
- WarnOnSizeOfIntegerExpression
- زمانی که true باشد، این بررسی روی عبارتی مانند sizeof(expr) که در آن نتیجه عبارت یک عدد صحیح است، هشدار میدهد. مقدار پیشفرض false است.
- WarnOnSizeOfThis
- زمانی که true باشد، این بررسی روی عبارتی مانند sizeof(this) هشدار میدهد. مقدار پیشفرض true است.
- WarnOnSizeOfCompareToConstant
- زمانی که true باشد، این بررسی روی عبارتی مانند sizeof(expr) <= k برای یک ثابت مشکوک k، در حالی که k برابر با 0 یا بزرگتر از 0x8000 باشد، هشدار میدهد. مقدار پیشفرض true است.
- WarnOnSizeOfPointerToAggregate
- زمانی که true باشد، اگر آرگومان sizeof یک نوع اشارهگر به نوع تجمیعی (pointer-to-aggregate)، عبارتی که مقداری از نوع اشارهگر به نوع تجمیعی را برمیگرداند، یا عبارتی باشد که حاصل تبدیل آرایه به اشارهگر است (که میتواند ضمنی یا صریح باشد، مانند array + 2 یا (int *)array)، این بررسی هشدار میدهد. مقدار پیشفرض true است.
- WarnOnSizeOfPointer
- زمانی که true باشد، این بررسی تمام عباراتی را گزارش میکند که آرگومان sizeof در آنها عبارتی است که یک اشارهگر تولید میکند (بهجز چند عبارت اصطلاحی که احتمالاً عامدانه و درست هستند). این گزینه رخدادهای CWE 467 را شناسایی میکند. مقدار پیشفرض false است.
- WarnOnOffsetDividedBySizeOf
- زمانی که true باشد، این بررسی روی محاسبات اشارهگر (pointer arithmetic) که در آن تعداد عناصر از طریق تقسیم با sizeof(...) به دست میآید، هشدار میدهد؛ مانند Ptr + Bytes / sizeof(*T). مقدار پیشفرض true است.
- WarnOnSizeOfInLoopTermination
- زمانی که true باشد، این بررسی در مورد استفاده نادرست از عبارت sizeof در شرط پایان حلقه هشدار میدهد. این هشدار زمانی فعال میشود که به نظر برسد عبارت sizeof به اشتباه برای تعیین تعداد عناصر آرایه/بافر استفاده شده است؛ مانند long arr[10]; for(int i = 0; i < sizeof(arr); i++) { ... }. مقدار پیشفرض true است.
bugprone-spuriously-wake-up-functions
فراخوانیهای توابع cnd_wait، cnd_timedwait، wait، wait_for یا wait_until را زمانی پیدا میکند که تابع از داخل حلقهای که برقراری یک گزاره شرطی را بررسی میکند فراخوانی نشده باشد، یا تابع فاقد پارامتر شرطی باشد.
if (condition_predicate) {
condition.wait(lk);
}
if (condition_predicate) {
if (thrd_success != cnd_wait(&condition, &lock)) {
}
}
این بررسی مطابق با استاندارد کدنویسی CERT C++ قاعده CON54-CPP. Wrap functions that can spuriously wake up in a loop https://wiki.sei.cmu.edu/confluence/display/cplusplus/CON54-CPP.+Wrap+functions+that+can+spuriously+wake+up+in+a+loop و استاندارد کدنویسی CERT C قاعده CON36-C. Wrap functions that can spuriously wake up in a loop https://wiki.sei.cmu.edu/confluence/display/c/CON36-C.+Wrap+functions+that+can+spuriously+wake+up+in+a+loop است.
bugprone-standalone-empty
زمانی که empty() روی یک بازه (range) استفاده شده و نتیجه آن نادیده گرفته شود، هشدار میدهد. در صورت وجود تابع عضو clear()، استفاده از آن را پیشنهاد میکند.
متد empty() در بسیاری از بازههای رایج، یک مقدار بولی برمیگرداند که نشان میدهد آیا بازه خالی است یا خیر، اما اغلب به اشتباه به عنوان راهی برای پاک کردن محتویات یک بازه تعبیر میشود. برخی از بازهها برای این منظور یک متد clear() ارائه میدهند. این بررسی زمانی هشدار میدهد که یک فراخوانی empty نتیجهای را برگرداند که نادیده گرفته شده است، و پیشنهاد میکند که در صورت در دسترس بودن به عنوان یک تابع عضو در بازه، با فراخوانی clear() جایگزین شود.
به عنوان مثال، کد زیر میتواند برای مشخص کردن خالی بودن یا نبودن یک بازه استفاده شود، اما نتیجه آن نادیده گرفته شده است:
std::vector<int> v; ... v.empty();
یک فراخوانی به clear() به درستی محتویات بازه را پاک میکند:
std::vector<int> v; ... v.clear();
محدودیتها
- •
- اگر empty() در تعریف الگوی کلاس (class template definition) تعریف شده و با نتیجه نادیدهگرفتهشده استفاده شود (به عنوان مثال در پیادهسازی کتابخانه)، هشداری نمیدهد. این موارد خطا را میتوان با خصوصیت [[nodiscard]] شناسایی کرد.
bugprone-std-namespace-modification
در مورد تغییرات در فضاهای نام std یا posix که میتواند منجر به رفتار تعریفنشده (undefined behavior) شود، هشدار میدهد.
فضای نام std (یا posix) مجاز است با تخصیصهای الگو (کلاس یا تابع) که وابسته به یک نوع تعریفشده توسط کاربر هستند (نوعی که در هدرهای استاندارد سیستم تعریف نشده است) گسترش یابد.
این بررسی اعلانات زیر (ارائهشده توسط کاربر) را در فضای نام std یا posix شناسایی میکند:
- هر چیزی که یک تخصیص الگو (template specialization) نباشد.
- تخصیصهای صریح هر الگوی تابع یا الگوی کلاس کتابخانه استاندارد، در صورتی که هیچ نوع تعریفشده توسط کاربری به عنوان آرگومان الگو نداشته باشد.
- تخصیصهای صریح هر تابع عضو از یک الگوی کلاس کتابخانه استاندارد.
- تخصیصهای صریح هر الگوی تابع عضو از یک کلاس یا الگوی کلاس کتابخانه استاندارد.
- تخصیص صریح یا جزئی هر الگوی کلاس عضو از یک کلاس یا الگوی کلاس کتابخانه استاندارد.
مثالها:
namespace std {
int x; // warning: modification of 'std' namespace can result in undefined behavior [bugprone-dont-modify-std-namespace]
}
namespace posix::a { // warning: modification of 'posix' namespace can result in undefined behavior
}
template <>
struct ::std::hash<long> { // warning: modification of 'std' namespace can result in undefined behavior
unsigned long operator()(const long &K) const {
return K;
}
};
struct MyData { long data; };
template <>
struct ::std::hash<MyData> { // no warning: specialization with user-defined type
unsigned long operator()(const MyData &K) const {
return K.data;
}
};
namespace std {
template <>
void swap<bool>(bool &a, bool &b); // warning: modification of 'std' namespace can result in undefined behavior
template <>
bool less<void>::operator()<MyData &&, MyData &&>(MyData &&, MyData &&) const { // warning: modification of 'std' namespace can result in undefined behavior
return true;
}
}
مراجع
این بررسی مطابق با استاندارد کدنویسی CERT C++ تحت قانون DCL58-CPP. Do not modify the standard namespaces است https://www.securecoding.cert.org/confluence/display/cplusplus/DCL58-CPP.+Do+not+modify+the+standard+namespaces.
bugprone-string-constructor
سازندههای (constructors) رشتهای مشکوک و احتمالا نادرست را پیدا میکند.
یک اشتباه رایج، جابجا کردن پارامترهای سازنده رشته پرکننده ('fill') است.
مثالها:
std::string str('x', 50); // should be str(50, 'x')
فراخوانی سازنده رشته لیترال با طولی بزرگتر از خود لیترال مشکوک بوده و نویسههای تصادفی اضافهای را به رشته میافزاید.
مثالها:
std::string("test", 200); // Will include random characters after "test".
std::string("test", 2, 5); // Will include random characters after "st".
std::string_view("test", 200);
ایجاد یک رشته خالی از طریق سازندههای دارای پارامتر، مشکوک در نظر گرفته میشود. برنامهنویس باید در عوض از سازنده خالی استفاده کند.
مثالها:
std::string("test", 0); // Creation of an empty string.
std::string("test", 1, 0);
std::string_view("test", 0);
ارسال یک پارامتر موقعیت نویسه اولِ نامعتبر به سازنده موجب پرتاب استثنای std::out_of_range در زمان اجرا خواهد شد.
مثالها:
std::string("test", -1, 10); // Negative first character position.
std::string("test", 10, 10); // First character position is bigger than string literal character range".
گزینهها
- WarnOnLargeLength
- هنگامی که روی true تنظیم شود، این بررسی برای رشتههایی با طول بیشتر از LargeLengthThreshold هشدار میدهد. مقدار پیشفرض true است.
- LargeLengthThreshold
- یک عدد صحیح که آستانه طول بزرگ را مشخص میکند. مقدار پیشفرض 0x800000 است.
- StringNames
- مقدار
پیشفرض
::std::basic_string;::std::basic_string_view
است.
فهرستی از نامهای کلاسها که با نقطه-ویرگول (semicolon) از هم جدا شدهاند تا این بررسی روی آنها اعمال شود. به طور پیشفرض ::std::basic_string برای std::string و std::wstring اعمال میشود. برای نمونه آن را روی ::std::basic_string;llvm::StringRef;QString تنظیم کنید تا این بررسی روی کلاسهای سفارشی انجام شود.
bugprone-string-integer-assignment
این بررسی، انتساب یک عدد صحیح به std::basic_string<CharT> (std::string، std::wstring و غیره) را شناسایی میکند. منشا مشکل عملگر انتساب زیر در std::basic_string<CharT> است:
basic_string& operator=( CharT ch );
انواع دادههای عددی میتوانند به صورت ضمنی به انواع کاراکتری تبدیل (cast) شوند.
std::string s; int x = 5965; s = 6; s = x;
از توابع تبدیل مناسب یا کاراکترهای لیترال استفاده کنید.
std::string s; int x = 5965; s = '6'; s = std::to_string(x);
جهت فرونشاندن مثبتهای کاذب (false positives)، از تبدیل صریح (explicit cast) استفاده کنید.
std::string s; s = static_cast<char>(6);
bugprone-string-literal-with-embedded-nul
موارد وقوع رشته لیترال دارای نویسه NUL توکار را پیدا کرده و نحوه استفاده از آنها را اعتبارسنجی میکند.
گریزدهی نامعتبر
نویسههای خاص را میتوان در یک رشته لیترال با استفاده از کدگذاری شانزدهشانزدهی (hexadecimal) آنها مانند \x42 گریز داد (escape کرد). یک اشتباه رایج، گریزدهی آنها به شکل \0x42 است که در آن \0 به معنای نویسه NUL خواهد بود.
const char* Example[] = "Invalid character: \0x12 should be \x12"; const char* Bytes[] = "\x03\0x02\0x01\0x00\0xFF\0xFF\0xFF";
Truncated literal
کلاسهای شبهرشته (string-like) میتوانند رشتههای حاوی NUL داخلی را دستکاری کنند، زیرا آنها بایتها و طول رشته را ردیابی و نگهداری میکنند. اما این موضوع در مورد رشتههای char* (خاتمهیافته با NUL) صدق نمیکند.
یک اشتباه رایج، ارسال یک لیترال رشتهای با NUL داخلی به سازندهای از رشته است که انتظار یک رشته خاتمهیافته با NUL را دارد. بایتهای پس از اولین نویسه NUL کوتاه (ترانکیت) میشوند.
std::string str("abc\0def"); // "def" is truncated
str += "\0"; // This statement is doing nothing
if (str == "\0abc") return; // This expression is always true
bugprone-stringview-nullptr
روشهای مختلفی را که ممکن است در آنها یک آرگومان null به سازنده const CharT* از std::basic_string_view ارسال شود، بررسی کرده و در بیشتر موارد آنها را با سازنده پیشفرض جایگزین میکند. برای عملگرهای مقایسهای، لیست مقداردهی اولیه محصور در آکولاد (braced initializer list) کامپایل نمیشود، بنابراین در صورت لزوم به جای آن از یک فراخوانی به .empty() یا لیترال رشته خالی استفاده میشود.
این بررسی مانع از اجرای کدی میشود که رفتار تعریفنشده (undefined behavior) قطعی دارد. سازنده تکآرگومانی const CharT* قبل از دیریفرنس کردن ورودی خود، حالت null بودن را بررسی نمیکند. استاندارد در نظر دارد یک سربارگذاری (overload) صریحاً حذفشده اضافه کند تا برخی از این موارد را مدیریت کند: wg21.link/p2166
برای پوشش موارد اضافی NULL (که به __null گسترش مییابد) و 0، ابتدا بررسی modernize-use-nullptr را اجرا کنید تا فراخوانندهها به nullptr تبدیل شوند.
std::string_view sv = nullptr;
sv = nullptr;
bool is_empty = sv == nullptr;
bool isnt_empty = sv != nullptr;
accepts_sv(nullptr);
accepts_sv({{}}); // A
accepts_sv({nullptr, 0}); // B
تبدیل میشود به...
std::string_view sv = {};
sv = {};
bool is_empty = sv.empty();
bool isnt_empty = !sv.empty();
accepts_sv("");
accepts_sv(""); // A
accepts_sv({nullptr, 0}); // B
نکته:
نکته:
bugprone-suspicious-enum-usage
این بررسیکننده موارد مختلفی را که یک enum احتمالاً به اشتباه (به عنوان بیتماسک) استفاده شده است، تشخیص میدهد.
- 1.
- هنگامی که "ADD" یا "bitwise OR" بین دو enum که از انواع مختلفی هستند استفاده شده و بازه مقادیر این انواع مجزا (disjoint) نباشد.
موارد زیر تنها با استفاده از StrictMode <#cmdoption-arg-StrictMode> مورد بررسی قرار میگیرند. ما enum را به عنوان یک بیتماسک (مشکوک) در نظر میگیریم اگر سه شرط زیر به طور همزمان برقرار باشند:
- حداکثر نیمی از عناصر enum اعدادی غیر از توانهای ۲ باشند (به دلیل شمارشهای کوتاه)
- عدد دیگری به جز ثابت enum که نشاندهنده همه گزینهها باشد (نتیجه عملیات "bitwise OR" تمام عناصر enum)، غیر از توان ۲ باشد
- متغیر/ثابت از نوع enum به عنوان آرگومان یک عملگر + یا عملگر "bitwise OR" استفاده شود
بنابراین هر زمان که عنصری که توان ۲ نیست به عنوان یک عنصر بیتماسک استفاده شود، استفاده نادرست را تشخیص داده و یک هشدار صادر میکنیم.
- 2.
- بررسی سمت راست عملگرهای += و |=.
- 3.
- بررسی تنها سمتی از عملگرهای | و + که مقدار enum دارد، در صورتی که یکی از آنها مقدار enum نباشد.
- 4.
- بررسی هر دو سمت عملگر | یا + در صورتی که مقادیر enum از یک نوع enum یکسان باشند.
مثالها:
enum { A, B, C };
enum { D, E, F = 5 };
enum { G = 10, H = 11, I = 12 };
unsigned flag;
flag =
A |
H; // OK, disjoint value intervals in the enum types ->probably good use.
flag = B | F; // Warning, have common values so they are probably misused.
// Case 2:
enum Bitmask {
A = 0,
B = 1,
C = 2,
D = 4,
E = 8,
F = 16,
G = 31 // OK, real bitmask.
};
enum Almostbitmask {
AA = 0,
BB = 1,
CC = 2,
DD = 4,
EE = 8,
FF = 16,
GG // Problem, forgot to initialize.
};
unsigned flag = 0;
flag |= E; // OK.
flag |=
EE; // Warning at the decl, and note that it was used here as a bitmask.
گزینهها
- StrictMode
- مقدار پیشفرض: 0. در صورتی که مقدار غیر صفر باشد، علاوه بر بررسی استفاده از enumهای متفاوت، استفادههای مشکوک از بیتماسک (bitmask) نیز به صورت مضاعف بررسی خواهد شد.
bugprone-suspicious-include
این بررسی مواردی را شناسایی میکند که در آنها یک include به فایلی ارجاع میدهد که به نظر یک فایل پیادهسازی (implementation file) میآید؛ امری که اغلب منجر به نقضهای ODR میشود که ردگیری آنها دشوار است.
مثالها:
#include "Dinosaur.hpp" // OK, .hpp files tend not to have definitions. #include "Pterodactyl.h" // OK, .h files tend not to have definitions. #include "Velociraptor.cpp" // Warning, filename is suspicious. #include_next <stdio.c> // Warning, filename is suspicious.
گزینهها
- IgnoredRegex
- یک عبارت باقاعده (regular expression) برای نام فایلهایی که باید توسط بررسی نادیده گرفته شوند. مقدار پیشفرض یک رشته خالی است.
bugprone-suspicious-memory-comparison
فراخوانیهای بالقوه نادرست به memcmp() را بر اساس ویژگیهای آرگومانها پیدا میکند. موارد زیر پوشش داده شدهاند:
مورد ۱: نوع با چیدمان غیر استاندارد (Non-standard-layout type)
مقایسه نمایش شیء (object representation) اشیایی که چیدمان غیراستاندارد دارند، ممکن است نمایش مقدار (value representation) را بهدرستی مقایسه نکند.
مورد ۲: انواع بدون نمایش شیء یکتا
اشیایی با مقدار یکسان ممکن است نمایش شیء یکسانی نداشته باشند. این حالت میتواند ناشی از فاصلهگذاری (padding) یا انواع ممیز شناور (floating-point) باشد.
همچنین ببینید: EXP42-C. Do not compare padding data https://wiki.sei.cmu.edu/confluence/display/c/EXP42-C.+Do+not+compare+padding+data و FLP37-C. Do not use object representations to compare floating-point values https://wiki.sei.cmu.edu/confluence/display/c/FLP37-C.+Do+not+use+object+representations+to+compare+floating-point+values
این بررسی همچنین با قوانین استاندارد کدنویسی CERT C++ زیر مرتبط بوده و تا حدی همپوشانی دارد: OOP57-CPP. Prefer special member functions and overloaded operators to C Standard Library functions https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP57-CPP.+Prefer+special+member+functions+and+overloaded+operators+to+C+Standard+Library+functions و EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation https://wiki.sei.cmu.edu/confluence/display/cplusplus/EXP62-CPP.+Do+not+access+the+bits+of+an+object+representation+that+are+not+part+of+the+object%27s+value+representation
بررسی cert-exp42-c به عنوان یک نام مستعار برای این بررسی، به اینجا تغییر مسیر میدهد.
bugprone-suspicious-memset-usage
این بررسی فراخوانیهای memset() را که دارای خطاهای بالقوه در آرگومانهایشان هستند پیدا میکند. با در نظر گرفتن تابع به صورت void* memset(void* destination, int fill_value, size_t byte_count)، موارد زیر پوشش داده میشوند:
مورد ۱: مقدار پرکننده یک کاراکتر ``'0'`` است
پر کردن یک ناحیه از حافظه با کاراکترهای کد اسکی ۴۸ مرسوم نیست، احتمالاً منظور اعداد صحیح صفر بوده است. این بررسی پیشنهاد جایگزینی '0' با 0 را ارائه میدهد. اجرای memset بر روی اشارهگرهای کاراکتری با '0' مجاز است.
مورد ۲: مقدار پرکننده دچار برش (truncated) میشود
تابع memset مقدار fill_value را قبل از استفاده به unsigned char تبدیل میکند. اگر fill_value خارج از محدوده unsigned character باشد، دچار برش شده و حافظه حاوی الگوی مورد نظر نخواهد بود.
مورد ۳: تعداد بایتها صفر است
فراخوانی memset با مقدار لیترال صفر در آرگومان byte_count آن احتمالاً ناخواسته بوده و با fill_value جابهجا شده است. این بررسی پیشنهاد تعویض این دو آرگومان را ارائه میدهد.
نام بررسی متناظر در cpplint.py عبارت است از: runtime/memset.
مثالها:
void foo() {
int i[5] = {1, 2, 3, 4, 5};
int *ip = i;
char c = '1';
char *cp = &c;
int v = 0;
// Case 1
memset(ip, '0', 1); // suspicious
memset(cp, '0', 1); // OK
// Case 2
memset(ip, 0xabcd, 1); // fill value gets truncated
memset(ip, 0x00, 1); // OK
// Case 3
memset(ip, sizeof(int), v); // zero length, potentially swapped
memset(ip, 0, 1); // OK
}
bugprone-suspicious-missing-comma
لیترالهای رشتهای که در کنار یکدیگر قرار میگیرند، در فاز ۶ ترجمه (پس از پیشپردازنده) الحاق (concatenate) میشوند. از این ویژگی برای نمایش لیترال رشتهای طولانی در چند خط استفاده میشود.
برای نمونه، اعلانهای زیر معادل هستند:
const char* A[] = "This is a test"; const char* B[] = "This" " is a " "test";
یک اشتباه رایج برنامهنویسان فراموش کردن کاما بین دو لیترال رشتهای در یک لیست مقداردهی اولیه آرایه (array initializer list) است.
const char* Test[] = {
"line 1",
"line 2" // Missing comma!
"line 3",
"line 4",
"line 5"
};
این آرایه شامل رشته "line 2line3" در آفست ۱ (یعنی Test[1]) خواهد بود. ابزار Clang در زمان کامپایل هیچ هشداری تولید نخواهد کرد.
این بررسی ممکن است در مواردی مانند زیر به نادرستی هشدار دهد:
const char* SupportedFormat[] = {
"Error %s",
"Code " PRIu64, // May warn here.
"Warning %s",
};
گزینهها
- SizeThreshold
- یک عدد صحیح بدون علامت که حداقل اندازه یک لیترال رشتهای را برای در نظر گرفته شدن توسط بررسی تعیین میکند. مقدار پیشفرض 5U است.
- RatioThreshold
- رشتهای که بیشینه نسبت آستانه [0, 1.0] لیترالهای رشتهای مشکوک را برای در نظر گرفته شدن مشخص میکند. مقدار پیشفرض ".2" است.
- MaxConcatenatedTokens
- یک عدد صحیح بدون علامت که بیشینه تعداد توکنهای متصلشده به یکدیگر را تعیین میکند. مقدار پیشفرض 5U است.
bugprone-suspicious-realloc-usage
این بررسی مواردی از استفاده از realloc را مییابد که در آنها مقدار بازگشتی به همان عبارتی انتساب داده میشود که به عنوان آرگومان اول ارسال شده است: p = realloc(p, size); مشکل این ساختار آن است که اگر realloc شکست بخورد، یک اشارهگر تهی (null) برمیگرداند اما حافظه اصلی را آزاد نمیکند. اگر متغیر دیگری به آن اشاره نکند، بلوک حافظه اصلی دیگر برای استفاده یا آزادسازی در دسترس برنامه نخواهد بود. در هر صورت، p = realloc(p, size); نشاندهنده سبک کدنویسی بد است و میتواند با q = realloc(p, size); جایگزین شود.
عبارت اشارهگر (استفادهشده در realloc) میتواند یک متغیر یا یک عضو فیلد از یک ساختار داده باشد، اما نمیتواند شامل فراخوانیهای تابع یا انواع حلنشده (unresolved types) باشد.
در موارد واضحی که اشارهگر استفادهشده در realloc قبل از فراخوانی realloc به متغیر دیگری انتساب داده شده باشد، هیچ هشداری صادر نمیشود. این حالت تنها زمانی رخ میدهد که یک عبارت ساده به شکل q = p یا void *q = p در همان تابعی یافت شود که p = realloc(p, ...) در آن پیدا شده است. این انتساب باید قبل از فراخوانی realloc (اما در غیر این صورت در هر نقطهای) در همان تابع باشد. این نادیدهگیری تنها در صورتی کار میکند که p یک متغیر تکی باشد.
مثالها:
struct A {
void *p;
};
A &getA();
void foo(void *p, A *a, int new_size) {
p = realloc(p, new_size); // warning: 'p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer
a->p = realloc(a->p, new_size); // warning: 'a->p' may be set to null if 'realloc' fails, which may result in a leak of the original buffer
getA().p = realloc(getA().p, new_size); // no warning
}
void foo1(void *p, int new_size) {
void *p1 = p;
p = realloc(p, new_size); // no warning
}
bugprone-suspicious-semicolon
بیشتر موارد وجود نقطهویرگولهای سرگردان را که به طور غیرمنتظرهای معنای کد را تغییر میدهند، پیدا میکند. بهطور دقیقتر، به دنبال دستورهای if، while، for و for-range میگردد که بدنه آنها یک نقطهویرگول تکی است، و سپس زمینه کد (مانند تورفتگی) را تحلیل میکند تا مشخص کند آیا این مورد عمدی بوده است یا خیر.
if (x < y);
{
x++;
}
در اینجا بدنه دستور if تنها از یک نقطهویرگول در انتهای خط اول تشکیل شده است، و x صرفنظر از شرط افزایش خواهد یافت.
while ((line = readLine(file)) != NULL); processLine(line);
در نتیجه این کد، processLine() تنها یک بار فراخوانی خواهد شد، یعنی زمانی که حلقه while با بدنه خالی با وضعیت line == NULL خاتمه یابد. تورفتگی کد قصد برنامهنویس را نشان میدهد.
if (x >= y); x -= y;
با اینکه تورفتگی هیچ تودرتویی را نشان نمیدهد، اما وجود یک دستور if با بدنه خالی اساساً دلیل موجهی ندارد (هرچند ممکن است برای یک حلقه معنیدار باشد). بنابراین این بررسی برای کد بالا هشدار صادر میکند.
برای حل مشکل، نقطهویرگول سرگردان را حذف کنید یا در صورتی که بدنه خالی عمدی است، این مورد را با استفاده از تورفتگی کد نشان دهید یا نقطهویرگول را در یک خط جدید قرار دهید. برای مثال:
while (readWhitespace()); Token t = readNextToken();
در اینجا خط دوم بهگونهای تورفتگی دارد که نشان میدهد قرار است بدنه حلقه while باشد - که در واقع بدنه آن به دلیل وجود نقطهویرگول در انتهای خط اول خالی است.
یا تورفتگی را از خط دوم حذف کنید:
while (readWhitespace()); Token t = readNextToken();
... یا نقطهویرگول را از انتهای خط اول به یک خط جدید منتقل کنید:
while (readWhitespace()) ; Token t = readNextToken();
در این حالت، بررسی فرض خواهد کرد که شما میدانید در حال انجام چه کاری هستید و هشداری صادر نخواهد کرد.
bugprone-suspicious-string-compare
استفادههای مشکوک از توابع مقایسه رشته در زمان اجرا را مییابد. این بررسی در C و C++ معتبر است.
فراخوانیها با عملگر مقایسه ضمنی را بررسی کرده و پیشنهاد میدهد که آن را صراحتاً اضافه کنید.
if (strcmp(...)) // مقایسه ضمنی با صفر if (!strcmp(...)) // هشداری داده نخواهد شد if (strcmp(...) != 0) // هشداری داده نخواهد شد
بررسی میکند که نتایج تابع مقایسه (مانند strcmp) با یک مقدار ثابت معتبر مقایسه شوند. مقدار حاصل عبارت است از:
< 0 زمانی که کوچکتر باشد، > 0 زمانی که بزرگتر باشد، == 0 زمانی که برابر باشد.
یک اشتباه رایج، مقایسه نتیجه با 1 یا -1 است.
if (strcmp(...) == -1) // استفاده نادرست از مقدار بازگرداندهشده.
علاوه بر این، در صورتی که مقدار نتیجه بهطور ضمنی به یک نوع غیرصحیح مشکوک تبدیل (cast) شود، این بررسی هشدار میدهد. این حالت زمانی رخ میدهد که مقدار بازگرداندهشده در یک بافت (context) نادرست استفاده شود.
if (strcmp(...) < 0.) // استفاده نادرست از مقدار بازگرداندهشده.
گزینهها
- WarnOnImplicitComparison
- هنگامی که true باشد، بررسی روی مقایسه ضمنی هشدار میدهد. مقدار پیشفرض true است.
- WarnOnLogicalNotComparison
- هنگامی که true باشد، بررسی روی مقایسه نقیض منطقی (logical not) هشدار میدهد. مقدار پیشفرض false است.
- StringCompareLikeFunctions
- رشتهای که نام توابع اضافی مقایسه رشته را بهصورت جداشده با کاما مشخص میکند. مقدار پیشفرض یک رشته خالی است. این بررسی توابع مقایسه رشته زیر را شناسایی خواهد کرد: __builtin_memcmp، __builtin_strcasecmp، __builtin_strcmp، __builtin_strncasecmp، __builtin_strncmp، _mbscmp، _mbscmp_l، _mbsicmp، _mbsicmp_l، _mbsnbcmp، _mbsnbcmp_l، _mbsnbicmp، _mbsnbicmp_l، _mbsncmp، _mbsncmp_l، _mbsnicmp، _mbsnicmp_l، _memicmp، _memicmp_l، _stricmp، _stricmp_l، _strnicmp، _strnicmp_l، _wcsicmp، _wcsicmp_l، _wcsnicmp، _wcsnicmp_l، lstrcmp، lstrcmpi، memcmp، memicmp، strcasecmp، strcmp، strcmpi، stricmp، strncasecmp، strncmp، strnicmp، wcscasecmp، wcscmp، wcsicmp، wcsncmp، wcsnicmp، wmemcmp.
bugprone-suspicious-stringview-data-usage
استفادههای مشکوک از std::string_view::data() را که ممکن است به دلیل خاتمهنیافتن مناسب یا صحیح رشته با null منجر به خواندن داده خارج از محدوده شود، شناسایی میکند.
زمانی هشدار میدهد که نتیجه data() به یک سازنده یا تابع ارسال شود بدون اینکه نتیجه متناظر توابع عضو size() یا length() نیز ارسال شده باشد. چنین استفادهای میتواند منجر به رفتارهای ناخواسته شود، بهویژه هنگامی که فرض بر این باشد که داده اشارهشده توسط data() به null ختم شده است.
عدم وجود متد c_str() در std::string_view اغلب توسعهدهندگان را به استفاده از data() به عنوان جایگزین سوق میدهد، بهویژه هنگام تعامل با APIهای زبان C که انتظار رشتههای خاتمهیافته با null را دارند. با این حال، از آنجا که data() خاتمه با null را تضمین نمیکند، در صورتی که API برای تفسیر صحیح رشته به خاتمه مناسب با null متکی باشد، این امر میتواند به رفتار ناخواسته منجر شود.
در برنامهنویسی امروزی، این سناریو میتواند هنگام تبدیل ضمنی یک std::string_view به std::string رخ دهد. از آنجا که سازنده طراحیشده در std::string برای اشیاء شبیه string-view از نوع explicit است، تلاش برای ارسال یک std::string_view به تابعی که انتظار std::string را دارد منجر به خطای کامپایل خواهد شد. به عنوان یک راهکار موقت، ممکن است توسعهدهندگان وسوسه شوند تا از متد .data() برای رفع خطای کامپایل استفاده کنند که خطرات بالقوهای به همراه دارد.
برای نمونه:
void printString(const std::string& str) {
std::cout << "String: " << str << std::endl;
}
void something(std::string_view sv) {
printString(sv.data());
}
در این مثال، ارسال مستقیم sv به تابع printString به دلیل صریح (explicit) بودن سازنده std::string منجر به خطای کامپایل میشود. در نتیجه، ممکن است توسعهدهندگان sv.data() را برای حل خطای کامپایل برگزینند، هرچند که خطرات بالقوه ذکرشده را به همراه دارد.
گزینهها
- StringViewTypes
- گزینهای که به کاربران اجازه میدهد انواع سفارشی شبیه string view را برای تحلیل مشخص کنند. این گزینه فهرستی از نامهای انواع یا عبارات باقاعده (regular expressions) منطبق بر این انواع را که با سمیکالن از هم جدا شدهاند میپذیرد. مقدار پیشفرض: ::std::basic_string_view;::llvm::StringRef.
- AllowedCallees
- متدها، توابع یا کلاسهایی را مشخص میکند که نتیجه .data() به آنها ارسال میشود. این گزینه اجازه میدهد چنین فراخوانیهایی را از تحلیل مستثنی کنید. فهرستی از نامها یا عبارات باقاعده منطبق بر این موجودیتها را که با سمیکالن از هم جدا شدهاند میپذیرد. مقدار پیشفرض: رشته خالی است.
bugprone-swapped-arguments
آرگومانهایی که احتمالاً جابهجا شدهاند را با بررسی تبدیلهای ضمنی پیدا میکند. این بررسی انواع آرگومانهای ارسالی به یک تابع را تحلیل کرده و آنها را با انواع مورد انتظار پارامترهای متناظر مقایسه میکند. در صورت عدم تطابق یا وجود تبدیل ضمنی که نشاندهنده جابهجایی احتمالی باشد، هشدار داده میشود.
void printNumbers(int a, float b);
int main() {
// آرگومانهای جابهجاشده: float به عنوان int ارسال شده، int به عنوان float
printNumbers(10.0f, 5);
return 0;
}
طیف گستردهای از تبدیلهای ضمنی را پوشش میدهد، از جمله: - تبدیلهای تعریفشده توسط کاربر (user-defined) - تبدیلها از انواع ممیز شناور به انواع بولی یا صحیح - تبدیلها از انواع صحیح به انواع بولی یا ممیز شناور - تبدیلها از بولی به انواع صحیح یا انواع ممیز شناور - تبدیلها از اشارهگرها (و اشارهگرهای عضو) به بولی
مهم است توجه داشته باشید که برای بیشتر جابهجاییهای آرگومان، انواع باید دقیقاً یکسان باشند. با این حال، استثناهایی برای این قاعده وجود دارد. بهویژه، هنگامی که آرگومان جابهجاشده از نوع صحیح باشد، تطابق دقیق همیشه الزامی نیست. تبدیلهای صریح ضمنی (implicit casts) از سایر انواع صحیح نیز پذیرفته میشوند. به طور مشابه، هنگام کار با آرگومانهای ممیز شناور، تبدیلهای ضمنی میان انواع مختلف ممیز شناور قابل قبول در نظر گرفته میشوند.
برای جلوگیری از سردرگمی، جابهجاییهایی که در آنها هر دو آرگومان جابهجاشده از نوع صحیح، یا هر دو از نوع ممیز شناور باشند، هشدار را فعال نمیکنند. در چنین مواردی، فرض بر این است که توسعهدهنده عمداً از انواع صحیح یا ممیز شناور مختلف استفاده کرده است و هشداری صادر نمیشود. این رویکرد مانع هشدارهای نادرست (false positives) شده و انعطافپذیری لازم را در مدیریت شرایطی که انواع گوناگون صحیح یا ممیز شناور بهصورت هدفمند استفاده شدهاند، فراهم میکند.
bugprone-switch-missing-default-case
اطمینان حاصل میکند که دستورهای switch فاقد حالت default نشانهگذاری شوند؛ این بررسی تنها بر پوشش حالتهای غیر enum تمرکز دارد، جایی که کامپایلر ممکن است هشداری صادر نکند.
دستورهای switch بدون حالت default میتوانند منجر به رفتارهای غیرمنتظره و مدیریت ناقص تمام حالتهای ممکن شوند. هنگامی که یک دستور switch فاقد حالت default باشد، در صورت مواجهه با مقداری که با هیچیک از حالتهای مشخصشده مطابقت ندارد، دستور switch هیچ کاری انجام نخواهد داد و برنامه بدون مدیریت آن مقدار، به اجرای خود ادامه میدهد.
این بررسی به شناسایی دستورهای switch فاقد حالت default کمک میکند و به توسعهدهندگان اجازه میدهد تا از مدیریت صحیح تمام حالتهای ممکن مطمئن شوند. افزودن حالت default امکان مدیریت مناسب مقادیر غیرمنتظره یا تطابقنیافته را فراهم ساخته و خطر خطاهای برنامه و رفتارهای پیشبینینشده را کاهش میدهد.
مثال:
// Example 1:
// warning: switching on non-enum value without default case may not cover all cases
switch (i) {
case 0:
break;
}
// Example 2:
enum E { eE1 };
E e = eE1;
switch (e) { // no-warning
case eE1:
break;
}
// Example 3:
int i = 0;
switch (i) { // no-warning
case 0:
break;
default:
break;
}
نکته:
همچنین ببینید:
bugprone-tagged-union-member-count
هشدارهایی برای tagged unionها صادر میکند، در شرایطی که تعداد برچسبها (tags) با تعداد اعضای دادهای درون union متفاوت باشد.
یک struct یا یک class در صورتی یک tagged union در نظر گرفته میشود که دقیقاً دارای یک عضو دادهای union و دقیقاً یک عضو دادهای enum باشد و هر تعداد عضو دادهای دیگر داشته باشد که نه union هستند و نه enum. علاوه بر این، انواع اعضای union و enum نباید از فایلهای سرایند سیستمی یا فضای نام std آمده باشند.
مثال:
enum Tags {
Tag1,
Tag2,
};
struct TaggedUnion { // warning: tagged union has more data members (3) than tags (2)
enum Tags Kind;
union {
int I;
float F;
char *Str;
} Data;
};
مثال زیر استثنای مربوط به unionها و enumهای برگرفته از فایلهای سرایند سیستمی و فضای نام std را نشان میدهد.
#include <pthread.h>
struct NotTaggedUnion {
enum MyEnum { MyEnumConstant1, MyEnumConstant2 } En;
pthread_mutex_t Mutex;
};
نوع pthread_mutex_t ممکن است در پس یک typedef به صورت یک union تعریف شده باشد، که در این حالت بررسی ممکن است این نوع را به اشتباه به عنوان یک tagged union تعریفشده توسط کاربر تشخیص دهد؛ چرا که دقیقاً دارای یک عضو دادهای enum و دقیقاً یک عضو دادهای union است. برای جلوگیری از موارد مثبت کاذب (false-positive) ناشی از این مسئله، unionها و enumهای موجود در سرایندهای سیستمی و فضای نام std هنگام مشخص کردن بخش union و بخش enum یک tagged union بالقوهٔ تعریفشده توسط کاربر، نادیده گرفته میشوند.
نحوهٔ شمارش ثوابت enum
عامل پیچیدگی اصلی هنگام شمارش تعداد ثوابت enum این است که برخی از آنها ممکن است مقادیر کمکی باشند که عمداً هیچ عضو دادهای متناظری در union ندارند و برای مقاصد دیگری استفاده میشوند. به عنوان مثال، آخرین ثابت enum گاهی اوقات بهطور صریح به آخرین ثابت enum معتبر اعلانشده "اشاره میکند" یا تعداد ثوابت enum اعلانشده را دنبال میکند.
جهت نمایش:
enum TagWithLast {
Tag1 = 0,
Tag2 = 1,
Tag3 = 2,
LastTag = 2
};
enum TagWithCounter {
Tag1, // is 0
Tag2, // is 1
Tag3, // is 2
TagCount, // is 3
};
این بررسی تعداد مقادیر متمایز را در بین ثوابت enum میشمارد و نه خود ثوابت enum را. به این ترتیب، ثوابت enum که در اصل تنها نامهای مستعاری (alias) برای سایر ثوابت enum هستند، در شمارش نهایی لحاظ نمیشوند.
مدیریت شمارش ثوابت enum (مواردی مانند TagCount در مثال کد قبلی) با کاهش یک واحد از تعداد مقادیر enum انجام میشود، در صورتی که نام آخرین ثابت enum با پیشوند یا پسوند مشخصشده در CountingEnumPrefixes یا CountingEnumSuffixes مطابقت داشته باشد و مقدار آن یکی کمتر از تعداد کل مقادیر متمایز در enum باشد.
هنگامی که شمارش نهایی بر اساس این روش اکتشافی (heuristic) تنظیم شود، یک یادداشت عیبیابی صادر میشود که نشان میدهد کدام ثابت enum با معیارها مطابقت داشته است.
این روش اکتشافی میتواند کاملاً غیرفعال شود (EnableCountingEnumHeuristic) یا به گونهای پیکربندی شود که از شیوهنامهٔ نامگذاری شما پیروی کند (CountingEnumPrefixes، CountingEnumSuffixes). رشتههای مشخصشده در CountingEnumPrefixes و CountingEnumSuffixes بدون حساسیت به حروف کوچک و بزرگ مطابقت داده میشوند.
مثالهای شمارش:
// Enum count is 3, because the value 2 is counted only once
enum TagWithLast {
Tag1 = 0,
Tag2 = 1,
Tag3 = 2,
LastTag = 2
};
// Enum count is 3, because TagCount is heuristically excluded
enum TagWithCounter {
Tag1, // is 0
Tag2, // is 1
Tag3, // is 2
TagCount, // is 3
};
گزینهها
این گزینه اکتشاف شمارش مقادیر enum (counting enum heuristic) را فعال یا غیرفعال میکند. این گزینه از پیشوندها و پسوندهای مشخصشده در گزینههای CountingEnumPrefixes و CountingEnumSuffixes برای یافتن مقادیر ثابت شمارشگر enum از طریق تطبیق پیشوند و پسوند استفاده میکند.
این گزینه بهطور پیشفرض فعال است.
هنگامی که EnableCountingEnumHeuristic برابر با false باشد:
enum TagWithCounter {
Tag1,
Tag2,
Tag3,
TagCount,
};
struct TaggedUnion {
TagWithCounter Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
هنگامی که EnableCountingEnumHeuristic برابر با true باشد:
enum TagWithCounter {
Tag1,
Tag2,
Tag3,
TagCount,
};
struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3)
TagWithCounter Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
به CountingEnumSuffixes در زیر مراجعه کنید.
گزینههای CountingEnumPrefixes و CountingEnumSuffixes فهرستهایی از رشتههای جداشده با نقطه-ویرگول (semicolon) هستند که برای جستجوی مقادیر ثابت احتمالی شمارشگر enum استفاده میشوند. این رشتهها بدون حساسیت به حروف بزرگ و کوچک (case insensitively) به ترتیب به عنوان پیشوند و پسوند روی نامهای مقادیر ثابت enum تطبیق داده میشوند. اگر EnableCountingEnumHeuristic برابر با false باشد، این گزینهها هیچ عملی انجام نمیدهند.
مقدار پیشفرض CountingEnumSuffixes برابر با count و برای CountingEnumPrefixes رشته خالی است.
هنگامی که EnableCountingEnumHeuristic برابر با true و CountingEnumSuffixes برابر با count;size باشد:
enum TagWithCounterCount {
Tag1,
Tag2,
Tag3,
TagCount,
};
struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterCount Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
enum TagWithCounterSize {
Tag11,
Tag22,
Tag33,
TagSize,
};
struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterSize Kind;
union {
int A;
long B;
char *Str;
float F;
} Data;
};
هنگامی که EnableCountingEnumHeuristic برابر با true و CountingEnumPrefixes برابر با maxsize;last_ باشد:
enum TagWithCounterLast {
Tag1,
Tag2,
Tag3,
last_tag,
};
struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterLast tag;
union {
int I;
short S;
char *C;
float F;
} Data;
};
enum TagWithCounterMaxSize {
Tag1,
Tag2,
Tag3,
MaxSizeTag,
};
struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterMaxSize tag;
union {
int I;
short S;
char *C;
float F;
} Data;
};
در صورت فعال بودن، این بررسی زمانی که تعداد برچسبها (tags) بیشتر از تعداد اعضای دادهای union باشد نیز هشدار میدهد.
این گزینه بهطور پیشفرض غیرفعال است.
هنگامی که StrictMode <#cmdoption-arg-StrictMode> برابر با false باشد:
struct TaggedUnion {
enum {
Tag1,
Tag2,
Tag3,
} Tags;
union {
int I;
float F;
} Data;
};
هنگامی که StrictMode <#cmdoption-arg-StrictMode> برابر با true باشد:
struct TaggedUnion { // warning: tagged union has fewer data members (2) than tags (3)
enum {
Tag1,
Tag2,
Tag3,
} Tags;
union {
int I;
float F;
} Data;
};
حلقههای do while با شرطی که همیشه به نادرست (false) ارزیابی میشود و دارای دستور continue هستند را شناسایی میکند، زیرا این continue عملاً حلقه را خاتمه میدهد.
void f() {
do {
// some code
continue; // terminating continue
// some other code
} while(false);
bugprone-throw-keyword-missing
درباره احتمال جا افتادن کلمه کلیدی throw هشدار میدهد. اگر یک شیء موقت ایجاد شود، اما نوع شیء از کلاسی مشتق شده باشد (یا همنام باشد) که عبارات 'EXCEPTION'، 'Exception' یا 'exception' در نام آن وجود داشته باشد، میتوانیم فرض کنیم که قصد برنامهنویس پرتاب کردن (throw) آن شیء بوده است.
مثال:
void f(int i) {
if (i < 0) {
// Exception is created but is not thrown.
std::runtime_error("Unexpected argument");
}
}
bugprone-throwing-static-initialization
تمام اعلانهای متغیرهای static یا thread_local را که مقداردهنده اولیه آن شیء ممکن است استثنایی پرتاب کند، پیدا میکند.
References
این بررسی با قاعده ERR58-CPP از استاندارد کدنویسی CERT C++ مطابقت دارد. مدیریت تمام استثناهای پرتابشده پیش از آغاز اجرای ()main https://www.securecoding.cert.org/confluence/display/cplusplus/ERR58-CPP.+Handle+all+exceptions+thrown+before+main%28%29+begins+executing.
bugprone-too-small-loop-variable
حلقههای for را شناسایی میکند که متغیر حلقه آنها از یک نوع «بسیار کوچک» ("too small") است، به این معنی که این نوع نمیتواند تمام مقادیر موجود در محدوده پیمایش را نمایش دهد.
int main() {
long size = 294967296l;
for (short i = 0; i < size; ++i) {}
}
این حلقه for یک حلقه بینهایت است زیرا نوع short نمیتواند تمام مقادیر بازه [0..size] را نمایش دهد.
در یک کاربرد واقعی، size به معنی اندازه یک ظرف (container) است که به ورودی کاربر بستگی دارد.
int doSomething(const std::vector& items) {
for (short i = 0; i < items.size(); ++i) {}
}
این الگوریتم برای تعداد اندکی شیء کار میکند، اما برای ورودیهای بزرگتر کاربر منجر به فریز شدن (قفل کردن) برنامه خواهد شد.
توصیه میشود هشدار کامپایلر -Wtautological-constant-out-of-range-compare را نیز فعال کنید، چرا که این بررسی برای جلوگیری از تداخل با این هشدار، مرزهای ثابت حلقه در زمان کامپایل را بازرسی نمیکند.
Options
- MagnitudeBitsUpperLimit
- حد بالای بیتهای مقدار (magnitude bits) متغیر حلقه. در صورت تعیین، بررسی مواردی را که در آنها نوع متغیر حلقه دارای بیتهای مقدار بیشتری نسبت به حد بالای مشخصشده باشد، فیلتر میکند. مقدار پیشفرض ۱۶ است. برای مثال، اگر کاربر این گزینه را روی 31 (بیت) تنظیم کند، یک unsigned int ۳۲ بیتی توسط بررسی نادیده گرفته میشود، اما یک int ۳۲ بیتی نادیده گرفته نمیشود (یک signed int ۳۲ بیتی دارای ۳۱ بیت مقدار است).
int main() {
long size = 294967296l;
for (unsigned i = 0; i < size; ++i) {} // no warning with MagnitudeBitsUpperLimit = 31 on a system where unsigned is 32-bit
for (int i = 0; i < size; ++i) {} // warning with MagnitudeBitsUpperLimit = 31 on a system where int is 32-bit
}
bugprone-unchecked-optional-access
نکته: این بررسی برای تولید نتایج خود از تحلیل ایستا حساس به جریان داده (flow-sensitive) استفاده میکند. بنابراین، ممکن است نسبت به یک بررسی معمولی clang-tidy به منابع بیشتری (RAM، CPU) نیاز داشته باشد.
این بررسی دسترسیهای ناامن به مقادیر موجود در اشیاء std::optional<T>، absl::optional<T>، base::Optional<T>، folly::Optional<T>، bsl::optional، یا BloombergLP::bdlb::NullableValue را شناسایی میکند. در ادامه ما به همه این نوعها به صورت جمعی با عنوان optional<T> اشاره خواهیم کرد.
دسترسی به مقدار یک optional<T> زمانی رخ میدهد که یکی از توابع عضو value، operator* یا operator-> آن فراخوانی شود. برای همسویی با باورهای نادرست رایج، این بررسی این توابع عضو را معادل در نظر میگیرد، اگرچه تفاوتهای ظریفی بین استثناها و رفتار تعریفنشده (undefined behavior) وجود دارد. برای اطلاعات بیشتر در این باره، بخش یادداشتهای بیشتر را در زیر ببینید.
دسترسی به مقدار یک optional<T> امن تلقی میشود اگر و تنها اگر کد موجود در حوزه محلی (برای مثال، بدنه یک تابع) تضمین کند که optional<T> در تمام مسیرهای ممکنِ اجرا که میتوانند به این دسترسی برسند، دارای مقدار است. این امر باید یا از طریق یک بررسی صریح با استفاده از تابع عضو optional<T>::has_value انجام شود، یا از طریق ساختن optional<T> به روشی که بدون ابهام نشان دهد حاوی مقدار است (مانند استفاده از std::make_optional که همیشه یک std::optional<T> پرشده را برمیگرداند).
در زیر مثالهایی آوردهایم که با الگوهای دسترسی ناامن به optional شروع شده و با الگوهای دسترسی امن ادامه مییابند.
Unsafe access patterns
Access the value without checking if it exists
این بررسی، دسترسیهایی به مقدار را نشانهگذاری میکند که به صورت محلی با بررسی وجود محافظت نشدهاند:
void f(std::optional<int> opt) {
use(*opt); // unsafe: it is unclear whether `opt` has a value.
}
دسترسی به مقدار در شاخه نادرست
این بررسی از وضعیت یک شیء optional در شاخههای مختلف کد آگاه است. برای مثال:
void f(std::optional<int> opt) {
if (opt.has_value()) {
} else {
use(opt.value()); // unsafe: it is clear that `opt` does *not* have a value.
}
}
فرض بر پایدار بودن نتیجه یک تابع
این بررسی آگاه است که نتایج توابع ممکن است پایدار نباشند. یعنی فراخوانیهای متوالی یک تابع یکسان ممکن است مقادیر متفاوتی را بازگردانند. برای مثال:
void f(Foo foo) {
if (foo.take().has_value()) {
use(*foo.take()); // unsafe: it is unclear whether `foo.take()` has a value.
}
}
استثنا: متدهای دستیابی (accessor)
این بررسی متدهای accessor یک کلاس را با استفاده از یک روش ابتکاری (heuristic) برای تشخیص اینکه کدام متدها دستیاب هستند، پایدار فرض میکند. بهطور مشخص، متدهای const بدون پارامتر و APIهای شبهاشارهگر هوشمند (سربارگذاریهای غیر const از * در صورتی که یک سربارگذاری const موازی وجود داشته باشد) به عنوان متدهای دستیاب در نظر گرفته میشوند. توجه داشته باشید که ایمن بودن این کار تضمینشده نیست -- اما در عمل بهطور گسترده (و ایمن) استفاده میشود. فرض بر این است که فراخوانی متدهای غیر const وضعیت شیء را تغییر داده و بر پایداری فراخوانیهای قبلی متدهای دستیاب اثر میگذارد.
اتکا به ناورداهای APIهای نامعمول
این بررسی از ناورداهای (invariants) مربوط به APIهای نامعمول آگاه نیست. برای مثال:
void f(Foo foo) {
if (foo.HasProperty("bar")) {
use(*foo.GetProperty("bar")); // unsafe: it is unclear whether `foo.GetProperty("bar")` has a value.
}
}
بررسی وجود مقدار، سپس ارسال optional به تابعی دیگر
این بررسی بر استدلال محلی تکیه دارد. بررسی و دسترسی به مقدار هر دو باید در یک تابع یکسان رخ دهند. دسترسی ناامن تلقی میشود، حتی اگر فراخوانندهٔ تابعی که دسترسی را انجام میدهد اطمینان حاصل کند که شیء optional حاوی مقدار است. برای مثال:
void g(std::optional<int> opt) {
use(*opt); // unsafe: it is unclear whether `opt` has a value.
}
void f(std::optional<int> opt) {
if (opt.has_value()) {
g(opt);
}
}
الگوهای دسترسی ایمن
بررسی وجود مقدار، سپس دسترسی به آن
این بررسی تمام روشهای سرراست برای بررسی وجود مقدار و دسترسی به مقدارِ درون یک شیء optional را شناسایی میکند. برای مثال:
void f(std::optional<int> opt) {
if (opt.has_value()) {
use(*opt);
}
}
بررسی وجود مقدار، سپس دسترسی به مقدار از طریق یک کپی
معیاری که این بررسی به کار میبرد معنایی (semantic) است، نه نحوی (syntactic). این بررسی تشخیص میدهد چه زمانی یک کپی از شیء optional در حال دسترسی، دارای مقدار است. برای مثال:
void f(std::optional<int> opt1) {
if (opt1.has_value()) {
std::optional<int> opt2 = opt1;
use(*opt2);
}
}
اطمینان از وجود مقدار با استفاده از ماکروهای رایج
این بررسی از ماکروهای رایجی مانند CHECK و DCHECK آگاه است. از این ماکروها میتوان برای اطمینان از اینکه یک شیء optional دارای مقدار است استفاده کرد. برای مثال:
void f(std::optional<int> opt) {
DCHECK(opt.has_value());
use(*opt);
}
اطمینان از وجود مقدار، سپس دسترسی به مقدار در شاخهای مرتبط
این بررسی از شاخههای مرتبط در کد آگاه است و میتواند تشخیص دهد چه زمانی در تمام مسیرهای اجرایی منتهی به دسترسی، وجود مقدار در یک شیء optional تضمین شده است. برای مثال:
void f(std::optional<int> opt) {
bool safe = false;
if (opt.has_value() && SomeOtherCondition()) {
safe = true;
}
// ... more code...
if (safe) {
use(*opt);
}
}
تثبیت نتایج توابع
فرض بر این نیست که نتایج تابع در طول فراخوانیهای پیاپی پایدار باشند، مگر برای متدهای دسترسیدهنده (accessor) از نوع const. برای متدهای دسترسیدهنده پیچیدهتر (غیر const، یا وابسته به چندین پارامتر) بهتر است نتیجه فراخوانی تابع در یک متغیر محلی ذخیره شده و از آن متغیر برای دسترسی به مقدار استفاده شود. به عنوان مثال:
void f(Foo foo) {
if (const auto& foo_opt = foo.take(); foo_opt.has_value()) {
use(*foo_opt);
}
}
عدم اتکا به ناورداهای APIهای غیرمعمول
هنگامی که APIهای نامتعارف تضمین میکنند که یک optional حاوی محتوا است، به آن اعتماد نکنید -- در عوض، صریحاً بررسی کنید که شیء optional دارای مقدار باشد. برای مثال:
void f(Foo foo) {
if (const auto& property = foo.GetProperty("bar")) {
use(*property);
}
}
به جای زوج HasProperty و GetProperty که در بالا دیدیم.
عدم اتکا به بررسیهای انجامشده توسط فراخواننده
اگر میدانید که تمام فراخوانندههای یک تابع بررسی کردهاند که یک آرگومان optional دارای مقدار است، یا تابع را به گونهای تغییر دهید که مقدار را مستقیماً دریافت کند یا optional را دوباره در حوزه محلی تابع فراخوانیشده بررسی کنید. برای مثال:
void g(int val) {
use(val);
}
void f(std::optional<int> opt) {
if (opt.has_value()) {
g(*opt);
}
}
و
struct S {
std::optional<int> opt;
int x;
};
void g(const S &s) {
if (s.opt.has_value() && s.x > 10) {
use(*s.opt);
}
void f(S s) {
if (s.opt.has_value()) {
g(s);
}
}
نکات تکمیلی
نامهای مستعار ایجاد شده از طریق اعلانهای using
این بررسی از نامهای مستعار انواع optional که از طریق اعلانهای using ایجاد میشوند آگاه است. برای مثال:
using OptionalInt = std::optional<int>;
void f(OptionalInt opt) {
use(opt.value()); // unsafe: it is unclear whether `opt` has a value.
}
توابع لامبدا
این بررسی در حال حاضر دسترسیهای ناامن optional را در توابع لامبدا گزارش نمیکند. نسخه بعدی با پیروی از قوانین مشخصشده در بالا، دامنه را به لامبداها گسترش خواهد داد. بهتر است هنگام استفاده از optionalها در لامبداها از همین اصول پیروی کنید.
دسترسی از طریق operator*() در مقایسه با value()
با توجه به اینکه value() رفتار کاملاً تعریفشدهای دارد (یا استثنا پرتاب میکند یا برنامه را خاتمه میدهد)، چرا رفتاری مشابه با operator*() دارد که باعث رفتار تعریفنشده (UB) میشود؟ به عبارت دیگر، چرا دسترسی به یک optional با استفاده از value() ناامن تلقی میشود، در صورتی که قابل اثبات نباشد که حاوی مقدار است؟ به همین ترتیب، چرا CHECK() به همراه operator*() بهتر از value() است، با توجه به اینکه از نظر معنایی معادل یکدیگرند (در پیکربندیهایی که استثناها را غیرفعال کردهاند)؟
پاسخ این است که ما فرض میکنیم اکثر کاربران متوجه تفاوت بین value() و operator*() نیستند. تغییر شیوه به operator*() و نوعی بررسی صریح وجود مقدار یا خاتمه صریح برنامه دارای دو مزیت است:
- خوانایی. بررسی انجامشده و هرگونه عوارض جانبی احتمالی مانند خاموش شدن برنامه، در کد کاملاً واضح هستند. جداسازی دسترسی از بررسیها میتواند در عمل بررسیها را مشهودتر سازد.
- کارایی. یک بررسی واحد میتواند بسیاری یا حتی تمام دسترسیهای موجود در یک محدوده را پوشش دهد. این کار بهترینهای هر دو حوزه را در اختیار کاربر قرار میدهد -- امنیت یک بررسی پویا، اما بدون تحمیل هزینههای مازاد و تکراری.
گزینهها
- IgnoreSmartPointerDereference
- اگر روی true تنظیم شود، این بررسی از optionalهایی که از طریق ارجاعزدایی سربارگذاریشده شبهاشارهگر هوشمند (operator*، operator->) روی کلاسهایی غیر از خود نوع optional به دست میآیند چشمپوشی میکند. این امر به جلوگیری از مثبت کاذب در مواردی که تحلیل نمیتواند نتایج حاصل از چنین فراخوانیهایی را تطبیق دهد، کمک میکند. این گزینه دسترسی از طریق operator[] را پوشش نمیدهد. مقدار پیشفرض false است.
- IgnoreValueCalls
- اگر روی true تنظیم شود، این بررسی فراخوانیهای مربوط به optional::value() را عیبیابی نمیکند. عیبیابی برای operator*() و operator->() همچنان فعال میماند. این گزینه برای پایگاههای کدی مناسب است که به طور هدفمند به value() برای دسترسی محافظتشده و تعریفشده متکی هستند، در حالی که همچنان ارجاعزداییهای مستعد UB عملگرها را نشانهگذاری میکنند. پیشفرض false است.
bugprone-unchecked-string-to-number-conversion
این بررسی فراخوانی توابع تبدیل رشته به عدد را که صحت تبدیل را بررسی نمیکنند، مانند atoi() یا scanf()، نشانهگذاری میکند. این بررسی فراخوانیهای strtol() یا سایر توابع تبدیل مرتبط را که بررسی خطای بهتری انجام میدهند، نشانهگذاری نمیکند.
#include <stdlib.h>
void func(const char *buff) {
int si;
if (buff) {
si = atoi(buff); /* 'atoi' used to convert a string to an integer, but function will
not report conversion errors; consider using 'strtol' instead. */
} else {
/* Handle error */
}
}
مراجع
این بررسی با قاعده استاندارد کدنویسی CERT C مطابقت دارد: ERR34-C. Detect errors when converting a string to a number https://www.securecoding.cert.org/confluence/display/c/ERR34-C.+Detect+errors+when+converting+a+string+to+a+number.
bugprone-undefined-memory-manipulation
فراخوانی توابع دستکاری حافظه memset()، memcpy() و memmove() را روی اشیاء غیر TriviallyCopyable که منجر به رفتار تعریفنشده (undefined behavior) میشوند، پیدا میکند.
استفاده از توابع دستکاری حافظه روی اشیاء غیر TriviallyCopyable میتواند منجر به مجموعهای از مشکلات ظریف و چالشبرانگیز در کدهای C++ شود. فوریترین نگرانی، احتمال بروز رفتار تعریفنشده است که در آن ممکن است وضعیت شیء خراب یا نامعتبر شود. این مسئله میتواند به صورت کرش، خرابی دادهها، یا رفتارهای غیرمنتظره در زمان اجرا ظاهر شود و شناسایی و عیبیابی علت اصلی را دشوار سازد. علاوه بر این، استفاده نادرست از توابع دستکاری حافظه میتواند عملیات ضروری مختص شیء، مانند سازندهها و نابودکنندهها را دور بزند و منجر به نشت منابع یا مقداردهی اولیه نادرست شود.
به عنوان مثال، هنگام استفاده از memcpy برای کپی کردن std::string، دادههای اشارهگر کپی میشوند و این امر میتواند منجر به خطای آزادسازی مضاعف (double free) شود.
#include <cstring>
#include <string>
int main() {
std::string source = "Hello";
std::string destination;
std::memcpy(&destination, &source, sizeof(std::string));
// Undefined behavior may occur here, during std::string destructor call.
return 0;
}
bugprone-undelegated-constructor
ایجاد اشیاء موقت در سازندهها را که شبیه به فراخوانی تابعی از یک سازنده دیگر از همان کلاس به نظر میرسند، پیدا میکند.
کاربر به احتمال زیاد قصد استفاده از یک سازنده واسط (delegating constructor) یا مقداردهی اولیه کلاس پایه را داشته است.
bugprone-unhandled-exception-at-new
فراخوانیهای new فاقد مدیریتکننده استثنا برای std::bad_alloc را پیدا میکند.
فراخوانیهای new ممکن است استثناهایی از نوع std::bad_alloc پرتاب کنند که باید مدیریت شوند. به عنوان روش جایگزین، میتوان از شکل بدون پرتاب استثنای (nonthrowing) عملگر new استفاده کرد. این بررسی صحت مدیریت استثنا در تابعی را که new را فراخوانی میکند، تأیید مینماید.
اگر از نسخه nonthrowing استفاده شود یا اجازه داده شود استثنا به خارج از تابع منتشر (propagate) شود، هیچ هشداری تولید نمیشود.
بررسی میشود که آیا مدیریتکننده استثنا یک استثنا از نوع std::bad_alloc، یا std::exception، و یا همه استثناها (catch-all) را دریافت میکند یا خیر. این بررسی فرض میکند هر operator new تعریفشده توسط کاربر یا noexcept است یا ممکن است استثنایی از نوع std::bad_alloc (یا مشتقشده از آن) پرتاب کند. سایر انواع کلاسهای استثنا در نظر گرفته نمیشوند.
int *f() noexcept {
int *p = new int[1000]; // warning: missing exception handler for allocation failure at 'new'
// ...
return p;
}
int *f1() { // not 'noexcept'
int *p = new int[1000]; // no warning: exception can be handled outside
// of this function
// ...
return p;
}
int *f2() noexcept {
try {
int *p = new int[1000]; // no warning: exception is handled
// ...
return p;
} catch (std::bad_alloc &) {
// ...
}
// ...
}
int *f3() noexcept {
int *p = new (std::nothrow) int[1000]; // no warning: "nothrow" is used
// ...
return p;
}
bugprone-unhandled-self-assignment
شناسه cert-oop54-cpp به عنوان نام مستعار این بررسی به اینجا تغییر مسیر میدهد. برای نام مستعار CERT، گزینه WarnOnlyIfThisHasSuspiciousField بر روی false تنظیم شده است.
عملگرهای انتساب کپی تعریفشده توسط کاربر را که با بررسی صریح انتساب به خود یا با استفاده از روش copy-and-swap یا copy-and-move از کد در برابر انتساب به خود محافظت نمیکنند، پیدا میکند.
بهطور پیشفرض، این بررسی برای جلوگیری از هشدارهای نادرست (false positives)، تنها کلاسهایی را جستجو میکند که دارای فیلد اشارهگر یا آرایه C هستند. در صورت وجود اشارهگر یا آرایه C، اگر عملگر انتساب کپی با دقت نوشته نشده باشد، احتمال دارد انتساب کپی به خود باعث خرابی شیء شود.
همچنین ببینید: OOP54-CPP. Gracefully handle self-copy assignment https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP54-CPP.+Gracefully+handle+self-copy+assignment
یک عملگر انتساب کپی باید از خراب شدن وضعیت شیء توسط انتساب کپی به خود جلوگیری کند. یک سناریوی رایج زمانی است که کلاس دارای یک فیلد اشارهگر باشد و عملگر انتساب کپی ابتدا شیء اشارهشده را آزاد کرده و سپس سعی کند آن را انتساب دهد:
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
T& operator=(const T &rhs) {
delete p;
p = new int(*rhs.p);
return *this;
}
};
برای جلوگیری از این مشکل، دو الگوی رایج C++ وجود دارد. الگوی اول، بررسی انتساب به خود (self-assignment check) است:
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
T& operator=(const T &rhs) {
if(this == &rhs)
return *this;
delete p;
p = new int(*rhs.p);
return *this;
}
};
روش دوم روش copy-and-swap است، هنگامی که یک کپی موقت (با استفاده از سازنده کپی) ایجاد میکنیم و سپس این شیء موقت را با this تعویض (swap) مینماییم:
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
void swap(T &rhs) {
using std::swap;
swap(p, rhs.p);
}
T& operator=(const T &rhs) {
T(rhs).swap(*this);
return *this;
}
};
الگوی سومی نیز وجود دارد که کمتر رایج است. میتوان آن را روش copy-and-move نامید، زمانی که یک کپی موقت ایجاد میکنیم (با استفاده از سازنده کپی) و سپس این شیء موقت را به this انتقال (move) میدهیم (به یک عملگر انتساب انتقال نیاز دارد):
class T {
int* p;
public:
T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
~T() { delete p; }
// ...
T& operator=(const T &rhs) {
T t = rhs;
*this = std::move(t);
return *this;
}
T& operator=(T &&rhs) {
p = rhs.p;
rhs.p = nullptr;
return *this;
}
};
Options
- WarnOnlyIfThisHasSuspiciousField
- هنگامی که true باشد، این بررسی تنها در صورتی هشدار میدهد که کلاس نگهدارندهٔ عملگر انتساب کپی (copy assignment operator) دارای هرگونه فیلد مشکوک (اشارهگر، آرایهٔ C و اشارهگر هوشمند ++C) باشد. این گزینه به طور پیشفرض روی true تنظیم شده است.
bugprone-unintended-char-ostream-output
خروجیهای ناخواسته کاراکتر از نوعهای unsigned char و signed char به یک ostream را مییابد.
معمولاً هنگامی که unsigned char (uint8_t) یا signed char (int8_t) استفاده میشود، بیشتر بیانگر یک عدد است تا یک کاراکتر. با این حال، هنگامی که مستقیماً به operator<< در std::ostream فرستاده میشود، نتیجه به جای مقدار عددی، خروجی کاراکتری خواهد بود. این مسئله اغلب با قصد برنامهنویس برای چاپ مقادیر عددی صحیح مغایرت دارد.
uint8_t v = 65; std::cout << v; // output 'A' instead of '65'
این بررسی پیشنهاد تبدیل نوع (cast) مقدار به نوعی مناسب را برای مشخص کردن هدف ارائه میدهد؛ به صورت پیشفرض، برای unsigned char به unsigned int و برای signed char به int تبدیل میکند.
std::cout << static_cast<unsigned int>(v); // when v is unsigned char std::cout << static_cast<int>(v); // when v is signed char
برای جلوگیری از عبارات طولانی تبدیل نوع، افزودن پیشوند + به متغیر نیز میتواند هشدارها را سرکوب کند؛ زیرا عبارت یکانی مقدار را به یک int ارتقا میدهد (type promotion).
std::cout << +v;
یا میتوان آن را به صورت صریح به char تبدیل نوع کرد تا مشخص شود خروجی حتماً باید یک کاراکتر باشد.
std::cout << static_cast<char>(v);
Options
- AllowedTypes
- فهرستی از نامهای انواع که با نقطه-ویرگول (semicolon) از هم جدا شدهاند و همانند نوع char با آنها رفتار خواهد شد: این بررسی متغیرهای اعلانشده با این انواع یا عبارات تبدیل صریح به این انواع را گزارش نخواهد کرد. توجه داشته باشید که این گزینه میان نامهای مستعار نوع (type aliases) و نوع اصلی تمایز قائل میشود، بنابراین مشخص کردن مواردی مانند unsigned char در اینجا، گزارشهای مربوط به uint8_t را حتی اگر به عنوان یک نام مستعار typedef برای unsigned char تعریف شده باشد، سرکوب نخواهد کرد. پیشفرض unsigned char;signed char است.
- CastTypeName
- هنگامی که CastTypeName مشخص شده باشد، رفع خودکار (fix-it) از CastTypeName به عنوان نوع مقصد تبدیل استفاده میکند. در غیر این صورت، fix-it نوع را به طور خودکار استنتاج خواهد کرد.
bugprone-unique-ptr-array-mismatch
مقداردهیهای اولیهٔ اشارهگرهای یکتای ++C (از نوع std::unique_ptr) از انواع غیرآرایهای را که با یک آرایه مقداردهی شدهاند، پیدا میکند.
اگر یک اشارهگر std::unique_ptr<T> با یک عبارت new از نوع new T[] مقداردهی اولیه شود، حافظه به درستی آزاد نمیشود. در این حالت برای آزادسازی حافظهٔ هدف از یک delete معمولی استفاده میشود؛ در حالی که به فراخوانی delete[] نیاز است. ساختار std::unique_ptr<T[]> از عملگر delete صحیح استفاده میکند. اگر از یک unique_ptr با deleter تعریفشده توسط کاربر استفاده شود، این بررسی هشداری صادر نمیکند.
اگر در اعلان یک متغیر منفرد استفاده شده باشد (یک متغیر در یک دستور)، این بررسی جایگزینی unique_ptr<T> با unique_ptr<T[]> را پیشنهاد میدهد.
مثال:
std::unique_ptr<Foo> x(new Foo[10]); // -> std::unique_ptr<Foo[]> x(new Foo[10]);
// ^ warning: unique pointer to non-array is initialized with array
std::unique_ptr<Foo> x1(new Foo), x2(new Foo[10]); // no replacement
// ^ warning: unique pointer to non-array is initialized with array
D d;
std::unique_ptr<Foo, D> x3(new Foo[10], d); // no warning (custom deleter used)
struct S {
std::unique_ptr<Foo> x(new Foo[10]); // no replacement in this case
// ^ warning: unique pointer to non-array is initialized with array
};
این بررسی تا حدی قاعدهٔ MEM51-CPP مربوط به CERT C++ Coding Standard را پوشش میدهد: Properly deallocate dynamically allocated resources https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM51-CPP.+Properly+deallocate+dynamically+allocated+resources با این حال، تنها حالت std::unique_ptr توسط این بررسی شناسایی میشود.
bugprone-unsafe-functions
توابعی را بررسی میکند که جایگزینهای ایمنتر و امنتری برای آنها در دسترس است، یا به دلیل ایرادات طراحی منسوخ (deprecated) در نظر گرفته میشوند. این بررسی به شدت به توابع موجود در Annex K. به عنوان «Bounds-checking interfaces» در استاندارد C11 متکی است.
- این بررسی قواعد زیر را از CERT C Coding Standard پیادهسازی میکند:
- توصیهٔ MSC24-C: عدم استفاده از توابع منسوخ یا رو به زوال https://wiki.sei.cmu.edu/confluence/display/c/MSC24-C.+Do+not+use+deprecated+or+obsolescent+functions.
- قاعدهٔ MSC33-C: عدم ارسال دادههای نامعتبر به تابع asctime() https://wiki.sei.cmu.edu/confluence/display/c/MSC33-C.+Do+not+pass+invalid+data+to+the+asctime%28%29+function.
گزینههای cert-msc24-c و cert-msc33-c به عنوان نامهای مستعار این بررسی به اینجا هدایت میشوند.
Unsafe functions
توابع زیر در صورت فعال بودن ReportDefaultFunctions گزارش میشوند.
در صورت در دسترس بودن Annex K.، یک جایگزین از Annex K. برای توابع زیر پیشنهاد میشود:
asctime, asctime_r, bsearch, ctime, fopen, fprintf, freopen, fscanf, fwprintf, fwscanf, getenv, gets, gmtime, localtime, mbsrtowcs, mbstowcs, memcpy, memmove, memset, printf, qsort, scanf, snprintf, sprintf, sscanf, strcat, strcpy, strerror, strlen, strncat, strncpy, strtok, swprintf, swscanf, vfprintf, vfscanf, vfwprintf, vfwscanf, vprintf, vscanf, vsnprintf, vsprintf, vsscanf, vswprintf, vswscanf, vwprintf, vwscanf, wcrtomb, wcscat, wcscpy, wcslen, wcsncat, wcsncpy, wcsrtombs, wcstok, wcstombs, wctomb, wmemcpy, wmemmove, wprintf, wscanf.
اگر Annex K. در دسترس نباشد، جایگزینها تنها برای توابع زیر از فهرست قبلی پیشنهاد میشوند:
- asctime, asctime_r، جایگزین پیشنهادی: strftime
- gets، جایگزین پیشنهادی: fgets
توابع زیر بدون در نظر گرفتن در دسترس بودن Annex K، همواره بررسی میشوند:
- rewind، جایگزین پیشنهادی: fseek
- setbuf، جایگزین پیشنهادی: setvbuf
اگر ReportMoreUnsafeFunctions فعال باشد، توابع زیر نیز بررسی میشوند:
- bcmp، جایگزین پیشنهادی: memcmp
- bcopy، جایگزین پیشنهادی: memcpy_s در صورت در دسترس بودن Annex K، یا memcpy
- bzero، جایگزین پیشنهادی: memset_s در صورت در دسترس بودن Annex K، یا memset
- getpw، جایگزین پیشنهادی: getpwuid
- vfork، جایگزین پیشنهادی: posix_spawn
اگرچه در قواعد مرتبط CERT ذکر شدهاند، توابع زیر توسط این بررسی نادیده گرفته میشوند:
atof, atoi, atol, atoll, tmpfile.
در دسترس بودن Annex K بر اساس ماکروهای زیر تعیین میشود:
- __STDC_LIB_EXT1__: ماکروی ویژگی (feature macro)، که نشاندهندهٔ حضور Annex K. "Bounds-checking interfaces" در پیادهسازی کتابخانه است
- __STDC_WANT_LIB_EXT1__: ماکروی تعریفشده توسط کاربر، که نشان میدهد کاربر درخواست تعریف توابع موجود در Annex K. را دارد.
هر دو ماکرو باید تعریف شده باشند تا توابع جایگزین از Annex K. پیشنهاد شوند. __STDC_LIB_EXT1__ توسط پیادهسازی کتابخانه تعریف میشود، و __STDC_WANT_LIB_EXT1__ باید پیش از گنجاندن هرگونه فایل سرایند سیستمی توسط کاربر برابر با 1 تعریف شود.
توابع سفارشی
گزینهٔ CustomFunctions به کاربر امکان میدهد تا توابع سفارشی را برای بررسی شدن تعریف کند. قالب آن به صورت زیر و بدون خطوط جدید (newline) است:
bugprone-unsafe-functions.CustomFunctions=" functionRegex1[, replacement1[, reason1]]; functionRegex2[, replacement2[, reason2]]; ... "
توابع با استفاده از عبارتهای منظم تعمیمیافتهٔ POSIX تطبیق داده میشوند. (نکته: عبارتهای منظم از تطبیقهای منفی (?!) پشتیبانی نمیکنند.)
شناسهٔ reason اختیاری است و برای ارائهٔ اطلاعات بیشتر در مورد دلیلِ پشتِ این جایگزینی استفاده میشود. دلیل پیشفرض عبارت است از is marked as unsafe.
اگر replacement خالی باشد، متن پیشفرض it should not be used به جای پیشنهادی برای جایگزینی نمایش داده خواهد شد.
اگر reason با نویسهٔ > شروع شود، دلیل کاملاً سفارشی میشود. پسوند پیشفرض حتی در صورت وجود replacement نیز غیرفعال شده و برای ایجاد امکان کنترل بهتر بر روی پیشنهادات، تنها پیام دلیل پس از تابع تطبیقیافته نمایش داده میشود. (نویسهٔ > ابتدایی و فاصلههای خالی بلافاصله پس از آن از پیام حذف میشوند.)
به عنوان مثال، پیکربندی زیر تنها تابع original در فضاینام پیشفرض را تطبیق میدهد. یک پیام عیبیابی مشابه را میتوان با استفاده از یک دلیل کاملاً سفارشی نیز چاپ کرد.
// bugprone-unsafe-functions.CustomFunctions: // ^original$, replacement, is deprecated; // Using the fully custom message syntax: // ^suspicious$,,> should be avoided if possible. original(); // warning: function 'original' is deprecated; 'replacement' should be used instead. suspicious(); // warning: function 'suspicious' should be avoided if possible. ::std::original(); // no-warning original_function(); // no-warning
اگر عبارت منظم شامل نویسهٔ : باشد، با نام دارای صلاحیت (یعنی std::original) تطبیق داده میشود؛ در غیر این صورت عبارت منظم با نام بدون صلاحیت (original) تطبیق داده خواهد شد. اگر عبارت منظم با :: (یا ^::) آغاز شود، با نام دارای صلاحیت کامل (::std::original) تطبیق داده میشود.
یکی از موارد کاربرد پیامهای کاملاً سفارشی، پیشنهاد دادن گزینههای کامپایلر و فلگهای هشدار است:
// bugprone-unsafe-functions.CustomFunctions: // ^memcpy$,,>is recommended to have compiler hardening using '_FORTIFY_SOURCE'; // ^printf$,,>is recommended to have the '-Werror=format-security' compiler warning flag; memcpy(dest, src, 999'999); // warning: function 'memcpy' is recommended to have compiler hardening using '_FORTIFY_SOURCE' printf(raw_str); // warning: function 'printf' is recommended to have the '-Werror=format-security' compiler warning flag
نکته:
به عنوان مثال، تابع عضو open در کلاس std::ifstream دارای نامِ با صلاحیت کاملِ ::std::basic_ifstream<char>::open است.
این مثال همچنین میتواند با عبارت منظم ::std::basic_ifstream<[^>]*>::open تطبیق یابد که تمام پارامترهای احتمالی الگو را تطبیق میدهد، اما کلاسهای الگوی تودرتو را تطبیق نمیدهد.
گزینهها
- ReportMoreUnsafeFunctions
- در صورت تنظیم روی true، توابع اضافی از APIهای پرکاربرد (مانند POSIX) به فهرست توابع گزارششده افزوده میشوند. برای مشاهدهٔ فهرست کامل مواردی که این گزینه فعال میکند، مستندات اصلی این بررسی را مشاهده کنید. پیشفرض true است.
- ReportDefaultFunctions
- در صورت تنظیم روی true، این بررسی مجموعهٔ پیشفرض توابع را گزارش میکند. اگر تنها میخواهید توابع سفارشی تطبیقیافته از طریق Custom functions را ببینید، تغییر دادن این تنظیم به false را مد نظر قرار دهید. پیشفرض true است.
- CustomFunctions
- فهرستی جداشده با نقطه ویرگول (;) از توابع سفارشی برای تطبیق داده شدن. هر تابعِ تطبیقیافته شامل یک عبارت منظم، نام اختیاری تابع جایگزین و یک دلیل اختیاری است که با کاما جدا میشوند. برای اطلاعات بیشتر، بخش توابع سفارشی را ببینید.
مثالها
#ifndef __STDC_LIB_EXT1__
#error "Annex K is not supported by the current standard library implementation."
#endif
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h> // Defines functions from Annex K.
#include <stdio.h>
enum { BUFSIZE = 32 };
void Unsafe(const char *Msg) {
static const char Prefix[] = "Error: ";
static const char Suffix[] = "\n";
char Buf[BUFSIZE] = {0};
strcpy(Buf, Prefix); // warning: function 'strcpy' is not bounds-checking; 'strcpy_s' should be used instead.
strcat(Buf, Msg); // warning: function 'strcat' is not bounds-checking; 'strcat_s' should be used instead.
strcat(Buf, Suffix); // warning: function 'strcat' is not bounds-checking; 'strcat_s' should be used instead.
if (fputs(buf, stderr) < 0) {
// error handling
return;
}
}
void UsingSafeFunctions(const char *Msg) {
static const char Prefix[] = "Error: ";
static const char Suffix[] = "\n";
char Buf[BUFSIZE] = {0};
if (strcpy_s(Buf, BUFSIZE, Prefix) != 0) {
// error handling
return;
}
if (strcat_s(Buf, BUFSIZE, Msg) != 0) {
// error handling
return;
}
if (strcat_s(Buf, BUFSIZE, Suffix) != 0) {
// error handling
return;
}
if (fputs(Buf, stderr) < 0) {
// error handling
return;
}
}
bugprone-unused-local-non-trivial-variable
هنگامی که یک متغیر غیربدیهی (non-trivial) محلی درون یک تابع بدون استفاده بماند، هشدار میدهد. انواع متغیرهای زیر از این بررسی مستثنی هستند:
- انواع بدیهی (trivial) و با قابلیت کپی بدیهی (trivially copyable)
- ارجاعها (references) و اشارهگرها (pointers)
- متغیرهای استثنا در عبارتهای catch
- متغیرهای static یا thread local
- اتصالهای ساختیافته (structured bindings)
- متغیرهای دارای صفت [[maybe_unused]]
- متغیرهای مستقل از نام (name-independent)
این بررسی میتواند پیکربندی شود تا با تنظیم IncludeTypes بر روی .* برای تمام متغیرهای غیربدیهی هشدار دهد، و انواع خاصی را با استفاده از ExcludeTypes مستثنی کند.
در این مثال، my_lock هشداری مبنی بر استفاده نشدن ایجاد میکند.
std::mutex my_lock; // my_lock local variable is never used
در مثال بعدی، future2 هشداری مبنی بر استفاده نشدن ایجاد میکند.
std::future<MyObject> future1; std::future<MyObject> future2; // ... MyObject foo = future1.get(); // future2 is not used.
گزینهها
- IncludeTypes
- فهرستی جداشده با نقطه-ویرگول از عبارات منظم که با انواع متغیرهای مورد بررسی مطابقت دارند. بهطور پیشفرض انواع زیر بررسی میشوند:
- ::std::.*mutex
- ::std::future
- ::std::basic_string
- ::std::basic_regex
- ::std::basic_istringstream
- ::std::basic_stringstream
- ::std::bitset
- ::std::filesystem::path
- ExcludeTypes
- فهرستی جداشده با نقطه-ویرگول از عبارات منظم برای تطبیق انواعی که از موارد تطبیقیافته در IncludeTypes مستثنی میشوند. بهطور پیشفرض یک فهرست خالی است.
bugprone-unused-raii
اشیاء موقتی را که شبیه به اشیاء RAII هستند پیدا میکند.
مثال کلاسیک برای این مورد، یک قفل با محدوده متغیر (scoped lock) است.
{
scoped_lock(&global_mutex);
critical_section();
}
مخرب (destructor) مربوط به scoped_lock پیش از ورود به critical_section فراخوانی میشود و آن را بدون محافظت باقی میگذارد.
ما از تعدادی روش ابتکاری (heuristics) برای کاهش تعداد موارد مثبت کاذب در این بررسی استفاده میکنیم:
- نادیده گرفتن کدهایی که از ماکروها گسترش یافتهاند. فریمورکهای تست استفاده زیادی از این مورد میکنند.
- نادیده گرفتن انواعی که مخربهای بدیهی (trivial destructors) دارند. احتمال بسیار کمی وجود دارد که آنها اشیاء RAII باشند و تفاوتی در زمان حذف شدن آنها وجود ندارد.
- نادیده گرفتن اشیاء در انتهای یک عبارت مرکب (compound statement) (رفتار برنامه را تغییر نمیدهد).
- نادیده گرفتن اشیاء بازگردانده شده از یک فراخوانی.
bugprone-unused-return-value
در مورد مقادیر بازگشتی استفادهنشده توابع هشدار میدهد. توابع مورد بررسی را میتوان پیکربندی کرد.
سربارگذاری عملگرها (operator overloading) با معنای انتساب نادیده گرفته میشوند.
گزینهها
- CheckedFunctions
- فهرستی جداشده با نقطه-ویرگول از توابع برای بررسی. این پارامتر از عبارت منظم (regexp) پشتیبانی میکند. در صورتی که نام و حوزه (scope) تابع با هر آرگومانی مطابقت داشته باشد، بررسی میشود. بهطور پیشفرض توابع زیر بررسی میشوند: ^::std::async$, ^::std::launder$, ^::std::remove$, ^::std::remove_if$, ^::std::unique$, ^::std::unique_ptr::release$, ^::std::basic_string::empty$, ^::std::vector::empty$, ^::std::back_inserter$, ^::std::distance$, ^::std::find$, ^::std::find_if$, ^::std::inserter$, ^::std::lower_bound$, ^::std::make_pair$, ^::std::map::count$, ^::std::map::find$, ^::std::map::lower_bound$, ^::std::multimap::equal_range$, ^::std::multimap::upper_bound$, ^::std::set::count$, ^::std::set::find$, ^::std::setfill$, ^::std::setprecision$, ^::std::setw$, ^::std::upper_bound$, ^::std::vector::at$, ^::bsearch$, ^::ferror$, ^::feof$, ^::isalnum$, ^::isalpha$, ^::isblank$, ^::iscntrl$, ^::isdigit$, ^::isgraph$, ^::islower$, ^::isprint$, ^::ispunct$, ^::isspace$, ^::isupper$, ^::iswalnum$, ^::iswprint$, ^::iswspace$, ^::isxdigit$, ^::memchr$, ^::memcmp$, ^::strcmp$, ^::strcoll$, ^::strncmp$, ^::strpbrk$, ^::strrchr$, ^::strspn$, ^::strstr$, ^::wcscmp$, ^::access$, ^::bind$, ^::connect$, ^::difftime$, ^::dlsym$, ^::fnmatch$, ^::getaddrinfo$, ^::getopt$, ^::htonl$, ^::htons$, ^::iconv_open$, ^::inet_addr$, isascii$, isatty$, ^::mmap$, ^::newlocale$, ^::openat$, ^::pathconf$, ^::pthread_equal$, ^::pthread_getspecific$, ^::pthread_mutex_trylock$, ^::readdir$, ^::readlink$, ^::recvmsg$, ^::regexec$, ^::scandir$, ^::semget$, ^::setjmp$, ^::shm_open$, ^::shmget$, ^::sigismember$, ^::strcasecmp$, ^::strsignal$, ^::ttyname$
- std::async(). استفاده نکردن از مقدار بازگشتی، فراخوانی را همگام (synchronous) میکند.
- std::launder(). استفاده نکردن از مقدار بازگشتی معمولاً به این معنی است که رابط تابع توسط برنامهنویس اشتباه درک شده است. تنها اشارهگر بازگردانده شده «شستشو داده شده» (laundered) است، نه آرگومان.
- std::remove()، std::remove_if() و std::unique(). تکرارکننده (iterator) بازگردانده شده مرز بین عناصر باقیمانده و عناصر برای حذف را مشخص میکند. استفاده نکردن از مقدار بازگشتی به این معنی است که اطلاعات مربوط به اینکه کدام عناصر باید حذف شوند از دست رفته است.
- std::unique_ptr::release(). عدم استفاده از مقدار بازگشتی میتواند منجر به نشت منابع (resource leaks) شود اگر همان اشارهگر در جای دیگری ذخیره نشده باشد. اغلب، نادیده گرفتن مقدار بازگشتی release() نشان میدهد که برنامهنویس تابع را با reset() اشتباه گرفته است.
- std::basic_string::empty() و std::vector::empty(). عدم استفاده از مقدار بازگشتی معمولاً بیانگر این است که برنامهنویس تابع را با clear() اشتباه گرفته است.
- CheckedReturnTypes
- فهرستی جداشده با نقطه-ویرگول از انواع مقادیر بازگشتی تابع جهت بررسی. بهطور پیشفرض انواع بازگشتی توابع زیر بررسی میشوند: ^::std::error_code$, ^::std::error_condition$, ^::std::errc$, ^::std::expected$, ^::boost::system::error_code$
- AllowCastToVoid
- تعیین میکند که آیا تبدیل نوع (cast) مقادیر بازگشتی به void مجاز است یا خیر. پیشفرض: false.
بررسی cert-err33-c <> نام مستعاری (alias) برای این بررسی است که مجموعه ثابت و بزرگی از توابع کتابخانه استاندارد را بررسی میکند.
bugprone-use-after-move
هشدار میدهد اگر از یک شیء پس از انتقال (move) داده شدن استفاده شود، برای نمونه:
std::string str = "Hello, world!\n"; std::vector<std::string> messages; messages.emplace_back(std::move(str)); std::cout << str;
خط آخر هشداری صادر میکند مبنی بر اینکه str پس از انتقال یافتن، استفاده شده است.
این بررسی در صورتی که شیء پس از انتقال و پیش از استفاده دوباره مقداردهی اولیه (reinitialize) شود، هشداری صادر نمیکند. برای نمونه، برای این کد هیچ هشداری ایجاد نخواهد شد:
messages.emplace_back(std::move(str)); str = "Greetings, stranger!\n"; std::cout << str;
بخشهای زیر دقیقتر توضیح میدهند که این بررسی دقیقاً چه مواردی را انتقال، استفاده و مقداردهی اولیه در نظر میگیرد.
این بررسی جریان کنترل (control flow) را در نظر میگیرد. هشدار تنها در صورتی صادر میشود که استفاده از شیء از نقطه انتقال قابل دستیابی باشد. این بدین معناست که کد زیر هشداری تولید نمیکند:
if (condition) {
messages.emplace_back(std::move(str));
} else {
std::cout << str;
}
از سوی دیگر، کد زیر یک هشدار تولید میکند:
for (int i = 0; i < 10; ++i) {
std::cout << str;
messages.emplace_back(std::move(str));
}
(استفاده پس از انتقال در تکرار دوم حلقه رخ میدهد.)
در برخی موارد، این بررسی ممکن است قادر به تشخیص ناسازگار بودن (mutually exclusive) دو شاخه نباشد. برای نمونه (با فرض اینکه i یک int است):
if (i == 1) {
messages.emplace_back(std::move(str));
}
if (i == 2) {
std::cout << str;
}
در این حالت، بررسی به اشتباه هشداری تولید خواهد کرد، هرچند که اجرای همزمان هر دو دستورِ انتقال و استفاده ناممکن است. به بیان رسمیتر، تحلیل این ابزار به جریان حساس است اما به مسیر حساس نیست (flow-sensitive but not path-sensitive) https://en.wikipedia.org/wiki/Data-flow_analysis#Sensitivities.
بیصدا کردن هشدارهای نادرست
یک هشدار نادرست را میتوان با مقداردهی اولیه دوباره شیء پس از انتقال بیصدا کرد:
if (i == 1) {
messages.emplace_back(std::move(str));
str = "";
}
if (i == 2) {
std::cout << str;
}
اگر میخواهید از بار اضافی ناشی از مقداردهی اولیه مجدد شیء جلوگیری کنید، میتوانید یک تابع صوری (dummy) بسازید که باعث شود این بررسی فرض کند شیء دوباره مقداردهی اولیه شده است:
template <class T>
void IS_INITIALIZED(T&) {}
میتوانید از آن به صورت زیر استفاده کنید:
if (i == 1) {
messages.emplace_back(std::move(str));
}
if (i == 2) {
IS_INITIALIZED(str);
std::cout << str;
}
در این حالت، بررسی هشداری صادر نخواهد کرد زیرا ارسال شیء به یک تابع به عنوان یک اشارهگر یا ارجاع غیر-ثابت (non-const) به عنوان مقداردهی اولیه دوباره به حساب میآید (بخش مقداردهی اولیه دوباره را در زیر ببینید).
انتقالها، استفادهها و مقداردهیهای اولیه مرتبنشده (Unsequenced)
در بسیاری از موارد، C++ هیچ تضمینی در مورد ترتیبی که در آن زیرعبارتهای یک گزاره ارزیابی میشوند ارائه نمیدهد. این بدان معناست که در کدهایی مانند نمونه زیر، تضمینی وجود ندارد که استفاده قبل از انتقال رخ خواهد داد یا بعد از آن:
void f(int i, std::vector<int> v);
std::vector<int> v = { 1, 2, 3 };
f(v[1], std::move(v));
در چنین شرایطی، بررسی متذکر خواهد شد که استفاده و انتقال از نوع مرتبنشده (unsequenced) هستند.
این بررسی همچنین قواعد ترتیببندی را هنگامی که مقداردهی اولیه دوباره در همان عبارت انتقال یا استفاده رخ میدهد، در نظر میگیرد. مقداردهی اولیه دوباره تنها در صورتی برای یک متغیر معتبر شناخته میشود که تضمین شده باشد پس از انتقال و پیش از استفاده ارزیابی میشود.
انتقال (Move)
این بررسی در حال حاضر تنها فراخوانیهای std::move روی متغیرهای محلی یا پارامترهای تابع را در نظر میگیرد. این بررسی انتقال متغیرهای عضو یا متغیرهای سراسری (global) را بررسی نمیکند.
هرگونه فراخوانی std::move روی یک متغیر به عنوان عامل ایجاد انتقال آن متغیر در نظر گرفته میشود، حتی اگر نتیجهٔ std::move به یک پارامتر ارجاع rvalue ارسال نشود.
این بدان معناست که بررسی حتی روی نوعی که دارای سازندهٔ انتقال (move constructor) یا عملگر انتساب انتقال (move assignment operator) نیست نیز یک use-after-move را گزارش میکند. این رفتار عمدی است. توسعهدهندگان ممکن است با این انتظار که در آینده سمنتیک انتقال (move semantics) به این نوع اضافه خواهد شد، از std::move استفاده کنند. اگر چنین فراخوانی std::move پتانسیل ایجاد یک use-after-move را داشته باشد، میخواهیم دربارهٔ آن هشدار دهیم حتی اگر آن نوع هنوز سمنتیک انتقال را پیادهسازی نکرده باشد.
علاوه بر این، اگر نتیجهٔ std::move به یک پارامتر ارجاع rvalue پاس داده شود، این حالت همواره به عنوان انتقال در نظر گرفته میشود، حتی اگر تابعی که این پارامتر را دریافت میکند مقداری از آن منتقل نکند، یا این کار را فقط بهصورت مشروط انجام دهد. به عنوان مثال، در شرایط زیر، بررسی فرض میکند که انتقال همواره رخ میدهد:
std::vector<std::string> messages;
void f(std::string &&str) {
// Only remember the message if it isn't empty.
if (!str.empty()) {
messages.emplace_back(std::move(str));
}
}
std::string str = "";
f(std::move(str));
بررسی فرض خواهد کرد که خط آخر باعث یک انتقال میشود، اگرچه در این مورد خاص، چنین اتفاقی نمیافتد. مجدداً، این رفتار عمدی است.
یک حالت خاص وجود دارد: فراخوانی std::move درون فراخوانی try_emplace به صورت محتاطانه انتقالنیافته فرض میشود. این کار به منظور جلوگیری از هشدارهای کاذب است، زیرا این بررسی روشی برای استنتاج مقدار bool بازگرداندهشده توسط try_emplace ندارد.
هنگام تحلیل ترتیبی که در آن انتقالها، استفادهها و مقداردهیهای مجدد رخ میدهند (بخش Unsequenced moves, uses, and reinitializations را ببینید)، فرض میشود که انتقال در همان تابعی رخ میدهد که نتیجهٔ std::move به آن پاس داده شده است.
این بررسی همچنین perfect-forwarding با std::forward را مدیریت میکند، بنابراین کد زیر نیز باعث ایجاد یک هشدار use-after-move خواهد شد.
void consume(int);
void f(int&& i) {
consume(std::forward<int>(i));
consume(std::forward<int>(i)); // use-after-move
}
استفاده (Use)
هرگونه رخداد متغیر منتقلشده که مقداردهی مجدد نباشد (زیر را ببینید) بهعنوان یک استفاده در نظر گرفته میشود.
یک استثنا برای این موضوع اشیاء از نوع std::unique_ptr، std::shared_ptr، std::weak_ptr، std::optional و std::any هستند. یک استثنا برای این موضوع اشیاء از نوع std::unique_ptr، std::shared_ptr، std::weak_ptr، std::optional و std::any هستند که میتوانند از طریق reset دوباره مقداردهی اولیه شوند. به طور خاص برای اشارهگرهای هوشمند، اشیاء منتقلشده دارای وضعیت مشخص nullptr هستند و تنها operator*، operator-> و operator[] دسترسیهای نامعتبر در نظر گرفته میشوند چرا که به یک nullptr ارجاعزدایی خواهند کرد.
اگر چندین استفاده پس از یک انتقال رخ دهد، تنها اولین مورد پرچمگذاری میشود.
مقداردهی مجدد (Reinitialization)
این بررسی یک متغیر را در موارد زیر مقداردهیمجددشده در نظر میگیرد:
- متغیر در سمت چپ یک انتساب قرار گیرد.
- متغیر به عنوان یک اشارهگر non-const یا یک ارجاع lvalue غیر ثابت (non-const) به یک تابع ارسال شود. (فرض بر این است که متغیر ممکن است یک پارامتر خروجی برای تابع باشد.)
- تابع clear() یا assign() روی متغیر فراخوانی شود و متغیر از یکی از انواع کانتینرهای استاندارد basic_string، vector، deque، forward_list، list، set، map، multiset، multimap، unordered_set، unordered_map، unordered_multiset یا unordered_multimap باشد.
- تابع reset() روی متغیر فراخوانی شود و متغیر از نوع std::unique_ptr، std::shared_ptr، std::weak_ptr، std::optional یا std::any باشد.
- یک تابع عضو دارای صفت [[clang::reinitializes]] روی متغیر فراخوانی شود.
اگر متغیر مورد نظر یک struct باشد و مقداری در یکی از متغیرهای عضو مجزای آن نوشته شود، این بررسی آن را مقداردهی مجدد در نظر نمیگیرد -- حتی اگر در نهایت، تمامی متغیرهای عضو آن struct مقداردهی شوند. به عنوان مثال:
struct S {
std::string str;
int i;
};
S s = { "Hello, world!\n", 42 };
S s_other = std::move(s);
s.str = "Lorem ipsum";
s.i = 99;
این بررسی s را پس از خط آخر دوباره مقداردهیشده در نظر نخواهد گرفت؛ در عوض، خطی که به s.str مقدار انتساب میدهد به عنوان یک use-after-move پرچمگذاری خواهد شد. این رفتار عمدی است چرا که این الگوی مقداردهی مجدد یک struct مستعد خطا است. برای مثال، اگر یک متغیر عضو اضافی به S افزوده شود، به سادگی ممکن است مقداردهی مجدد برای این عضو اضافی فراموش شود. در عوض، انتساب به کل struct در یک گام ایمنتر است و از اخطار use-after-move نیز جلوگیری خواهد کرد.
گزینهها (Options)
- InvalidationFunctions
- یک فهرست جداشده با نقطه-ویرگول از عبارات باقاعده (regular expressions) منطبق بر نام توابعی که باعث باطل شدن اولین آرگومان خود میشوند (مانند بستن یک handle). برای توابع عضو، اولین آرگومان همان آرگومان شیء ضمنی (this) در نظر گرفته میشود. مقدار پیشفرض یک رشتهٔ خالی است.
- ReinitializationFunctions
- یک فهرست جداشده با نقطه-ویرگول از عبارات باقاعده منطبق بر نام توابعی که شیء را دوباره مقداردهی اولیه میکنند. برای توابع عضو، آرگومان شیء ضمنی (*this) باز-مقداردهیشده در نظر گرفته میشود. برای توابع غیر عضو یا توابع عضو ایستا (static)، اولین آرگومان دوباره مقداردهیشده فرض میشود. مقدار پیشفرض یک رشتهٔ خالی است.
bugprone-virtual-near-miss
اگر تابعی دچار خطای نزدیک به هدف باشد (یعنی نام آن بسیار شبیه و امضای تابع با یک تابع مجازی از کلاس پایه یکسان باشد)، هشدار میدهد.
مثال:
struct Base {
virtual void func();
};
struct Derived : Base {
virtual void funk();
// warning: 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it?
};
cert-arr39-c
بررسی cert-arr39-c یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-sizeof-expression <> مراجعه کنید.
cert-con36-c
بررسی cert-con36-c یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-spuriously-wake-up-functions <> مراجعه کنید.
cert-con54-cpp
بررسی cert-con54-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-spuriously-wake-up-functions <> مراجعه کنید.
cert-ctr56-cpp
بررسی cert-ctr56-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-pointer-arithmetic-on-polymorphic-object <> مراجعه کنید.
cert-dcl03-c
بررسی cert-dcl03-c یک نام مستعار است، لطفاً برای اطلاعات بیشتر به misc-static-assert <> مراجعه کنید.
cert-dcl16-c
بررسی cert-dcl16-c یک نام مستعار است، لطفاً برای اطلاعات بیشتر به readability-uppercase-literal-suffix <> مراجعه کنید.
cert-dcl37-c
بررسی cert-dcl37-c یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-reserved-identifier <> مراجعه کنید.
cert-dcl50-cpp
بررسی cert-dcl50-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به modernize-avoid-variadic-functions <> مراجعه کنید.
این بررسی متناظر با قاعدهٔ DCL50-CPP از استاندارد کدنویسی CERT C++ است: Do not define a C-style variadic function https://www.securecoding.cert.org/confluence/display/cplusplus/DCL50-CPP.+Do+not+define+a+C-style+variadic+function.
cert-dcl51-cpp
بررسی cert-dcl51-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-reserved-identifier <> مراجعه کنید.
cert-dcl54-cpp
بررسی cert-dcl54-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به misc-new-delete-overloads <> مراجعه کنید.
cert-dcl58-cpp
بررسی cert-dcl58-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-std-namespace-modification <> مراجعه کنید.
این بررسی متناظر با قاعدهٔ DCL58-CPP از استاندارد کدنویسی CERT C++ است: Do not modify the standard namespaces https://www.securecoding.cert.org/confluence/display/cplusplus/DCL58-CPP.+Do+not+modify+the+standard+namespaces.
cert-dcl59-cpp
بررسی cert-dcl59-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به misc-anonymous-namespace-in-header <> مراجعه کنید.
cert-env33-c
بررسی cert-env33-c یک نام مستعار است، لطفاً برای اطلاعات بیشتر به bugprone-command-processor <> مراجعه کنید.
این بررسی متناظر با قاعدهٔ ENV33-C از استاندارد کدنویسی CERT C است: Do not call system() <https://www.securecoding.cert.org/confluence/display/c/ENV33-C.+Do+not+call+system()>.
cert-err09-cpp
بررسی cert-err09-cpp یک نام مستعار است، لطفاً برای اطلاعات بیشتر به misc-throw-by-value-catch-by-reference <> مراجعه کنید.
این بررسی با توصیهٔ ERR09-CPP در استاندارد کدنویسی CERT C++ مطابقت دارد: Throw anonymous temporaries. با این حال، تمام توصیههای CERT از دید عموم حذف شدهاند و بنابراین مشاهدهٔ توجیه آنها برای رفتار این بررسی نیازمند داشتن یک حساب کاربری در ویکی آنها است.
cert-err33-c
در مورد مقادیر بازگشتی استفادهنشدهٔ توابع هشدار میدهد. بسیاری از توابع کتابخانهٔ استاندارد مقداری را برمیگردانند که موفقیتآمیز بودن فراخوانی را مشخص میکند. نادیده گرفتن مقدار بازگشتی در صورت بروز خطا میتواند رفتارهای پیشبینینشدهای ایجاد کند. توابع زیر بررسی میشوند:
- aligned_alloc()
- asctime_s()
- at_quick_exit()
- atexit()
- bsearch()
- bsearch_s()
- btowc()
- c16rtomb()
- c32rtomb()
- calloc()
- clock()
- cnd_broadcast()
- cnd_init()
- cnd_signal()
- cnd_timedwait()
- cnd_wait()
- ctime_s()
- fclose()
- fflush()
- fgetc()
- fgetpos()
- fgets()
- fgetwc()
- fopen()
- fopen_s()
- fprintf()
- fprintf_s()
- fputc()
- fputs()
- fputwc()
- fputws()
- fread()
- freopen()
- freopen_s()
- fscanf()
- fscanf_s()
- fseek()
- fsetpos()
- ftell()
- fwprintf()
- fwprintf_s()
- fwrite()
- fwscanf()
- fwscanf_s()
- getc()
- getchar()
- getenv()
- getenv_s()
- gets_s()
- getwc()
- getwchar()
- gmtime()
- gmtime_s()
- localtime()
- localtime_s()
- malloc()
- mbrtoc16()
- mbrtoc32()
- mbsrtowcs()
- mbsrtowcs_s()
- mbstowcs()
- mbstowcs_s()
- memchr()
- mktime()
- mtx_init()
- mtx_lock()
- mtx_timedlock()
- mtx_trylock()
- mtx_unlock()
- printf_s()
- putc()
- putwc()
- raise()
- realloc()
- remove()
- rename()
- setlocale()
- setvbuf()
- scanf()
- scanf_s()
- signal()
- snprintf()
- snprintf_s()
- sprintf()
- sprintf_s()
- sscanf()
- sscanf_s()
- strchr()
- strerror_s()
- strftime()
- strpbrk()
- strrchr()
- strstr()
- strtod()
- strtof()
- strtoimax()
- strtok()
- strtok_s()
- strtol()
- strtold()
- strtoll()
- strtoumax()
- strtoul()
- strtoull()
- strxfrm()
- swprintf()
- swprintf_s()
- swscanf()
- swscanf_s()
- thrd_create()
- thrd_detach()
- thrd_join()
- thrd_sleep()
- time()
- timespec_get()
- tmpfile()
- tmpfile_s()
- tmpnam()
- tmpnam_s()
- tss_create()
- tss_get()
- tss_set()
- ungetc()
- ungetwc()
- vfprintf()
- vfprintf_s()
- vfscanf()
- vfscanf_s()
- vfwprintf()
- vfwprintf_s()
- vfwscanf()
- vfwscanf_s()
- vprintf_s()
- vscanf()
- vscanf_s()
- vsnprintf()
- vsnprintf_s()
- vsprintf()
- vsprintf_s()
- vsscanf()
- vsscanf_s()
- vswprintf()
- vswprintf_s()
- vswscanf()
- vswscanf_s()
- vwprintf_s()
- vwscanf()
- vwscanf_s()
- wcrtomb()
- wcschr()
- wcsftime()
- wcspbrk()
- wcsrchr()
- wcsrtombs()
- wcsrtombs_s()
- wcsstr()
- wcstod()
- wcstof()
- wcstoimax()
- wcstok()
- wcstok_s()
- wcstol()
- wcstold()
- wcstoll()
- wcstombs()
- wcstombs_s()
- wcstoumax()
- wcstoul()
- wcstoull()
- wcsxfrm()
- wctob()
- wctrans()
- wctype()
- wmemchr()
- wprintf_s()
- wscanf()
- wscanf_s()
این بررسی یک نام مستعار از بررسی bugprone-unused-return-value <> با مجموعهای ثابت از توابع است.
نادیدهگرفتن هشدارها با تبدیل نوع (cast) به void به طور پیشفرض فعال است و میتواند با تنظیم گزینه AllowCastToVoid روی false غیرفعال شود.
این بررسی متناظر با بخشی از قاعده ERR33-C در استاندارد کدنویسی CERT C است: «شناسایی و مدیریت خطاهای کتابخانه استاندارد» https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors. فهرست توابع بررسیشده از این قاعده برگرفته شده است، به استثنای موارد زیر:
- •
- این بررسی نمیتواند تشخیص دهد که آیا یک تابع با آرگومان NULL فراخوانی شده است یا خیر. بنابراین توابع زیر بررسی نمیشوند: mblen، mbrlen، mbrtowc، mbtowc، wctomb، wctomb_s
cert-err34-c
بررسی cert-err34-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-unchecked-string-to-number-conversion <> را ببینید.
cert-err52-cpp
بررسی cert-err52-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر modernize-avoid-setjmp-longjmp <> را ببینید.
این بررسی متناظر با قاعده ERR52-CPP در استاندارد کدنویسی CERT C++ است: «از setjmp() یا longjmp() استفاده نکنید» https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=1834.
cert-err58-cpp
بررسی cert-err58-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-throwing-static-initialization <> را ببینید.
این بررسی متناظر با قاعده ERR58-CPP در استاندارد کدنویسی CERT C++ است: «مدیریت تمام استثناهای پرتابشده پیش از آغاز اجرای main()» https://www.securecoding.cert.org/confluence/display/cplusplus/ERR58-CPP.+Handle+all+exceptions+thrown+before+main%28%29+begins+executing.
cert-err60-cpp
بررسی cert-err60-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-exception-copy-constructor-throws <> را ببینید.
این بررسی متناظر با قاعده ERR60-CPP در استاندارد کدنویسی CERT C++ است: «اشیاء استثنا باید دارای سازنده کپی بدون پرتاب استثنا (nothrow) باشند» https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible.
cert-err61-cpp
بررسی cert-err61-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر misc-throw-by-value-catch-by-reference <> را ببینید.
cert-exp42-c
بررسی cert-exp42-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-suspicious-memory-comparison <> را ببینید.
cert-fio38-c
بررسی cert-fio38-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر misc-non-copyable-objects <> را ببینید.
این بررسی متناظر با قاعده FIO38-C در استاندارد کدنویسی CERT C++ است: «یک شیء FILE را کپی نکنید» https://www.securecoding.cert.org/confluence/display/c/FIO38-C.+Do+not+copy+a+FILE+object.
cert-flp30-c
بررسی cert-flp30-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-float-loop-counter <> را ببینید.
این بررسی متناظر با قاعده FLP30-C در استاندارد کدنویسی CERT C است: «از متغیرهای ممیز شناور به عنوان شمارنده حلقه استفاده نکنید» https://www.securecoding.cert.org/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters.
cert-flp37-c
بررسی cert-flp37-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-suspicious-memory-comparison <> را ببینید.
cert-int09-c
بررسی cert-int09-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر readability-enum-initial-value <> را ببینید.
cert-mem57-cpp
بررسی cert-mem57-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-default-operator-new-on-overaligned-type <> را ببینید.
این بررسی متناظر با قاعده MEM57-CPP در استاندارد کدنویسی CERT C++ است: «اجتناب از استفاده از operator new پیشفرض برای انواع بیشازحد ترازشده (over-aligned)» https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM57-CPP.+Avoid+using+default+operator+new+for+over-aligned+types.
cert-msc24-c
بررسی cert-msc24-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-unsafe-functions <> را ببینید.
cert-msc30-c
بررسی cert-msc30-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر misc-predictable-rand <> را ببینید.
این بررسی متناظر با قاعده MSC30-C در استاندارد کدنویسی CERT C است: «برای تولید اعداد شبهتصادفی از تابع rand() استفاده نکنید» https://wiki.sei.cmu.edu/confluence/display/c/MSC30-C.+Do+not+use+the+rand%28%29+function+for+generating+pseudorandom+numbers.
cert-msc32-c
بررسی cert-msc32-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-random-generator-seed <> را ببینید.
این بررسی متناظر با قاعده MSC32-C در استاندارد کدنویسی CERT C است: «مولدهای اعداد شبهتصادفی را به درستی مقداردهی اولیه (seed) کنید» https://wiki.sei.cmu.edu/confluence/display/c/MSC32-C.+Properly+seed+pseudorandom+number+generators.
cert-msc33-c
بررسی cert-msc33-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-unsafe-functions <> را ببینید.
cert-msc50-cpp
بررسی cert-msc50-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر misc-predictable-rand <> را ببینید.
این بررسی متناظر با قاعده MSC50-CPP در استاندارد کدنویسی CERT C است: «برای تولید اعداد شبهتصادفی از std::rand() استفاده نکنید» https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC50-CPP.+Do+not+use+std%3A%3Arand%28%29+for+generating+pseudorandom+numbers.
cert-msc51-cpp
بررسی cert-msc51-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-random-generator-seed <> را ببینید.
این بررسی متناظر با قاعده MSC51-CPP در استاندارد کدنویسی CERT C++ است: «اطمینان حاصل کنید که مولد اعداد تصادفی شما به درستی مقداردهی اولیه (seed) شده است» https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC51-CPP.+Ensure+your+random+number+generator+is+properly+seeded.
cert-msc54-cpp
بررسی cert-msc54-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-signal-handler <> را ببینید.
cert-oop11-cpp
بررسی cert-oop11-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر performance-move-constructor-init <> را ببینید.
این بررسی با توصیهنامهٔ OOP11-CPP از استاندارد کدنویسی CERT C++ مطابقت دارد: «اعضا یا کلاسهای پایه را از طریق یک سازندهٔ انتقال بهصورت رونوشت مقداردهی اولیه نکنید» (Do not copy-initialize members or base classes from a move constructor). با این حال، تمامی توصیههای CERT از دید عموم حذف شدهاند و به همین دلیل، مشاهدهٔ دلایل و توجیهات آنها برای رفتار این بررسی نیازمند داشتن حساب کاربری در ویکی آنها است.
cert-oop54-cpp
بررسی cert-oop54-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-unhandled-self-assignment <> را ببینید.
cert-oop57-cpp
بررسی cert-oop57-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-raw-memory-call-on-non-trivial-type <> را ببینید.
این بررسی با قاعدهٔ OOP57-CPP از استاندارد کدنویسی CERT C++ مطابقت دارد: «توابع عضو ویژه و عملگرهای بارگذاریشده را به توابع کتابخانهٔ استاندارد C ترجیح دهید» (Prefer special member functions and overloaded operators to C Standard Library functions) https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP57-CPP.+Prefer+special+member+functions+and+overloaded+operators+to+C+Standard+Library+functions.
cert-oop58-cpp
بررسی cert-oop58-cpp یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-copy-constructor-mutates-argument <> را ببینید.
cert-pos44-c
بررسی cert-pos44-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-bad-signal-to-kill-thread <> را ببینید.
cert-pos47-c
بررسی cert-pos47-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر concurrency-thread-canceltype-asynchronous <> را ببینید.
cert-sig30-c
بررسی cert-sig30-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-signal-handler <> را ببینید.
cert-str34-c
بررسی cert-str34-c یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر bugprone-signed-char-misuse <> را ببینید.
clang-analyzer-core.BitwiseShift
مواردی را پیدا میکند که در آنها عملیات تغییر بیتی (bitwise shift) منجر به رفتار تعریفنشده (undefined behaviour) میشود.
بررسی clang-analyzer-core.BitwiseShift یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-bitwiseshift را ببینید.
clang-analyzer-core.CallAndMessage
خطاهای منطقی مربوط به فراخوانیهای توابع و عبارات پیام Objective-C را بررسی میکند (مانند آرگومانهای مقداردهینشده، اشارهگرهای تابع تهی/null).
بررسی clang-analyzer-core.CallAndMessage یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-callandmessage را ببینید.
clang-analyzer-core.DivideZero
تقسیم بر صفر را بررسی میکند.
بررسی clang-analyzer-core.DivideZero یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-dividezero را ببینید.
clang-analyzer-core.NonNullParamChecker
اشارهگرهای تهی (null) پاسدادهشده بهعنوان آرگومان به تابعی که آرگومانهای آن ارجاع هستند یا با صفت 'nonnull' علامتگذاری شدهاند را بررسی میکند.
بررسی clang-analyzer-core.NonNullParamChecker یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-nonnullparamchecker را ببینید.
clang-analyzer-core.NullDereference
ابطال ارجاع (dereference) اشارهگرهای تهی (null) را بررسی میکند.
بررسی clang-analyzer-core.NullDereference یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-nulldereference را ببینید.
clang-analyzer-core.StackAddressEscape
بررسی میکند که آدرسهای حافظهٔ پشته (stack) به بیرون از تابع نشت نکنند (escape).
بررسی clang-analyzer-core.StackAddressEscape یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-stackaddressescape را ببینید.
clang-analyzer-core.UndefinedBinaryOperatorResult
نتایج تعریفنشدهٔ عملگرهای باینری را بررسی میکند.
بررسی clang-analyzer-core.UndefinedBinaryOperatorResult یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-undefinedbinaryoperatorresult را ببینید.
clang-analyzer-core.VLASize
تعریفهای آرایههای با طول متغیر (VLA) با اندازهٔ تعریفنشده یا صفر را بررسی میکند.
بررسی clang-analyzer-core.VLASize یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-vlasize را ببینید.
clang-analyzer-core.uninitialized.ArraySubscript
استفاده از مقادیر مقداردهینشده بهعنوان اندیس آرایه را بررسی میکند.
بررسی clang-analyzer-core.uninitialized.ArraySubscript یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-arraysubscript را ببینید.
clang-analyzer-core.uninitialized.Assign
انتساب مقادیر مقداردهینشده را بررسی میکند.
بررسی clang-analyzer-core.uninitialized.Assign یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-assign را ببینید.
clang-analyzer-core.uninitialized.Branch
استفاده از مقادیر مقداردهینشده بهعنوان شرایط انشعاب (branch conditions) را بررسی میکند.
بررسی clang-analyzer-core.uninitialized.Branch یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-branch را ببینید.
clang-analyzer-core.uninitialized.CapturedBlockVariable
بلاکهایی را که مقادیر مقداردهینشده را تسخیر میکنند (capture)، بررسی میکند.
بررسی clang-analyzer-core.uninitialized.CapturedBlockVariable یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-capturedblockvariable را ببینید.
clang-analyzer-core.uninitialized.NewArraySize
بررسی میکند که آیا اندازهٔ آرایه در یک عبارت []new تعریفنشده است یا خیر.
بررسی clang-analyzer-core.uninitialized.NewArraySize یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-newarraysize را ببینید.
clang-analyzer-core.uninitialized.UndefReturn
بازگرداندن مقادیر مقداردهینشده به فراخواننده (caller) را بررسی میکند.
بررسی clang-analyzer-core.uninitialized.UndefReturn یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-undefreturn را ببینید.
clang-analyzer-cplusplus.ArrayDelete
تخریب آرایههایی از اشیاء چندریختی (polymorphic) را که به عنوان کلاس پایهشان تخریب میشوند، گزارش میدهد.
بررسی clang-analyzer-cplusplus.ArrayDelete یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete مراجعه کنید.
clang-analyzer-cplusplus.InnerPointer
اشارهگرهای داخلی دربرگیرندههای ++C را که پس از تخصیص مجدد یا آزادسازی حافظه (re/deallocation) استفاده شدهاند بررسی میکند.
بررسی clang-analyzer-cplusplus.InnerPointer یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-innerpointer مراجعه کنید.
clang-analyzer-cplusplus.Move
باگهای استفاده پس از انتقال (use-after-move) را در ++C پیدا میکند.
بررسی clang-analyzer-cplusplus.Move یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-move مراجعه کنید.
clang-analyzer-cplusplus.NewDelete
مشکلات آزادسازی مضاعف (double-free) و استفاده پس از آزادسازی (use-after-free) را بررسی میکند. حافظه مدیریتشده توسط new/delete را ردگیری میکند.
بررسی clang-analyzer-cplusplus.NewDelete یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdelete مراجعه کنید.
clang-analyzer-cplusplus.NewDeleteLeaks
نشتهای حافظه (memory leaks) را بررسی میکند. حافظه مدیریتشده توسط new/delete را ردگیری میکند.
بررسی clang-analyzer-cplusplus.NewDeleteLeaks یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdeleteleaks مراجعه کنید.
clang-analyzer-cplusplus.PlacementNew
بررسی میکند که آیا placement new پیشفرض به اشارهگرهایی با ظرفیت ذخیرهسازی کافی مجهز شده است یا خیر.
بررسی clang-analyzer-cplusplus.PlacementNew یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-placementnew مراجعه کنید.
clang-analyzer-cplusplus.SelfAssignment
عملگرهای انتساب کپی و انتقال در ++C را برای انتساب به خود (self assignment) بررسی میکند.
بررسی clang-analyzer-cplusplus.SelfAssignment یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-stringchecker مراجعه کنید.
clang-analyzer-cplusplus.StringChecker
باگهای std::string در ++C را بررسی میکند.
بررسی clang-analyzer-cplusplus.StringChecker یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-stringchecker مراجعه کنید.
clang-analyzer-deadcode.DeadStores
مقادیر ذخیرهشده در متغیرهایی را که پس از آن هرگز خوانده نمیشوند بررسی میکند.
بررسی clang-analyzer-deadcode.DeadStores یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#deadcode-deadstores مراجعه کنید.
clang-analyzer-fuchsia.HandleChecker
بررسیکنندهای که نشتهای مربوط به هندلهای Fuchsia را شناسایی میکند.
بررسی clang-analyzer-fuchsia.HandleChecker یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#fuchsia-handlechecker برای اطلاعات بیشتر مراجعه کنید.
clang-analyzer-nullability.NullPassedToNonnull
هنگامی که یک اشارهگر null به اشارهگری با نوع _Nonnull ارسال میشود، هشدار میدهد.
بررسی clang-analyzer-nullability.NullPassedToNonnull یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullpassedtononnull مراجعه کنید.
clang-analyzer-nullability.NullReturnedFromNonnull
هنگامی که یک اشارهگر null از تابعی که نوع بازگشتی آن _Nonnull است برگردانده شود، هشدار میدهد.
بررسی clang-analyzer-nullability.NullReturnedFromNonnull یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullreturnedfromnonnull مراجعه کنید.
clang-analyzer-nullability.NullableDereferenced
هنگامی که ارجاع یک اشارهگر nullable بازمیشود (dereference میشود)، هشدار میدهد.
بررسی clang-analyzer-nullability.NullableDereferenced یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullabledereferenced مراجعه کنید.
clang-analyzer-nullability.NullablePassedToNonnull
هنگامی که یک اشارهگر nullable به اشارهگری با نوع _Nonnull ارسال میشود، هشدار میدهد.
بررسی clang-analyzer-nullability.NullablePassedToNonnull یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullablepassedtononnull مراجعه کنید.
clang-analyzer-nullability.NullableReturnedFromNonnull
هنگامی که یک اشارهگر nullable از تابعی با نوع بازگشتی _Nonnull بازگردانده میشود، هشدار میدهد.
بررسی clang-analyzer-nullability.NullableReturnedFromNonnull یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullablereturnedfromnonnull مراجعه کنید.
clang-analyzer-optin.core.EnumCastOutOfRange
تبدیلهای نوع عدد صحیح به شمارشی را برای مقادیر خارج از محدوده بررسی میکند.
بررسی clang-analyzer-optin.core.EnumCastOutOfRange یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-core-enumcastoutofrange مراجعه کنید.
clang-analyzer-optin.cplusplus.UninitializedObject
فیلدهای مقداردهینشده پس از ساخت شیء را گزارش میدهد.
بررسی clang-analyzer-optin.cplusplus.UninitializedObject یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-cplusplus-uninitializedobject مراجعه کنید.
clang-analyzer-optin.cplusplus.VirtualCall
فراخوانی توابع مجازی در طول ساخت/تخریب شیء را بررسی میکند.
بررسی clang-analyzer-optin.cplusplus.VirtualCall یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-cplusplus-virtualcall مراجعه کنید.
clang-analyzer-optin.mpi.MPI-Checker
کدهای MPI را بررسی میکند.
بررسی clang-analyzer-optin.mpi.MPI-Checker یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-mpi-mpi-checker مراجعه کنید.
clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker
بررسی میکند که آیا ماکروهای NSLocalizedString شامل یک کامنت برای بافتار (context) هستند یا خیر.
بررسی clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-osx-cocoa-localizability-emptylocalizationcontextchecker مراجعه کنید.
clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker
در مورد استفاده از NSStringهای محلیسازینشده که به متدهای رابط کاربری نیازمند NSStringهای محلیسازیشده ارسال میشوند، هشدار میدهد.
بررسی clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-osx-cocoa-localizability-nonlocalizedstringchecker مراجعه کنید.
clang-analyzer-optin.performance.GCDAntipattern
بررسی ضدالگوهای (anti-patterns) کارایی هنگام استفاده از Grand Central Dispatch.
بررسی clang-analyzer-optin.performance.GCDAntipattern یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-gcdantipattern را ببینید.
clang-analyzer-optin.performance.Padding
بررسی structهایی که دارای لایهگذاری (padding) بیش از حد هستند.
بررسی clang-analyzer-optin.performance.Padding یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-padding را ببینید.
clang-analyzer-optin.portability.UnixAPI
یافتن رفتارهای وابسته به پیادهسازی (implementation-defined) در توابع UNIX/Posix.
بررسی clang-analyzer-optin.portability.UnixAPI یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-portability-unixapi را ببینید.
clang-analyzer-optin.taint.TaintedAlloc
بررسی تخصیصهای حافظهای که ممکن است پارامتر اندازهٔ آنها یک مقدار آلوده (کنترلشده توسط مهاجم) باشد.
بررسی clang-analyzer-optin.taint.TaintedAlloc یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#optin-taint-taintedalloc را ببینید.
clang-analyzer-osx.API
بررسی استفادهٔ صحیح از رابطهای برنامهنویسی مختلف Apple.
بررسی clang-analyzer-osx.API یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-api را ببینید.
clang-analyzer-osx.NumberObjectConversion
بررسی تبدیلهای نادرست شیءهای نمایشدهندهٔ اعداد به عددها.
بررسی clang-analyzer-osx.NumberObjectConversion یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion را ببینید.
clang-analyzer-osx.ObjCProperty
بررسی استفادهٔ مناسب از ویژگیهای Objective-C.
بررسی clang-analyzer-osx.ObjCProperty یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-objcproperty را ببینید.
clang-analyzer-osx.SecKeychainAPI
بررسی استفادهٔ صحیح از APIهای Secure Keychain.
بررسی clang-analyzer-osx.SecKeychainAPI یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-seckeychainapi را ببینید.
clang-analyzer-osx.cocoa.AtSync
بررسی اشارهگرهای nil که به عنوان mutex برای @synchronized استفاده شدهاند.
بررسی clang-analyzer-osx.cocoa.AtSync یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-atsync را ببینید.
clang-analyzer-osx.cocoa.AutoreleaseWrite
هشدار در مورد عملیات نوشتن بالقوه منجر به فروپاشی (crash) روی اشیاء autoreleasing از poolهای آزادسازی خودکار مختلف در Objective-C.
بررسی clang-analyzer-osx.cocoa.AutoreleaseWrite یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-autoreleasewrite را ببینید.
clang-analyzer-osx.cocoa.ClassRelease
بررسی ارسال مستقیم 'retain'، 'release'، یا 'autorelease' به یک Class.
بررسی clang-analyzer-osx.cocoa.ClassRelease یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-classrelease را ببینید.
clang-analyzer-osx.cocoa.Dealloc
هشدار دربارهٔ کلاسهای Objective-C که فاقد پیادهسازی صحیح -dealloc هستند.
بررسی clang-analyzer-osx.cocoa.Dealloc یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-dealloc را ببینید.
clang-analyzer-osx.cocoa.IncompatibleMethodTypes
هشدار در مورد امضای متدهای Objective-C با ناسازگاریهای نوع (type incompatibilities).
بررسی clang-analyzer-osx.cocoa.IncompatibleMethodTypes یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-incompatiblemethodtypes را ببینید.
clang-analyzer-osx.cocoa.Loops
مدلسازی بهبودیافته از حلقهها با استفاده از انواع مجموعههای Cocoa.
بررسی clang-analyzer-osx.cocoa.Loops یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-loops را ببینید.
clang-analyzer-osx.cocoa.MissingSuperCall
هشدار دربارهٔ متدهای Objective-C که فاقد فراخوانی لازم به super هستند.
بررسی clang-analyzer-osx.cocoa.MissingSuperCall یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-missingsupercall را ببینید.
clang-analyzer-osx.cocoa.NSAutoreleasePool
هشدار در مورد استفادههای غیربهینه از NSAutoreleasePool در حالت GC زبان Objective-C.
بررسی clang-analyzer-osx.cocoa.NSAutoreleasePool یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nsautoreleasepool را ببینید.
clang-analyzer-osx.cocoa.NSError
بررسی نحوهٔ استفاده از پارامترهای **NSError.
بررسی clang-analyzer-osx.cocoa.NSError یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nserror را ببینید.
clang-analyzer-osx.cocoa.NilArg
بررسی آرگومانهای غیرمجاز nil در فراخوانی متدهای ObjC.
بررسی clang-analyzer-osx.cocoa.NilArg یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nilarg را ببینید.
clang-analyzer-osx.cocoa.NonNilReturnValue
مدلسازی APIهایی که تضمین میکنند مقداری غیر از nil برمیگردانند.
بررسی clang-analyzer-osx.cocoa.NonNilReturnValue یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nonnilreturnvalue را ببینید.
clang-analyzer-osx.cocoa.ObjCGenerics
بررسی خطاهای نوع هنگام استفاده از جنریکهای Objective-C.
بررسی clang-analyzer-osx.cocoa.ObjCGenerics یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-objcgenerics را ببینید.
clang-analyzer-osx.cocoa.RetainCount
بررسی نشتها و مدیریت نادرست شمارندهٔ ارجاع (reference count).
بررسی clang-analyzer-osx.cocoa.RetainCount یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-retaincount را ببینید.
clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak
بررسی حافظهٔ نشتیافته در autorelease poolهایی که هرگز تخلیه نمیشوند.
بررسی clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر بخش Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-runloopautoreleaseleak را ببینید.
clang-analyzer-osx.cocoa.SelfInit
بررسی میکند که 'self' درون یک متد مقداردهی اولیه (initializer) بهدرستی مقداردهی شده باشد.
بررسی clang-analyzer-osx.cocoa.SelfInit یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-selfinit مراجعه کنید.
clang-analyzer-osx.cocoa.SuperDealloc
درباره استفاده نادرست از '[super dealloc]' در Objective-C هشدار میدهد.
بررسی clang-analyzer-osx.cocoa.SuperDealloc یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-superdealloc مراجعه کنید.
clang-analyzer-osx.cocoa.UnusedIvars
درباره ivarهای خصوصی که هرگز استفاده نشدهاند هشدار میدهد.
بررسی clang-analyzer-osx.cocoa.UnusedIvars یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-unusedivars مراجعه کنید.
clang-analyzer-osx.cocoa.VariadicMethodTypes
ارسال انواع غیر Objective-C به متدهای مقداردهی اولیه مجموعههای متغیر (variadic) را که فقط انتظار انواع Objective-C دارند، بررسی میکند.
بررسی clang-analyzer-osx.cocoa.VariadicMethodTypes یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-variadicmethodtypes مراجعه کنید.
clang-analyzer-osx.coreFoundation.CFError
استفاده از پارامترهای *CFErrorRef را بررسی میکند.
بررسی clang-analyzer-osx.coreFoundation.CFError یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-cferror مراجعه کنید.
clang-analyzer-osx.coreFoundation.CFNumber
استفاده صحیح از APIهای CFNumber را بررسی میکند.
بررسی clang-analyzer-osx.coreFoundation.CFNumber یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-cfnumber مراجعه کنید.
clang-analyzer-osx.coreFoundation.CFRetainRelease
آرگومانهای تهی (null) به CFRetain/CFRelease/CFMakeCollectable را بررسی میکند.
بررسی clang-analyzer-osx.coreFoundation.CFRetainRelease یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-cfretainrelease مراجعه کنید.
clang-analyzer-osx.coreFoundation.containers.OutOfBounds
خارج از محدوده بودن اندیس را هنگام استفاده از API مربوط به 'CFArray' بررسی میکند.
بررسی clang-analyzer-osx.coreFoundation.containers.OutOfBounds یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-outofbounds مراجعه کنید.
clang-analyzer-osx.coreFoundation.containers.PointerSizedValues
اگر 'CFArray'، 'CFDictionary' یا 'CFSet' با مقادیری با اندازهای غیر از اندازه اشارهگر ساخته شوند، هشدار میدهد.
بررسی clang-analyzer-osx.coreFoundation.containers.PointerSizedValues یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-pointersizedvalues مراجعه کنید.
clang-analyzer-security.FloatLoopCounter
در مورد استفاده از یک مقدار ممیز شناور به عنوان شمارنده حلقه هشدار میدهد (CERT: FLP30-C, FLP30-CPP).
بررسی clang-analyzer-security.FloatLoopCounter یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-floatloopcounter مراجعه کنید.
clang-analyzer-security.PutenvStackArray
فراخوانیهای تابع putenv را پیدا میکند که اشارهگری به یک آرایه تخصیصیافته روی پشته (خودکار/اتوماتیک) را به عنوان آرگومان ارسال میکنند. تابع putenv رشته ارسالی را کپی نمیکند، بلکه تنها یک اشارهگر به داده ذخیره میشود و این داده حتی توسط ریسههای دیگر نیز قابل خواندن است. محتوای یک آرایه تخصیصیافته روی پشته به احتمال زیاد پس از خروج از تابع رونویسی میشود.
بررسی clang-analyzer-security.PutenvStackArray یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-putenvstackarray-c مراجعه کنید.
clang-analyzer-security.SetgidSetuidOrder
این بررسیکننده توالیهای فراخوانیهای setuid(getuid()) و setgid(getgid()) را (به همین ترتیب) بررسی میکند. اگر چنین توالیای یافت شود و هیچ فراخوانی تابع تغییردهنده مجوز دیگری (seteuid، setreuid، setresuid و نسخههای GID آنها) در این بین نباشد، یک هشدار صادر میشود. این بررسیکننده فقط فراخوانیهای دقیقاً setuid(getuid()) (و نسخههای GID) را مییابد، نه مثلاً مواردی که نتیجه getuid() در یک متغیر ذخیره شده باشد.
بررسی clang-analyzer-security.SetgidSetuidOrder یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-setgidsetuidorder-c مراجعه کنید.
clang-analyzer-security.cert.env.InvalidPtr
استفاده از اشارهگرهای احتمالاً باطلشده را مییابد.
بررسی clang-analyzer-security.cert.env.InvalidPtr یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr مراجعه کنید.
clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling
در مورد استفاده از توابع ناامن یا منسوخشده دستکاری بافر هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-deprecatedorunsafebufferhandling مراجعه کنید.
clang-analyzer-security.insecureAPI.UncheckedReturn
در مورد استفاده از توابعی که مقادیر بازگشتی آنها همیشه باید بررسی شوند، هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.UncheckedReturn یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-uncheckedreturn مراجعه کنید.
clang-analyzer-security.insecureAPI.bcmp
در مورد استفاده از تابع 'bcmp' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.bcmp یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bcmp مراجعه کنید.
clang-analyzer-security.insecureAPI.bcopy
در مورد استفاده از تابع 'bcopy' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.bcopy یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bcopy مراجعه کنید..SS clang-analyzer-security.insecureAPI.bzero
درباره استفاده از تابع 'bzero' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.bzero یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bzero مراجعه کنید.
clang-analyzer-security.insecureAPI.decodeValueOfObjCType
درباره استفاده از متد '-decodeValueOfObjCType:at:' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.decodeValueOfObjCType یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer security.insecureAPI.decodeValueOfObjCType https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-decodevalueofobjctype مراجعه کنید.
clang-analyzer-security.insecureAPI.getpw
درباره استفاده از تابع 'getpw' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.getpw یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-getpw مراجعه کنید.
clang-analyzer-security.insecureAPI.gets
درباره استفاده از تابع 'gets' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.gets یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-gets مراجعه کنید.
clang-analyzer-security.insecureAPI.mkstemp
هنگامی که کمتر از ۶ نویسه X در رشته قالببندی به 'mkstemp' ارسال شود، هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.mkstemp یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-mkstemp مراجعه کنید.
clang-analyzer-security.insecureAPI.mktemp
درباره استفاده از تابع 'mktemp' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.mktemp یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-mktemp مراجعه کنید.
clang-analyzer-security.insecureAPI.rand
درباره استفاده از توابع 'rand'، 'random' و توابع مرتبط هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.rand یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-rand مراجعه کنید.
clang-analyzer-security.insecureAPI.strcpy
درباره استفاده از توابع 'strcpy' و 'strcat' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.strcpy یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-strcpy مراجعه کنید.
clang-analyzer-security.insecureAPI.vfork
درباره استفاده از تابع 'vfork' هشدار میدهد.
بررسی clang-analyzer-security.insecureAPI.vfork یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-vfork مراجعه کنید.
clang-analyzer-unix.API
فراخوانی توابع مختلف UNIX/Posix را بررسی میکند.
بررسی clang-analyzer-unix.API یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-api مراجعه کنید.
clang-analyzer-unix.BlockInCriticalSection
فراخوانی توابع مسدودکننده (blocking) در داخل بخش بحرانی (critical section) را بررسی میکند.
بررسی clang-analyzer-unix.BlockInCriticalSection یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-blockincriticalsection مراجعه کنید.
clang-analyzer-unix.Errno
استفاده نادرست از 'errno' را بررسی میکند.
بررسی clang-analyzer-unix.Errno یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno مراجعه کنید.
clang-analyzer-unix.Malloc
نشتی حافظه (memory leaks)، آزادسازی دوباره (double free) و مشکلات استفاده پس از آزادسازی (use-after-free) را بررسی میکند. حافظه مدیریتشده توسط malloc()/free() را ردیابی میکند.
بررسی clang-analyzer-unix.Malloc یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-malloc مراجعه کنید.
clang-analyzer-unix.MallocSizeof
آرگومانهای مشکوک malloc شامل sizeof را بررسی میکند.
بررسی clang-analyzer-unix.MallocSizeof یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-mallocsizeof مراجعه کنید.
clang-analyzer-unix.MismatchedDeallocator
عدم تطابق توابع تخصیصزدا (deallocators) را بررسی میکند.
بررسی clang-analyzer-unix.MismatchedDeallocator یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-mismatcheddeallocator مراجعه کنید.
clang-analyzer-unix.StdCLibraryFunctions
آرگومانهای نامعتبر توابع کتابخانه استاندارد C را بررسی کرده و روابط میان آرگومانها و مقدار بازگشتی را اعمال میکند.
بررسی clang-analyzer-unix.StdCLibraryFunctions یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-stdclibraryfunctions مراجعه کنید.
clang-analyzer-unix.Stream
توابع مدیریت جریان (stream) را بررسی میکند.
بررسی clang-analyzer-unix.Stream یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream مراجعه کنید.
clang-analyzer-unix.Vfork
استفاده صحیح از vfork را بررسی میکند.
بررسی clang-analyzer-unix.Vfork یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-vfork مراجعه کنید.
clang-analyzer-unix.cstring.BadSizeArg
آرگومان اندازه پاس داده شده به توابع رشتهای C را برای الگوهای خطای رایج بررسی میکند.
بررسی clang-analyzer-unix.cstring.BadSizeArg یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-badsizearg مراجعه کنید.
clang-analyzer-unix.cstring.NullArg
پاس داده شدن اشارهگرهای تهی (null) به عنوان آرگومان به توابع رشتهای C را بررسی میکند.
بررسی clang-analyzer-unix.cstring.NullArg یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-nullarg مراجعه کنید.
clang-analyzer-webkit.NoUncountedMemberChecker
عدم وجود متغیرهای عضو شمارشنشده (uncounted) را بررسی میکند.
بررسی clang-analyzer-webkit.NoUncountedMemberChecker یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#webkit-nouncountedmemberchecker مراجعه کنید.
clang-analyzer-webkit.RefCntblBaseVirtualDtor
بررسی میکند که آیا هر کلاس پایهای با قابلیت شمارش ارجاع (ref-countable) دارای مخرب مجازی (virtual destructor) است یا خیر.
بررسی clang-analyzer-webkit.RefCntblBaseVirtualDtor یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#webkit-refcntblbasevirtualdtor مراجعه کنید.
clang-analyzer-webkit.UncountedLambdaCapturesChecker
گرفتنهای (captures) شمارشنشده در عبارات لامبدا را بررسی میکند.
بررسی clang-analyzer-webkit.UncountedLambdaCapturesChecker یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به Clang Static Analyzer Available Checkers https://clang.llvm.org/docs/analyzer/checkers.html#webkit-uncountedlambdacaptureschecker مراجعه کنید.
concurrency-mt-unsafe
برخی از توابع ناامن از نظر ریسهبندی (thread-unsafe) را در برابر یک لیست سیاه از توابع شناختهشده به ناامنی بررسی میکند. معمولاً این توابع بدون همگامسازی به متغیرهای ایستا دسترسی پیدا میکنند (مانند gmtime(3)) یا از سیگنالها به شکلی همراه با شرایط رقابتی (racy) استفاده مینمایند. مجموعه توابعی که باید بررسی شوند با گزینه FunctionSet مشخص میشود.
توجه داشته باشید که استفاده از برخی توابع thread-unsafe ممکن است در برنامهنویسی همروند در صورتی که تنها از یک ریسه استفاده شود همچنان معتبر باشد (مانند setenv(3))؛ با این حال، برخی از توابع ممکن است حالتی را در متغیرهای سراسری ردیابی کنند که با فراخوانیهای بعدی (غیرموازی، اما همروند) به یک تابع مرتبط تخریب میشود. به عنوان مثال، کد زیر از دسترسیهای محافظتنشده به یک وضعیت سراسری رنج میبرد:
// getnetent(3) maintains global state with DB connection, etc. // If a concurrent green thread calls getnetent(3), the global state is corrupted. netent = getnetent(); yield(); netent = getnetent();
مثالها:
tm = gmtime(timep); // uses a global buffer sleep(1); // implementation may use SIGALRM
گزینهها
- FunctionSet
- مشخص
میکند که
کدام توابع
در libc باید
امن از نظر
ریسه در نظر
گرفته
شوند؛
مقادیر
ممکن
عبارتند از
posix، glibc، یا any.
مقدار posix به معنای توابع thread-unsafe تعریفشده توسط POSIX است. استاندارد POSIX.1-2001 در «2.9.1 Thread-Safety» تعریف میکند که تمام توابع مشخصشده در استاندارد، به جز یک فهرست از پیش تعریفشده از توابع thread-unsafe، امن از نظر ریسه هستند.
کتابخانه Glibc برخی از آنها را به عنوان thread-safe تعریف میکند (مانند dirname(3))، اما موارد ناامن غیراستاندارد POSIX را نیز اضافه میکند (مانند getopt_long(3)). فهرست Glibc از مستندات وب GNU با جستجو برای برچسب MT-Safe گردآوری شده است: https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html
اگر میخواهید APIهای thread-unsafe را حداقل برای یک libc شناسایی کنید یا مطمئن نیستید از کدام libc استفاده خواهد شد، از any (پیشفرض) استفاده کنید.
concurrency-thread-canceltype-asynchronous
فراخوانیهای تابع pthread_setcanceltype را که در آنها نوع لغو ریسه روی ناهمگام (asynchronous) تنظیم شده است، پیدا میکند. نوع لغو ناهمگام (PTHREAD_CANCEL_ASYNCHRONOUS) به طور کلی ناامن است؛ به جای آن از نوع PTHREAD_CANCEL_DEFERRED که حالت پیشفرض است استفاده کنید. حتی با لغو به تعویق افتاده (deferred)، یک نقطه لغو در یک مدیر سیگنال ناهمگام ممکن است همچنان اعمال شود و اثر آن به گونهای خواهد بود که گویی یک لغو ناهمگام بوده است.
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
این بررسی با قاعده استاندارد کدنویسی CERT C مطابقت دارد: POS47-C. Do not use threads that can be canceled asynchronously https://wiki.sei.cmu.edu/confluence/display/c/POS47-C.+Do+not+use+threads+that+can+be+canceled+asynchronously.
بررسی cert-pos47-c به عنوان نام مستعار این بررسی به اینجا هدایت میشود.
cppcoreguidelines-avoid-c-arrays
بررسی cppcoreguidelines-avoid-c-arrays یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به modernize-avoid-c-arrays <> مراجعه کنید.
cppcoreguidelines-avoid-capturing-lambda-coroutines
لامبداهای کوروتین C++20 با لیستهای ضبط غیرخالی را که ممکن است باعث خطاهای use-after-free شوند نشانهگذاری میکند و پیشنهاد میکند از گرفتن مقادیر خودداری شود یا اطمینان حاصل شود که شیء بستار (closure) لامبدا دارای طول عمر تضمینشده است.
این بررسی قاعده CP.51 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rcoro-capture از C++ Core Guidelines را پیادهسازی میکند.
استفاده از لامبداهای کوروتین با لیستهای گرفتن (capture lists) غیرخالی میتواند پرخطر باشد، زیرا گرفتن متغیرها میتواند منجر به دسترسی به حافظه آزاد شده پس از اولین نقطه تعلیق (suspension point) شود. این مشکل حتی ممکن است با اشارهگرهای هوشمند شمارش ارجاعشده (refcounted) و انواع قابل کپی نیز رخ دهد. هنگامی که یک عبارت لامبدا یک کوروتین ایجاد میکند، منجر به ایجاد یک شیء closure با فضای ذخیرهسازی میشود که اغلب روی پشته قرار دارد و در نهایت از محدوده (scope) خارج میشود. هنگامی که شیء closure از محدوده خارج میشود، مقادیر ضبطشده آن نیز از محدوده خارج میشوند. در حالی که لامبداهای معمولی قبل از وقوع این امر اجرای خود را به پایان میرسانند، لامبداهای کوروتین ممکن است پس از تخریب شیء closure از حالت تعلیق از سر گرفته شوند که این امر منجر به دسترسی به حافظه use-after-free برای تمام مقادیر ضبطشده میشود.
مثال زیر را در نظر بگیرید:
int value = get_value();
std::shared_ptr<Foo> sharedFoo = get_foo();
{
const auto lambda = [value, sharedFoo]() -> std::future<void>
{
co_await something();
// "sharedFoo" and "value" have already been destroyed
// the "shared" pointer didn't accomplish anything
};
lambda();
} // the lambda closure object has now gone out of scope
در این مثال، شیء لامبدا با دو مورد ضبط تعریف شده است: value و sharedFoo. هنگامی که lambda() فراخوانی میشود، شیء لامبدا روی پشته ایجاد میشود و مقادیر ضبطشده در شیء closure کپی میشوند. هنگامی که کوروتین معلق میشود، شیء لامبدا از محدوده خارج شده و شیء closure تخریب میشود. هنگامی که کوروتین از سر گرفته میشود، متغیرهای ضبطشده ممکن است تخریب شده باشند که منجر به باگهای use-after-free میشود.
در نتیجه، استفاده از لامبداهای کوروتین با لیستهای ضبط غیرخالی میتواند هنگام ازسرگیری کوروتین پس از تخریب شیء closure منجر به خطاهای use-after-free شود. این بررسی با نشانهگذاری لامبداهای کوروتین C++20 با لیستهای ضبط غیرخالی و پیشنهاد اجتناب از captures یا تضمین طول عمر معتبر شیء closure لامبدا، به جلوگیری از چنین خطاهایی کمک میکند.
پیروی از این دستورالعملها میتواند به تضمین استفاده ایمن و قابل اعتماد از لامبداهای کوروتین در کدهای C++ کمک کند.
cppcoreguidelines-avoid-const-or-ref-data-members
این بررسی زمانی هشدار میدهد که ساختارها یا کلاسهایی که قابلیت کپی یا انتقال دارند، دارای اعضای دادهای واجد شرایط const یا ارجاعی (lvalue یا rvalue) باشند. داشتن چنین اعضایی به ندرت مفید است و باعث میشود کلاس تنها قابلیت ساخت با کپی (copy-constructible) داشته باشد اما قابلیت انتساب با کپی (copy-assignable) را از دست بدهد.
مثالها:
// Bad, const-qualified member
struct Const {
const int x;
}
// Good:
class Foo {
public:
int get() const { return x; }
private:
int x;
};
// Bad, lvalue reference member
struct Ref {
int& x;
};
// Good:
struct Foo {
int* x;
std::unique_ptr<int> x;
std::shared_ptr<int> x;
gsl::not_null<int*> x;
};
// Bad, rvalue reference member
struct RefRef {
int&& x;
};
این بررسی بند C.12 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-constref از C++ Core Guidelines را پیادهسازی میکند.
مطالعه بیشتر: Data members: Never const https://quuxplusone.github.io/blog/2022/01/23/dont-const-all-the-things/#data-members-never-const.
cppcoreguidelines-avoid-do-while
هنگام استفاده از حلقههای do-while هشدار میدهد. خوانایی آنها نسبت به حلقههای سادهٔ while کمتر است، زیرا شرط پایان در انتها قرار دارد و شرط پیش از اولین تکرار حلقه بررسی نمیشود. این مسئله میتواند منجر به باگهای نامحسوس شود.
این بررسی بند ES.75 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-do از C++ Core Guidelines را پیادهسازی میکند.
مثالها:
int x;
do {
std::cin >> x;
// ...
} while (x < 0);
گزینهها
- IgnoreMacros
- هنگام تحلیل ماکروها، این بررسی را نادیده میگیرد. این مورد برای تعریف ایمن ماکروهای شبهتابع کاربرد دارد:
#define FOO_BAR(x) \
do { \
foo(x); \
bar(x); \
} while(0)
پیشفرض false است.
cppcoreguidelines-avoid-goto
استفاده از goto برای کنترل جریان خطاپذیر است و باید با ساختارهای تکرار حلقه جایگزین شود. تنها پرشهای روبهجلو در حلقههای تودرتو مجاز هستند.
این بررسی بند ES.76 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es76-avoid-goto از C++ Core Guidelines و بند 6.3.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/statements از High Integrity C++ Coding Standard را پیادهسازی میکند.
برای اطلاعات بیشتر در مورد چرایی پرهیز از برنامهنویسی با goto میتوانید مقالهٔ مشهور A Case against the GO TO Statement را مطالعه کنید: https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF.
این بررسی goto را برای پرشهای روبهعقب در تمامی حالتهای زبان عیبیابی میکند. این موارد باید با ساختارهای تکرار حلقه در C/C++ جایگزین شوند.
// Bad, handwritten for loop.
int i = 0;
// Jump label for the loop
loop_start:
do_some_operation();
if (i < 100) {
++i;
goto loop_start;
}
// Better
for(int i = 0; i < 100; ++i)
do_some_operation();
زبان C++ مدرن فقط برای پرش به بیرون از حلقههای تودرتو به goto نیاز دارد.
for(int i = 0; i < 100; ++i) {
for(int j = 0; j < 100; ++j) {
if (i * j > 500)
goto early_exit;
}
}
early_exit:
some_operation();
سایر کاربردهای goto در C++ عیبیابی میشوند.
گزینهها
- IgnoreMacros
- اگر روی true تنظیم شود، در صورتی که یک عبارت goto از یک ماکرو بسط یافته باشد، این بررسی هشداری صادر نخواهد کرد. مقدار پیشفرض false است.
cppcoreguidelines-avoid-magic-numbers
بررسی cppcoreguidelines-avoid-magic-numbers یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به readability-magic-numbers <> مراجعه کنید.
cppcoreguidelines-avoid-non-const-global-variables
متغیرهای سراسری غیر-const را همانطور که در بند I.2 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i2-avoid-non-const-global-variables از C++ Core Guidelines توصیف شده است پیدا میکند. از آنجا که قاعده R.6 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rr-global از C++ Core Guidelines تکرار قاعده I.2 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i2-avoid-non-const-global-variables است، این بررسی آن قاعده را نیز پوشش میدهد.
char a; // Warns!
const char b = 0;
namespace some_namespace
{
char c; // Warns!
const char d = 0;
}
char * c_ptr1 = &some_namespace::c; // Warns!
char *const c_const_ptr = &some_namespace::c; // Warns!
char & c_reference = some_namespace::c; // Warns!
class Foo // No Warnings inside Foo, only namespace scope is covered
{
public:
char e = 0;
const char f = 0;
protected:
char g = 0;
private:
char h = 0;
};
متغیرهای a، c، c_ptr1، c_const_ptr و c_reference همگی هشدار ایجاد خواهند کرد، زیرا یا یک متغیر غیر-const با دسترسی سراسری هستند، یا اشارهگر یا ارجاعی هستند که دسترسی سراسری به دادههای غیر-const را فراهم میکنند، و یا هر دو.
گزینهها
- AllowInternalLinkage
- هنگامی که روی true تنظیم شود، متغیرهای استاتیک غیر-const و متغیرهای موجود در فضاهای نام بینام (anonymous namespaces) هشداری ایجاد نخواهند کرد. مقدار پیشفرض false است.
- AllowThreadLocal
- هنگامی که روی true تنظیم شود، متغیرهای سراسری غیر-const با طول عمر ذخیرهسازی محلی-نخ (thread-local storage duration) هشداری تولید نمیکنند. مقدار پیشفرض false است.
cppcoreguidelines-avoid-reference-coroutine-parameters
زمانی که یک کوروتین (coroutine) پارامترهای ارجاعی میپذیرد هشدار میدهد. پس از نقطه تعلیق (suspend point) کوروتین، ارجاعها ممکن است معلق (dangling) شده و دیگر معتبر نباشند. در عوض، پارامترها را به صورت مقداری (by value) ارسال کنید.
مثالها:
std::future<int> someCoroutine(int& val) {
co_await ...;
// When the coroutine is resumed, 'val' might no longer be valid.
if (val) ...
}
این بررسی قاعده CP.53 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rcoro-reference-parameters از C++ Core Guidelines را پیادهسازی میکند.
cppcoreguidelines-c-copy-assignment-signature
بررسی cppcoreguidelines-c-copy-assignment-signature یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به misc-unconventional-assign-operator <> مراجعه کنید.
cppcoreguidelines-explicit-virtual-functions
بررسی cppcoreguidelines-explicit-virtual-functions یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به modernize-use-override <> مراجعه کنید.
cppcoreguidelines-init-variables
بررسی میکند که آیا متغیرهای محلی وجود دارند که بدون مقدار اولیه اعلان شده باشند. در صورتی که مسیر کدی وجود داشته باشد که متغیر را قبل از مقداردهی بخواند، این امر ممکن است منجر به رفتار غیرمنتظره شود.
این قاعده بخشی از نمایه ایمنی نوع (Type.5) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-init و ES.20 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-always از C++ Core Guidelines است.
تنها اعداد صحیح، بولیها، اعشاریهای float و double و اشارهگرها بررسی میشوند. گزینه اصلاح (fix) تمام مقادیر شناساییشده را با مقدار صفر مقداردهی اولیه میکند. یک استثنا انواع float و double هستند که با NaN مقداردهی اولیه میشوند.
به عنوان مثال تابعی که به این صورت است:
void function() {
int x;
char *txt;
double d;
// Rest of the function.
}
به این صورت بازنویسی میشود:
#include <math.h>
void function() {
int x = 0;
char *txt = nullptr;
double d = NAN;
// Rest of the function.
}
برای حالت enum مقداردهینشده هشدار میدهد، اما بدون FixIt:
enum A {A1, A2, A3};
enum A_c : char { A_c1, A_c2, A_c3 };
enum class B { B1, B2, B3 };
enum class B_i : int { B_i1, B_i2, B_i3 };
void function() {
A a; // Warning: variable 'a' is not initialized
A_c a_c; // Warning: variable 'a_c' is not initialized
B b; // Warning: variable 'b' is not initialized
B_i b_i; // Warning: variable 'b_i' is not initialized
}
گزینهها
- IncludeStyle
- رشتهای که شیوه درج include را مشخص میکند، llvm یا google. مقدار پیشفرض llvm است.
- MathHeader
- رشتهای که هدر لازم جهت دریافت تعریف NAN را مشخص میکند. پیشفرض <math.h> است.
cppcoreguidelines-interfaces-global-init
این بررسی مقداردهندههای متغیرهای سراسری (globals) را که به اشیای extern دسترسی دارند پرچمگذاری میکند، چرا که این موضوع میتواند به مشکل در ترتیب مقداردهی اولیه (order-of-initialization) منجر شود.
این بررسی بخش I.22 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-global-init از C++ Core Guidelines را پیادهسازی میکند.
توجه داشته باشید که در حال حاضر این بررسی، فراخوانیهای توابع غیر constexpr را پرچمگذاری نمیکند، بنابراین متغیرهای سراسری همچنان ممکن است از درون خود توابع قابل دسترسی باشند.
cppcoreguidelines-macro-to-enum
بررسی cppcoreguidelines-macro-to-enum یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر modernize-macro-to-enum <> را ببینید.
cppcoreguidelines-macro-usage
موارد استفاده از ماکروها را که به دلیل وجود ساختارهای زبانی بهتر برای انجام آن کار مشکلساز تلقی میشوند، پیدا میکند.
بخشهای مربوطه در C++ Core Guidelines عبارتند از ES.31 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es31-dont-use-macros-for-constants-or-functions و ES.32 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es32-use-all_caps-for-all-macro-names.
مثالها:
#define C 0 #define F1(x, y) ((a) > (b) ? (a) : (b)) #define F2(...) (__VA_ARGS__) #define F3(x, y) x##y #define COMMA , #define NORETURN [[noreturn]] #define DEPRECATED attribute((deprecated)) #if LIB_EXPORTS #define DLLEXPORTS __declspec(dllexport) #else #define DLLEXPORTS __declspec(dllimport) #endif
به هشدارهای زیر منجر میشود:
4 warnings generated.
test.cpp:1:9: warning: macro 'C' used to declare a constant; consider using a 'constexpr' constant [cppcoreguidelines-macro-usage]
#define C 0
^
test.cpp:2:9: warning: function-like macro 'F1' used; consider a 'constexpr' template function [cppcoreguidelines-macro-usage]
#define F1(x, y) ((a) > (b) ? (a) : (b))
^
test.cpp:3:9: warning: variadic macro 'F2' used; consider using a 'constexpr' variadic template function [cppcoreguidelines-macro-usage]
#define F2(...) (__VA_ARGS__)
^
گزینهها
- AllowedRegexp
- یک عبارت باقاعده (regular expression) برای فیلتر کردن ماکروهای مجاز. برای مثال DEBUG*|LIBTORRENT*|TORRENT*|UNI* را میتوان برای فیلتر کردن libtorrent اعمال کرد. مقدار پیشفرض ^DEBUG_* است.
- CheckCapsOnly
- پرچم بولی برای هشدار دادن روی تمام ماکروها به جز آنهایی که نامشان صرفاً با حروف بزرگ (CAPS_ONLY) است. هدف این گزینه، تسهیل ورود این بررسی به کدهای قدیمیتر است. مقدار پیشفرض false است.
- IgnoreCommandLineMacros
- پرچم بولی برای نادیده گرفتن ماکروهای تعریفشده در خط فرمان. مقدار پیشفرض true است.
cppcoreguidelines-misleading-capture-default-by-value
زمانی که یک لامبدا پیشفرض دریافت با مقدار (by-value) را مشخص کرده و this را ضبط میکند، هشدار میدهد.
دریافتهای پیشفرض با مقدار در توابع عضو میتواند درباره اینکه آیا اعضای دادهای با مقدار یا با ارجاع دریافت شدهاند گمراهکننده باشد. این امر به این دلیل رخ میدهد که مشخص کردن دریافت پیشفرض با مقدار [=] در عمل اشارهگر this را با مقدار دریافت میکند، نه خود اعضای دادهای را. در نتیجه، اعضای دادهای همچنان بهطور غیرمستقیم از طریق اشارهگر دریافتشدهٔ this در دسترس قرار میگیرند، که اساساً به این معنی است که با ارجاع مورد دسترسی قرار گرفتهاند. بنابراین، حتی هنگام استفاده از [=]، اعضای دادهای عملاً با ارجاع ضبط میشوند، که ممکن است با انتظارات کاربر همخوانی نداشته باشد.
مثالها:
struct AClass {
int member;
void misleadingLogic() {
int local = 0;
member = 0;
auto f = [=]() mutable {
local += 1;
member += 1;
};
f();
// Here, local is 0 but member is 1
}
void clearLogic() {
int local = 0;
member = 0;
auto f = [this, local]() mutable {
local += 1;
member += 1;
};
f();
// Here, local is 0 but member is 1
}
};
این بررسی بخش F.54 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f54-when-writing-a-lambda-that-captures-this-or-any-class-data-member-dont-use--default-capture از C++ Core Guidelines را پیادهسازی میکند.
cppcoreguidelines-missing-std-forward
زمانی که یک پارامتر ارجاع هدایتکننده (forwarding reference) در بدنه تابع فوروارد نشده باشد، هشدار میدهد.
مثال:
template <class T>
void wrapper(T&& t) {
impl(std::forward<T>(t), 1, 2); // Correct
}
template <class T>
void wrapper2(T&& t) {
impl(t, 1, 2); // Oops - should use std::forward<T>(t)
}
template <class T>
void wrapper3(T&& t) {
impl(std::move(t), 1, 2); // Also buggy - should use std::forward<T>(t)
}
template <class F>
void wrapper_function(F&& f) {
std::forward<F>(f)(1, 2); // Correct
}
template <class F>
void wrapper_function2(F&& f) {
f(1, 2); // Incorrect - may not invoke the desired qualified function operator
}
گزینهها
- ForwardFunction
- تابعی که برای فوروارد کردن استفاده میشود را تعیین میکند. پیشفرض ::std::forward است.
این بررسی، بخش F.19 http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-forward از C++ Core Guidelines را پیادهسازی میکند.
cppcoreguidelines-narrowing-conversions
این بررسی بخشی از ES.46 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es46-avoid-lossy-narrowing-truncating-arithmetic-conversions از C++ Core Guidelines را پیادهسازی میکند.
بررسی cppcoreguidelines-narrowing-conversions یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به bugprone-narrowing-conversions <> مراجعه کنید.
cppcoreguidelines-no-malloc
این بررسی مدیریت حافظه به سبک زبان C با استفاده از malloc()، realloc()، calloc() و free() را مدیریت میکند. درباره استفاده از آنها هشدار داده و سعی میکند استفاده از یک شیء RAII مناسب را پیشنهاد دهد. علاوه بر این، میتواند طوری پیکربندی شود که فهرستی تعیینشده توسط کاربر از توابع مورد استفاده برای مدیریت حافظه (مانند posix_memalign()) را بررسی کند.
این بررسی، بخش R.10 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rr-mallocfree از C++ Core Guidelines را پیادهسازی میکند.
هیچ تلاشی برای ارائه راهنماییهای تصحیح خودکار (fix-it hints) صورت نگرفته است، زیرا مدیریت دستی منابع را نمیتوان به سادگی و به صورت خودکار به RAII تبدیل کرد.
// Warns each of the following lines. // Containers like std::vector or std::string should be used. char* some_string = (char*) malloc(sizeof(char) * 20); char* some_string = (char*) realloc(sizeof(char) * 30); free(some_string); int* int_array = (int*) calloc(30, sizeof(int)); // Rather use a smartpointer or stack variable. struct some_struct* s = (struct some_struct*) malloc(sizeof(struct some_struct));
گزینهها
- Allocations
- فهرستی جداشده با نقطه ویرگول (semicolon) از نامهای کاملاً واجد شرایط (fully qualified) توابع تخصیص حافظه. پیشفرض ::malloc;::calloc است.
- Deallocations
- فهرستی جداشده با نقطه ویرگول از نامهای کاملاً واجد شرایط توابع آزادسازی حافظه. پیشفرض ::free است.
- Reallocations
- فهرستی جداشده با نقطه ویرگول از نامهای کاملاً واجد شرایط توابع تخصیص مجدد حافظه. پیشفرض ::realloc است.
cppcoreguidelines-no-suspend-with-lock
کوروتینهایی (coroutines) را نشانهگذاری میکند که در نقطه تعلیق، یک lock guard در دامنه (scope) آنها وجود دارد و به حالت تعلیق درمیآیند.
هنگامی که یک کوروتین معلق میشود، هر قفل یا mutex در اختیار آن تا زمان ازسرگیری مجدد کوروتین و نهایتاً تخریب lock guard، قفل باقی خواهد ماند. این امر میتواند منجر به دورههای طولانی نگهداشتن mutex شده و خطر قفل متقابل (deadlock) را به همراه داشته باشد.
در عوض، قفلها باید پیش از تعلیق یک کوروتین آزاد شوند.
این بررسی تنها تعلیق کوروتینها در شرایطی که lock_guard در دامنه وجود دارد را بررسی میکند؛ و قفلگذاری یا آزادسازی دستی mutexها را، به عنوان مثال از طریق فراخوانی std::mutex::lock()، در نظر نمیگیرد.
مثالها:
future bad_coro() {
std::lock_guard lock{mtx};
++some_counter;
co_await something(); // Suspending while holding a mutex
}
future good_coro() {
{
std::lock_guard lock{mtx};
++some_counter;
}
// Destroy the lock_guard to release the mutex before suspending the coroutine
co_await something(); // Suspending while holding a mutex
}
این بررسی، بخش CP.52 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rcoro-locks از C++ Core Guidelines را پیادهسازی میکند.
cppcoreguidelines-noexcept-destructor
این بررسی، بخش C.37 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c37-make-destructors-noexcept از C++ Core Guidelines را پیادهسازی میکند.
بررسی cppcoreguidelines-noexcept-destructor یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به performance-noexcept-destructor <> مراجعه کنید.
cppcoreguidelines-noexcept-move-operations
این بررسی، بخش C.66 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c66-make-move-operations-noexcept از C++ Core Guidelines را پیادهسازی میکند.
بررسی cppcoreguidelines-noexcept-move-operations یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به performance-noexcept-move-constructor <> مراجعه کنید.
cppcoreguidelines-noexcept-swap
این بررسی، بخشهای C.83 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c83-for-value-like-types-consider-providing-a-noexcept-swap-function ، C.84 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c84-a-swap-function-must-not-fail و C.85 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c85-make-swap-noexcept از C++ Core Guidelines را پیادهسازی میکند.
بررسی cppcoreguidelines-noexcept-swap check یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به performance-noexcept-swap <> مراجعه کنید.
cppcoreguidelines-non-private-member-variables-in-classes
بررسی cppcoreguidelines-non-private-member-variables-in-classes یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر به misc-non-private-member-variables-in-classes <> مراجعه کنید.
cppcoreguidelines-owning-memory
این بررسی، معناشناسی مبتنی بر نوع gsl::owner<T*> را پیادهسازی میکند که تحلیل ایستا را روی کدهایی که از اشارهگرهای خام برای مدیریت منابعی مانند حافظه پویا استفاده میکنند ولی مفاهیم RAII را به کار نمیگیرند، امکانپذیر میسازد.
این بررسی، بخشهای I.11 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i11-never-transfer-ownership-by-a-raw-pointer-t-or-reference-t، C.33 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c33-if-a-class-has-an-owning-pointer-member-define-a-destructor، R.3 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r3-a-raw-pointer-a-t-is-non-owning و GSL.Views https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-views از C++ Core Guidelines را پیادهسازی میکند. تعریف یک gsl::owner<T*> بسیار سرراست است
namespace gsl { template <typename T> owner = T; }
بنابراین، معرفی مالک حتی بدون استفاده از پیادهسازی Guideline Support Library https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#s-gsl نیز ساده است.
همه بررسیها کاملاً مبتنی بر نوع هستند و (هنوز) به جریان حساس (flow sensitive) نمیباشند.
مثالهای زیر مقداردهیهای اولیه درست و نادرست مالکها را نشان میدهند؛ با انتسابها نیز به همین روش برخورد میشود. توجه داشته باشید که هر دو توابع منابع new و توابع مشابه malloc()، به عنوان تولیدکننده منبع در نظر گرفته میشوند.
// Creating an owner with factory functions is checked.
gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
// Dynamic memory must be assigned to an owner
int* Something = new int(42); // BAD, will be caught
gsl::owner<int*> Owner = new int(42); // Good
gsl::owner<int*> Owner = new int[42]; // Good as well
// Returned owner must be assigned to an owner
int* Something = function_that_returns_owner(); // Bad, factory function
gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner
// Something not a resource or owner should not be assigned to owners
int Stack = 42;
gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned
در مورد حافظه پویا به عنوان منبع، تنها متغیرهای gsl::owner<T*> مجاز به حذف (delete) شدن هستند.
// Example Bad, non-owner as resource handle, will be caught. int* NonOwner = new int(42); // First warning here, since new must land in an owner delete NonOwner; // Second warning here, since only owners are allowed to be deleted // Example Good, Ownership correctly stated gsl::owner<int*> Owner = new int(42); // Good delete Owner; // Good as well, statically enforced, that only owners get deleted
علاوه بر این، بررسی تضمین میکند توابعی که یک gsl::owner<T*> را به عنوان شناسه انتظار دارند، یا با یک gsl::owner<T*> یا با یک منبع تازهساختهشده فراخوانی شوند.
void expects_owner(gsl::owner<int*> o) { delete o; }
// Bad Code
int NonOwner = 42;
expects_owner(&NonOwner); // Bad, will get caught
// Good Code
gsl::owner<int*> Owner = new int(42);
expects_owner(Owner); // Good
expects_owner(new int(42)); // Good as well, recognized created resource
// Port legacy code for better resource-safety
gsl::owner<FILE*> File = fopen("my_file.txt", "rw+");
FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned
// ... use the file
fclose(File); // Ok, File is annotated as 'owner<>'
fclose(BadFile); // BadFile is not an 'owner<>', will be warned
گزینهها
- LegacyResourceProducers
- فهرستی جداشده با نقطه-ویرگول (semicolon) از نامهای کامل توابع قدیمی (legacy) که منابع را ایجاد میکنند اما نمیتوانند gsl::owner<> را معرفی کنند. پیشفرض: ::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile.
- LegacyResourceConsumers
- فهرستی جداشده با نقطه-ویرگول از نامهای کامل توابع قدیمی که مالکان منابع را به عنوان آرگومانهای اشارهگر میپذیرند اما نمیتوانند gsl::owner<> را معرفی کنند. پیشفرض: ::free;::realloc;::freopen;::fclose.
محدودیتها
استفاده از gsl::owner<T*> در یک typedef یا alias به درستی مدیریت نمیشود.
using heap_int = gsl::owner<int*>; heap_int allocated = new int(42); // False positive!
مورد gsl::owner<T*> به عنوان یک نام مستعار نوع قالببندیشده (templated type alias) اعلان شده است. در توابع و کلاسهای قالب (template)، مانند مثال زیر، اطلاعات نامهای مستعار نوع از بین میرود. بنابراین، استفاده از gsl::owner<T*> در یک پایگاه کد به شدت مبتنی بر قالب ممکن است به مثبت کاذب (false positive) منجر شود.
ساختارهای کدی شناختهشدهای که به درستی عیبیابی نمیشوند عبارتند از:
- std::exchange
- std::vector<gsl::owner<T*>>
// This template function works as expected. Type information doesn't get lost.
template <typename T>
void delete_owner(gsl::owner<T*> owned_object) {
delete owned_object; // Everything alright
}
gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
// Type deduction does not work for auto variables.
// This is caught by the check and will be noted accordingly.
auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int*
// Problematic function template that looses the typeinformation on owner
template <typename T>
void bad_template_function(T some_object) {
// This line will trigger the warning, that a non-owner is assigned to an owner
gsl::owner<T*> new_owner = some_object;
}
// Calling the function with an owner still yields a false positive.
bad_template_function(gsl::owner<int*>(new int(42)));
// The same issue occurs with templated classes like the following.
template <typename T>
class OwnedValue {
public:
const T getValue() const { return _val; }
private:
T _val;
};
// Code, that yields a false positive.
OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int *
// False positive, getValue returns int* and not gsl::owner<int*>
gsl::owner<int*> OwnedInt = Owner.getValue();
محدودیت دیگر پیادهسازی فعلی، بررسیِ صرفاً مبتنی بر نوع است. فرض کنید کدی شبیه به زیر دارید:
// Two owners with assigned resources gsl::owner<int*> Owner1 = new int(42); gsl::owner<int*> Owner2 = new int(42); Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2! Owner1 = nullptr;
معناشناسی یک gsl::owner<T*> عمدتاً شبیه به یک std::unique_ptr<T> است، بنابراین انتساب دو gsl::owner<T*> به عنوان یک انتقال (move) در نظر گرفته میشود، که مستلزم آن است منبع Owner2 باید قبل از انتساب آزاد شده باشد. این نوع شرایط میتواند در بهبودهای آینده این بررسی با تحلیل حساس به جریان (flow-sensitive analysis) شناسایی شود. در حال حاضر، ابزار Clang Static Analyzer این باگ را برای حافظه پویا شناسایی میکند، اما نه برای انواع عمومی منابع.
cppcoreguidelines-prefer-member-initializer
مقداردهیهای اولیه اعضا را در بدنه سازنده پیدا میکند که میتوانند به جای آن به مقداردهندههای اولیه عضو (member initializers) سازنده تبدیل شوند. این کار نه تنها خوانایی کد را بهبود میبخشد، بلکه بر کارایی و عملکرد آن نیز تأثیر مثبتی دارد. انتسابهای اعضای کلاس در داخل یک عبارت کنترلی یا پس از اولین عبارت کنترلی نادیده گرفته میشوند.
این بررسی قاعده C.49 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c49-prefer-initialization-to-assignment-in-constructors از C++ Core Guidelines را پیادهسازی میکند.
لطفاً توجه داشته باشید که این بررسی قاعده C.48 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c48-prefer-in-class-initializers-to-member-initializers-in-constructors-for-constant-initializers از C++ Core Guidelines را اعمال نمیکند. برای این منظور، بررسی modernize-use-default-member-init <> را ببینید.
مثال ۱
class C {
int n;
int m;
public:
C() {
n = 1; // Literal in default constructor
if (dice())
return;
m = 1;
}
};
در اینجا n بر خلاف m میتواند در فهرست مقداردهنده اولیه سازنده مقداردهی شود، زیرا مقداردهی اولیه m پس از یک عبارت کنترلی (if) قرار دارد:
class C {
int n;
int m;
public:
C(): n(1) {
if (dice())
return;
m = 1;
}
};
مثال ۲
class C {
int n;
int m;
public:
C(int nn, int mm) {
n = nn; // Neither default constructor nor literal
if (dice())
return;
m = mm;
}
};
در اینجا برخلاف m، میتوان n را در فهرست مقداردهی اولیهٔ سازنده مقداردهی کرد، زیرا مقداردهی اولیهٔ m پس از یک دستور کنترلی (if) قرار دارد:
C(int nn, int mm) : n(nn) {
if (dice())
return;
m = mm;
}
cppcoreguidelines-pro-bounds-array-to-pointer-decay
این بررسی تمام زوالهای آرایه به اشارهگر (decay) را پرچمگذاری میکند.
اشارهگرها نباید به عنوان آرایه استفاده شوند. ساختار span<T> یک جایگزین امن و با بررسی محدوده (bounds-checked) برای استفاده از اشارهگرها جهت دسترسی به آرایهها است.
این قاعده بخشی از نمایهٔ ایمنی محدوده (Bounds 3) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-bounds-decay از C++ Core Guidelines است.
cppcoreguidelines-pro-bounds-avoid-unchecked-container-access
فراخوانیهای operator[] را در نگهدارندههای STL پیدا کرده و جایگزینی آنها با گزینههای امن را پیشنهاد میکند. جایگزینهای امن شامل توابع at از STL یا at از GSL، توابع begin() یا end()، حلقههای range-for، std::span، یا یک تابع مناسب از <algorithms> هستند.
برای مثال، هر دوی
std::vector<int> a; int b = a[4];
و
std::unique_ptr<vector> a; int b = a[0];
یک هشدار ایجاد خواهند کرد.
نگهدارندههای STL که در آنها operator[] برای همهٔ ورودیها خوشتعریف است، از این بررسی مستثنی هستند (مانند: std::map::operator[]).
این بررسی بخشی از رهنمود SL.con.3 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#slcon3-avoid-bounds-errors را اعمال میکند و بخشی از نمایهٔ ایمنی محدوده (Bounds 4) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-bounds-arrayindex از C++ Core Guidelines است.
گزینهها
- ExcludeClasses
- فهرستی جداشده با نقطه-ویرگول از عبارات باقاعده (regex) مطابق با نام کلاسها که فهرست استثناهای پیشفرض را بازنویسی میکند. مقدار پیشفرض عبارت است از: ::std::map;::std::unordered_map;::std::flat_map.
- FixMode
- مشخص میکند چه اصلاحاتی پیشنهاد شوند. یکی از مقادیر none، at (استفاده از a.at(index) در صورت وجود تابعی مناسب) یا function (استفاده از یک تابع f(a, index)). مقدار پیشفرض none است.
- FixFunction
- تابعی که باید در حالت function استفاده شود. برای C++23 و پس از آن، تابع ارائهشده باید از عملگر زیرنویس خالی پشتیبانی کند، یعنی حالتی که a[] تبدیل به f(a) میشود. از FixFunctionEmptyArgs میتوان برای بازنویسی تابع پیشنهادی در آن حالت استفاده کرد. مقدار پیشفرض gsl::at است.
- FixFunctionEmptyArgs
- تابعی که باید در حالت function فقط برای عملگر زیرنویس خالی در C++23 و پس از آن استفاده شود. اگر نباید هیچ اصلاحی برای عملگرهای زیرنویس خالی انجام شود، یک رشتهٔ خالی ارسال کنید. در این حالت، تنها هشدارها چاپ خواهند شد. مقدار پیشفرض، مقدار FixFunction است.
cppcoreguidelines-pro-bounds-constant-array-index
این بررسی تمام عبارات زیرنویس آرایه روی آرایههای ایستا و std::arrayها را که فاقد اندیس از نوع عبارت عدد صحیح ثابت هستند یا از محدوده خارجاند (برای std::array) پرچمگذاری میکند. برای بررسی خروج از محدوده در آرایههای ایستا، به عیبیابی -Warray-bounds در Clang مراجعه کنید.
این قاعده بخشی از نمایهٔ ایمنی محدوده (Bounds 2) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-bounds-arrayindex از C++ Core Guidelines است.
به صورت اختیاری، این بررسی میتواند اصلاحاتی را با استفاده از gsl::at برای اندیسگذاری تولید کند.
گزینهها
- GslHeader
- این بررسی میتواند پس از تنظیم این گزینه روی نام فایل include حاوی gsl::at() (مانند "gsl/gsl.h")، اصلاحات را تولید کند. مقدار پیشفرض یک رشتهٔ خالی است.
- IncludeStyle
- رشتهای که سبک include مورد استفاده را مشخص میکند: llvm یا google. مقدار پیشفرض llvm است.
cppcoreguidelines-pro-bounds-pointer-arithmetic
این بررسی تمام موارد استفاده از محاسبات اشارهگر (pointer arithmetic) را پرچمگذاری میکند، زیرا میتواند منجر به اشارهگر نامعتبر شود. تفریق دو اشارهگر توسط این بررسی پرچمگذاری نمیشود.
اشارهگرها فقط باید به اشیاء منفرد ارجاع دهند، و محاسبات اشارهگر شکننده بوده و خطایابی آن دشوار است. span<T> یک نوع دادهٔ امن و دارای بررسی محدوده برای دسترسی به آرایههای داده است.
این قاعده بخشی از نمایهٔ ایمنی محدوده (Bounds 1) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-bounds-arithmetic از C++ Core Guidelines است.
گزینهها
- AllowIncrementDecrementOperators
- در صورت فعال بودن، این بررسی استفاده از عملگرهای افزایش یا کاهش پیشوندی/پسوندی را روی اشارهگرها مجاز میداند. مقدار پیشفرض false است.
cppcoreguidelines-pro-type-const-cast
محدودیتهایی را بر استفاده از const_cast در کدهای C++ اعمال میکند. این بررسی به تنظیم گزینهٔ StrictMode <#cmdoption-arg-StrictMode> وابسته است تا مشخص کند که آیا باید همهٔ موارد استفاده از const_cast را علامتگذاری کند یا فقط مواردی که توصیفکنندهٔ const یا volatile را حذف میکنند.
تغییر دادن متغیری که در C++ بهصورت const اعلان شده است عموماً رفتاری تعریفنشده (undefined behavior) محسوب میشود و این موضوع حتی هنگام استفاده از const_cast نیز صادق است. در C++، توصیفکنندهٔ const نشان میدهد که متغیر فقطخواندنی است و کامپایلر با جلوگیری از هرگونه تلاش برای تغییر مقدار آن متغیر، این قاعده را اعمال میکند.
حذف توصیفکنندهٔ volatile در C++ میتواند پیامدهای جدی داشته باشد. این توصیفکننده نشان میدهد که مقدار یک متغیر میتواند بهطور غیرقابلپیشبینی تغییر کند، و حذف آن ممکن است منجر به رفتار تعریفنشده، مشکلات بهینهسازی و چالشهای اشکالزدایی شود. حفظ توصیفکنندهٔ volatile در شرایطی که فرار بودن متغیر بخش حیاتی درستی و قابلیت اطمینان برنامه است، ضروری میباشد.
این قاعده بخشی از نمایهٔ Type safety (Type 3) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-constcast و قاعدهٔ ES.50: Don’t cast away const https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es50-dont-cast-away-const از C++ Core Guidelines است.
گزینهها
- StrictMode
- هنگامی که این تنظیم روی true باشد، به این معنی است که هیچ استفادهای از const_cast مجاز نیست. از سوی دیگر، وقتی روی false تنظیم شود، تبدیل نوع به انواع const یا volatile را مجاز میداند. مقدار پیشفرض false است.
cppcoreguidelines-pro-type-cstyle-cast
این بررسی تمام استفادهها از تبدیلهای نوع به سبک C (C-style casts) را که یک تبدیل به پایینِ (downcast) static_cast، یا const_cast، یا reinterpret_cast انجام میدهند علامتگذاری میکند.
استفاده از این تبدیلها میتواند ایمنی نوع (type safety) را نقض کرده و باعث شود برنامه به متغیری که در واقع از نوع X است، بهگونهای دسترسی پیدا کند که گویی از نوع نامرتبط Z است. توجه داشته باشید که یک تبدیل به سبک C بهصورت (T)expression به معنای انجام اولین تبدیل ممکن از موارد زیر است: یک const_cast، یک static_cast، یک static_cast که به دنبال آن یک const_cast آمده باشد، یک reinterpret_cast، یا یک reinterpret_cast که به دنبال آن یک const_cast آمده باشد. این قاعده (T)expression را تنها زمانی که برای انجام یک تبدیل ناامن استفاده شود منع میکند.
این قاعده بخشی از نمایهٔ Type safety (Type.4) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-cstylecast از C++ Core Guidelines است.
cppcoreguidelines-pro-type-member-init
این بررسی تعاریف سازندههای ارائهشده توسط کاربر را که تمام فیلدهایی که با مقداردهی اولیهٔ پیشفرض در حالت تعریفنشده باقی میمانند (مانند انواع داخلی، اشارهگرها و انواع رکوردی فاقد سازندهٔ پیشفرض ارائهشده توسط کاربر که حداقل شامل یکی از این انواع هستند) مقداردهی نمیکنند، علامتگذاری میکند. اگر این فیلدها مقداردهی نشوند، سازنده بخشی از حافظه را در وضعیتی تعریفنشده باقی میگذارد.
برای C++11، این بررسی اصلاحاتی جهت افزودن مقداردهندههای درونکلاسی فیلدها پیشنهاد میدهد. برای نسخههای قدیمیتر، مقداردهندههای فیلد را در لیست مقداردهی اولیهٔ سازنده (initializer list) درج میکند. همچنین هر کلاس پایهٔ مستقیمی را که باید صفر شود در لیست مقداردهی اولیهٔ سازنده مقداردهی میکند.
این بررسی انتساب فیلدها درون بدنهٔ سازنده را در نظر میگیرد، اما برای فیلدهایی که درون متدهای فراخوانیشده در بدنهٔ سازنده مقداردهی میشوند، هشدارهای نادرست (false positive) تولید میکند.
این بررسی همچنین متغیرهای با طول عمر ذخیرهسازی خودکار را که انواع رکوردی بدون سازندهٔ ارائهشده توسط کاربر دارند و مقداردهی اولیه نشدهاند علامتگذاری میکند. اصلاح پیشنهادی، مقداردهی اولیه با صفر از طریق {} برای C++11 به بعد، یا = {} برای نسخههای قدیمیتر زبان است.
گزینهها
- IgnoreArrays
- در صورت تنظیم روی true، این بررسی دربارهٔ اعضای آرایهای که در طول ساخت با صفر مقداردهی اولیه نشدهاند هشداری نمیدهد. برای کدهایی که کارایی در آنها حیاتی است، عدم مقداردهی اولیهٔ اعضای آرایهای با اندازهٔ ثابت میتواند مهم باشد. مقدار پیشفرض false است.
- UseAssignment
- در صورت تنظیم روی true، این بررسی اصلاحات را با مقداردهندههای لیتورال ( int i = 0; ) به جای آکولادها ( int i{}; ) ارائه میدهد. مقدار پیشفرض false است.
این قاعده بخشی از نمایهٔ Type safety (Type.6) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-memberinit از C++ Core Guidelines است.
cppcoreguidelines-pro-type-reinterpret-cast
این بررسی تمام موارد استفاده از reinterpret_cast را در کدهای C++ علامتگذاری میکند.
استفاده از این تبدیلها میتواند ایمنی نوع را نقض کرده و باعث شود برنامه به متغیری که در واقع از نوع X است، بهگونهای دسترسی پیدا کند که گویی از نوع نامرتبط Z است.
این قاعده بخشی از نمایهٔ Type safety (Type.1.1) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-reinterpretcast از C++ Core Guidelines است.
cppcoreguidelines-pro-type-static-cast-downcast
این بررسی تمام موارد استفاده از static_cast را که در آن یک کلاس پایه به یک کلاس مشتقشده تبدیل میشود، علامتگذاری میکند. در این موارد، یک راهکار اصلاحی جهت تبدیل آن به dynamic_cast ارائه میشود.
استفاده از این تبدیلها میتواند ایمنی نوع را نقض کرده و باعث شود برنامه به متغیری که در واقع از نوع X است، بهگونهای دسترسی پیدا کند که گویی از نوع نامرتبط Z است.
این قاعده بخشی از نمایهٔ Type safety (Type.2) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-downcast از C++ Core Guidelines است.
گزینهها
- StrictMode
- هنگامی که روی false تنظیم شود، هیچ هشداری برای تبدیلهای روی انواع غیرچندریختی (non-polymorphic) صادر نمیشود. مقدار پیشفرض true است.
cppcoreguidelines-pro-type-union-access
این بررسی تمام دسترسیها به اعضای unionها را علامتگذاری میکند. ارسال unionها بهطور کلی و یکپارچه علامتگذاری نمیشود.
خواندن از یک عضو union فرض را بر این میگذارد که آن عضو آخرین عضوی بوده که در آن نوشته شده است، و نوشتن در یک عضو union فرض را بر این میگذارد که عضوی دیگر با یک مخرب غیربدیهی (nontrivial destructor)، مخربش فراخوانی شده است. این رفتار شکننده است زیرا عموماً نمیتوان در زبان امنیت آن را تضمین کرد و بنابراین برای درست انجام شدن به انضباط برنامهنویس متکی است.
این قاعده بخشی از نمایهٔ Type safety (Type.7) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-unions از C++ Core Guidelines است.
cppcoreguidelines-pro-type-vararg
این بررسی تمام فراخوانیهای توابع با تعداد آرگومان متغیر به سبک C (vararg) و تمام استفادهها از va_arg را علامتگذاری میکند.
جهت امکانپذیر ساختن استفاده از توابع vararg در SFINAE، اگر یک لیترال 0 به عنوان تنها آرگومان vararg ارسال شود یا تابع در یک بافت ارزیابینشده (unevaluated context) استفاده شود، فراخوانی علامتگذاری نمیگردد.
ارسال به vararg فرض میکند که نوع صحیح خوانده خواهد شد. این رفتار شکننده است زیرا عموماً نمیتوان در زبان ایمنی آن را تضمین کرد و بنابراین برای عملکرد صحیح به انضباط برنامهنویس متکی است.
این قاعده بخشی از نمایهٔ Type safety (Type.8) https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#pro-type-varargs از C++ Core Guidelines است.
cppcoreguidelines-rvalue-reference-param-not-moved
زمانی که یک پارامتر تابع از نوع ارجاع rvalue هرگز در بدنهٔ تابع منتقل (move) نشود، هشدار میدهد.
پارامترهای ارجاع rvalue نشاندهندهٔ پارامتری هستند که باید با std::move از داخل بدنهٔ تابع منتقل شوند. هرگونه پارامتر از این دست که هرگز منتقل نشود، گمراهکننده بوده و میتواند نشاندهندهٔ خطای برنامهنویسی باشد.
مثال:
void logic(std::string&& Input) {
std::string Copy(Input); // Oops - forgot to std::move
}
توجه داشته باشید پارامترهایی که استفاده نشدهاند و به عنوان استفادهنشده علامتگذاری شدهاند، مورد عیبیابی قرار نمیگیرند.
مثال:
void conditional_use([[maybe_unused]] std::string&& Input) {
// No diagnostic here since Input is unused and marked as such
}
گزینهها
// 'p' is flagged by this check if and only if AllowPartialMove is false
void move_members_of(pair<Obj, Obj>&& p) {
pair<Obj, Obj> other;
other.first = std::move(p.first);
other.second = std::move(p.second);
}
// 'p' is never flagged by this check
void move_whole_pair(pair<Obj, Obj>&& p) {
pair<Obj, Obj> other = std::move(p);
}
- IgnoreUnnamedParams
- اگر بر روی true تنظیم شود، این بررسی پارامترهای مرجع rvalue بینام را نادیده میگیرد. پیشفرض false است.
template <class T>
struct SomeClass {
// Below, 'T' is not deduced and 'T&&' is an rvalue reference type.
// This will be flagged if and only if IgnoreNonDeducedTemplateTypes is
// false. One suggested fix would be to specialize the class for 'T' and
// 'T&' separately (e.g., see std::future), or allow only one of 'T' or
// 'T&' instantiations of SomeClass (e.g., see std::optional).
SomeClass(T&& t) { }
};
// Never flagged, since 'T' is a forwarding reference in a deduced context
template <class T>
void forwarding_ref(T&& t) {
T other = std::forward<T>(t);
}
- MoveFunction
- تابعی که برای انتقال (move) استفاده میشود را مشخص میکند. پیشفرض ::std::move است.
این بررسی قاعده F.18 را از C++ Core Guidelines پیادهسازی میکند: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f18-for-will-move-from-parameters-pass-by-x-and-stdmove-the-parameter
cppcoreguidelines-slicing
برش (slicing) متغیرهای عضو یا vtable را علامتگذاری میکند. برش زمانی رخ میدهد که یک شیء مشتقشده در یک شیء پایه کپی شود: اعضای شیء مشتقشده (هم متغیرهای عضو و هم توابع عضو مجازی) نادیده گرفته و دور ریخته میشوند. این موضوع به ویژه برای برش توابع عضو میتواند گمراهکننده باشد، به عنوان مثال:
struct B { int a; virtual int f(); };
struct D : B { int b; int f() override; };
void use(B b) { // Missing reference, intended?
b.f(); // Calls B::f.
}
D d;
use(d); // Slice.
این بررسی قواعد ES.63 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es63-dont-slice و C.145 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c145-access-polymorphic-objects-through-pointers-and-references را از C++ Core Guidelines پیادهسازی میکند.
cppcoreguidelines-special-member-functions
این بررسی کلاسهایی را که در آنها تعدادی از توابع عضو ویژه تعریف شدهاند اما نه همه آنها، پیدا میکند.
به طور پیشفرض، کامپایلر یک سازنده کپی، عملگر انتساب کپی، سازنده جابهجایی، عملگر انتساب جابهجایی و ویرانگر را تعریف میکند. این رفتار پیشفرض را میتوان با تعاریف صریح کاربر لغو کرد. رابطه میان اینکه کدام توابع با تعریف توابع دیگر لغو میشوند پیچیده است و توصیه میشود که هر پنج مورد یا به صورت پیشفرض (default) قرار گیرند یا به صورت صریح تعریف شوند.
توجه داشته باشید که تعریف یک تابع با عبارت = delete به عنوان یک تعریف در نظر گرفته میشود.
این بررسی قاعده C.21 را از C++ Core Guidelines پیادهسازی میکند: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-five
گزینهها
- AllowSoleDefaultDtor
- هنگامی که بر روی true تنظیم شود (پیشفرض false است)، این بررسی تنها زمانی روی ویرانگرها فعال میشود که آنها تعریف شده باشند و از نوع پیشفرض (default) نباشند.
struct A { // This is fine.
virtual ~A() = default;
};
struct B { // This is not fine.
~B() {}
};
struct C {
// This is not checked, because the destructor might be defaulted in
// another translation unit.
~C();
};
- AllowMissingMoveFunctions
- هنگامی که بر روی true تنظیم شود (پیشفرض false است)، این بررسی کلاسهایی را که هیچ عملیات جابهجایی در آنها تعریف نشده علامتگذاری نمیکند. با این حال همچنان کلاسهایی را که فقط یکی از سازنده جابهجایی یا عملگر انتساب جابهجایی را تعریف کردهاند علامتگذاری میکند. با فعال بودن این گزینه، کلاس زیر علامتگذاری نخواهد شد:
struct A {
A(const A&);
A& operator=(const A&);
~A();
};
- AllowMissingMoveFunctionsWhenCopyIsDeleted
- هنگامی که بر روی true تنظیم شود (پیشفرض false است)، این بررسی کلاسهایی را که عملیات کپی در آنها حذف شده (delete) ولی عملیات جابهجایی را تعریف نکردهاند علامتگذاری نمیکند. این پرچم مرتبط با راهنمای سبک کدنویسی سیپلاسپلاس گوگل بخش Copyable and Movable Types است https://google.github.io/styleguide/cppguide.html#Copyable_Movable_Types. با فعال بودن این گزینه، کلاس زیر علامتگذاری نخواهد شد:
struct A {
A(const A&) = delete;
A& operator=(const A&) = delete;
~A();
};
- AllowImplicitlyDeletedCopyOrMove
- هنگامی که بر روی true تنظیم شود (پیشفرض false است)، این بررسی کلاسهایی را که عملیات کپی یا جابهجایی در آنها به طور ضمنی حذف شدهاند علامتگذاری نمیکند. با فعال بودن این گزینه، کلاس زیر علامتگذاری نخواهد شد:
struct A : boost::noncopyable {
~A() { std::cout << "dtor\n"; }
};
- IgnoreMacros
- اگر بر روی true تنظیم شود، این بررسی هشداری برای کلاسهای تعریفشده درون ماکروها صادر نمیکند. پیشفرض true است.
cppcoreguidelines-use-default-member-init
این بررسی، بند C.48 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-in-class-initializer از C++ Core Guidelines را پیادهسازی میکند.
بررسی cppcoreguidelines-use-default-member-init یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر modernize-use-default-member-init <> را ببینید.
cppcoreguidelines-use-enum-class
اعلانهای enum با محدوده سراسری (غیر کلاسی یا unscoped) را پیدا کرده و پیشنهاد میدهد به جای آن از enum class استفاده شود.
این بررسی، بند Enum.3 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#renum-class از C++ Core Guidelines را پیادهسازی میکند.
مثال:
enum E {}; // use "enum class E {};" instead
enum class E {}; // OK
struct S {
enum E {}; // use "enum class E {};" instead
// OK with option IgnoreUnscopedEnumsInClasses
};
namespace N {
enum E {}; // use "enum class E {};" instead
}
گزینهها
- IgnoreUnscopedEnumsInClasses
- در صورت فعال بودن (true)، اعلانهای enum فاقد محدوده را در کلاسها نادیده میگیرد. پیشفرض false است.
cppcoreguidelines-virtual-class-destructor
کلاسهای مجاز را که مخرب آنها نه عمومی و مجازی (“public and virtual”) است و نه حفاظتشده و غیرمجازی (“protected and non-virtual”)، پیدا میکند. مخرب یک کلاس مجازی باید به یکی از این دو روش تعریف شود تا از رفتار تعریفنشده (undefined behavior) جلوگیری شود.
این بررسی، بند C.35 http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-dtor-virtual از C++ Core Guidelines را پیادهسازی میکند.
توجه داشته باشید که این بررسی، کلاسی را که متد مجازی دارد، بدون در نظر گرفتن اینکه به عنوان کلاس پایه استفاده شده است یا نه، عیبیابی میکند.
اصلاحات خودکار برای مخربهای اعلانشده توسط کاربر یا ضمنی که عمومی و غیرمجازی، یا حفاظتشده و مجازی هستند، در دسترس است. هیچ اصلاحی برای مخربهای خصوصی (private) ارائه نمیشود؛ چرا که تصمیمگیری درباره خصوصی و مجازی بودن آنها، یا حفاظتشده و غیرمجازی بودنشان، به کاربرد مد نظر وابسته است و بنابراین به کاربر واگذار شده است.
مثال
برای مثال، کلاسها/ساختارهای زیر توسط این بررسی پرچمگذاری میشوند، زیرا دستورالعمل C.35 را نقض میکنند:
struct Foo { // NOK, protected destructor should not be virtual
virtual void f();
protected:
virtual ~Foo(){}
};
class Bar { // NOK, public destructor should be virtual
virtual void f();
public:
~Bar(){}
};
این کد میتواند به شکل زیر بازنویسی شود:
struct Foo { // OK, destructor is not virtual anymore
virtual void f();
protected:
~Foo(){}
};
class Bar { // OK, destructor is now virtual
virtual void f();
public:
virtual ~Bar(){}
};
darwin-avoid-spinlock
موارد استفاده از OSSpinlock را پیدا میکند؛ این ساختار به دلیل مشکلات بالقوه قفل فعال (livelock) منسوخ شده است.
این بررسی، فراخوانیهای توابع زیر را شناسایی میکند:
- OSSpinlockLock
- OSSpinlockTry
- OSSpinlockUnlock
اطلاعات مرتبط در مورد مشکل OSSpinlock: https://blog.postmates.com/why-spinlocks-are-bad-on-ios-b69fc5221058
darwin-dispatch-once-nonstatic
اعلانهای متغیرهای dispatch_once_t را که فاقد فضای ذخیرهسازی ایستا (static) یا سراسری هستند، پیدا میکند. رفتار استفاده از گزارههای dispatch_once_t با حافظه خودکار (automatic) یا پویا، توسط libdispatch تعریفنشده است و باید از آن اجتناب شود.
یک الگوی رایج این است که توابع، دادههای ایستای داخلی یا سراسری را یکبار در هنگام اجرای تابع مقداردهی اولیه کنند؛ اما دیده شده است که برنامهنویسان واژه static را برای گزاره dispatch_once_t فراموش میکنند، که این امر منجر به مقدار پرچم مقداردهینشده در پشته (stack) میشود.
همچنین برنامهنویسان متغیرهای dispatch_once_t را به عنوان اعضای ساختارها یا کلاسها تعریف کردهاند، با این هدف که مقداردهی اولیه پرهزینه عضو کلاس را به صورت تنبل (lazy) تنها یکبار انجام دهند؛ با این حال، این کار پیشنیازهای libdispatch را نقض میکند.
برای اطلاعات بیشتر، بخش بحث در مستندات dispatch_once شرکت اپل https://developer.apple.com/documentation/dispatch/1447169-dispatch_once را ببینید.
fuchsia-default-arguments-calls
در صورتی که یک تابع یا متد با آرگومانهای پیشفرض فراخوانی شود، هشدار میدهد.
به عنوان مثال، با فرض وجود این اعلان:
int foo(int value = 5) { return value; }
یک عبارت فراخوانی تابع که از یک آرگومان پیشفرض استفاده میکند، عیبیابی خواهد شد. فراخوانی آن بدون مقادیر پیشفرض هشداری ایجاد نمیکند:
foo(); // warning foo(0); // no warning
ویژگیهای غیرمجاز در Fuchsia را در https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en مشاهده کنید.
fuchsia-default-arguments-declarations
اگر یک تابع یا متد با پارامترهای پیشفرض اعلان شده باشد، هشدار میدهد.
برای نمونه، اعلان زیر:
int foo(int value = 5) { return value; }
باعث ایجاد یک هشدار خواهد شد.
ویژگیهای غیرمجاز در Fuchsia را در این نشانی ببینید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
fuchsia-header-anon-namespaces
بررسی fuchsia-header-anon-namespaces یک نام مستعار (alias) است؛ لطفاً برای اطلاعات بیشتر به misc-anonymous-namespace-in-header <> مراجعه کنید.
fuchsia-multiple-inheritance
بررسی fuchsia-multiple-inheritance یک نام مستعار است؛ لطفاً برای جزئیات به misc-multiple-inheritance <> مراجعه کنید.
ویژگیهای غیرمجاز در Fuchsia را در این نشانی ببینید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
fuchsia-overloaded-operator
اگر یک عملگر سرریزشده (overload شده) باشد، بهجز عملگرهای انتساب (کپی و جابهجایی)، هشدار میدهد.
برای نمونه:
int operator+(int); // Warning B &operator=(const B &Other); // No warning B &operator=(B &&Other) // No warning
ویژگیهای غیرمجاز در Fuchsia را در این نشانی ببینید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
fuchsia-statically-constructed-objects
اگر اشیاء سراسری و غیرساده (non-trivial) با فضای ذخیرهسازی ایستا (static storage) ساخته شوند، هشدار میدهد، مگر اینکه شیء بهطور ایستا با یک سازندهٔ constexpr مقداردهی اولیه شود یا هیچ سازندهٔ صریحی نداشته باشد.
برای نمونه:
class A {};
class B {
public:
B(int Val) : Val(Val) {}
private:
int Val;
};
class C {
public:
constexpr C(int Val) : Val(Val) {}
C(int Val1, int Val2) : Val(Val1+Val2) {}
private:
int Val;
};
static A a; // No warning, as there is no explicit constructor
static C c(0); // No warning, as constructor is constexpr
static B b(0); // Warning, as constructor is not constexpr
static C c2(0, 1); // Warning, as constructor is not constexpr
static int i; // No warning, as it is trivial
extern int get_i();
static C c3(get_i());// Warning, as the constructor is dynamically initialized
ویژگیهای غیرمجاز در Fuchsia را در این نشانی ببینید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
fuchsia-temporary-objects
هنگام ساخت اشیاء موقت مشخصی در هستهٔ Zircon هشدار میدهد. اگر شیء باید نشانهگذاری شود، نام نوع بهطور کامل واجد شرایط (fully qualified) باید بهطور صریح به این بررسی ارسال شود.
برای نمونه، با فرض فهرست کلاسهای "Foo" و "NS::Bar"، تمامی موارد زیر باعث ایجاد هشدار میشوند:
Foo();
Foo F = Foo();
func(Foo());
namespace NS {
Bar();
}
با همان فهرست، موارد زیر هشداری ایجاد نخواهند کرد:
Foo F; // Non-temporary construction okay Foo F(param); // Non-temporary construction okay Foo *F = new Foo(); // New construction okay Bar(); // Not NS::Bar, so okay NS::Bar B; // Non-temporary construction okay
توجه داشته باشید که اشیاء برای نشانهگذاری شدن باید صریحاً مشخص شوند، بنابراین اشیایی که از یک شیء مشخصشده ارثبری میکنند نشانهگذاری نخواهند شد.
این بررسی اشیاء موقت را بدون در نظر گرفتن ارثبری تطبیق میدهد و بنابراین یک نوع کلاس پایهٔ ممنوعشده، به شکل مشابه انواع کلاسهای مشتقشده را ممنوع نمیکند.
class Derived : Foo {} // Derived is not explicitly disallowed
Derived(); // and so temporary construction is okay
گزینهها
- Names
- فهرستی جداشده با نقطه ویرگول (semi-colon) از نامهای کاملاً واجد شرایط کلاسهای C++ که نباید بهصورت اشیاء موقت ساخته شوند. پیشفرض یک رشتهٔ خالی است.
fuchsia-trailing-return
استفاده از توابعی که دارای نوع بازگشتی پسرو (trailing return) هستند مجاز نیست، به جز مواردی که از مشخصکنندههای decltype و توابع لامبدا با نوع بازگشتی غیرقابل بیان استفاده میکنند.
برای مثال:
// بدون هشدار
int add_one(const int arg) { return arg; }
// هشدار
auto get_add_one() -> int (*)(const int) {
return add_one;
}
برای توابع لامبدا و مشخصکنندههای decltype استثنا قائل شده است:
// بدون هشدار
auto lambda = [](double x, double y) -> double {return x + y;};
// بدون هشدار
template <typename T1, typename T2>
auto fn(const T1 &lhs, const T2 &rhs) -> decltype(lhs + rhs) {
return lhs + rhs;
}
ویژگیهای غیرمجاز در Fuchsia را در این نشانی ببینید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
fuchsia-virtual-inheritance
در صورتی که کلاسها با توارث مجازی (virtual inheritance) تعریف شده باشند، هشدار میدهد.
برای مثال، کلاسها نباید با توارث مجازی تعریف شوند:
class B : public virtual A {}; // هشدار
ویژگیهای غیرمجاز در Fuchsia را در این نشانی ببینید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
google-build-explicit-make-pair
بررسی میکند که آرگومانهای الگوی make_pair استنتاج (deduce) شوند.
کامپایلر G++ 4.6 در حالت C++11 در صورتی که آرگومانهای الگوی make_pair به طور صریح مشخص شده باشند دچار خطای شدید میشود، و به هر حال چنین استفادهای مد نظر نبوده است.
نام بررسی متناظر در cpplint.py عبارت است از: build/explicit_make_pair.
google-build-namespaces
بررسی google-build-namespaces یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر misc-anonymous-namespace-in-header <> را ببینید.
فضاهای نام ناشناس (anonymous namespaces) را در هدرها پیدا میکند.
https://google.github.io/styleguide/cppguide.html#Namespaces
نام بررسی متناظر در cpplint.py عبارت است از: build/namespaces.
google-build-using-namespace
دستورهای using namespace را پیدا میکند.
این بررسی قاعده زیر را از راهنمای سبک کدنویسی Google C++ اعمال میکند https://google.github.io/styleguide/cppguide.html#Namespaces:
// ممنوع -- این کار فضای نام را آلوده میکند. using namespace foo;
نام بررسی متناظر در cpplint.py عبارت است از: build/namespaces.
google-default-arguments
بررسی میکند که برای متدهای مجازی (virtual methods) آرگومانهای پیشفرض مشخص نشده باشد.
ببینید: https://google.github.io/styleguide/cppguide.html#Default_Arguments
google-explicit-constructor
بررسی میکند که سازندههای قابل فراخوانی با یک آرگومان واحد و عملگرهای تبدیل نوع، با explicit علامتگذاری شوند تا از ریسک تبدیلهای ضمنی ناخواسته جلوگیری شود.
این مثال را در نظر بگیرید:
struct S {
int x;
operator bool() const { return true; }
};
bool f() {
S a{1};
S b{2};
return a == b;
}
این تابع مقدار true را برمیگرداند، زیرا اشیاء قبل از مقایسه به طور ضمنی به bool تبدیل میشوند که احتمالاً این هدف مورد نظر نبوده است.
این بررسی پیشنهاد درج explicit را پیش از اعلان سازنده یا عملگر تبدیل نوع ارائه میدهد. با این حال، سازندههای کپی و انتقال، و همچنین سازندههایی که یک آرگومان initializer_list واحد میگیرند، نباید explicit باشند.
این کد:
struct S {
S(int a);
explicit S(const S&);
operator bool() const;
...
تبدیل خواهد شد به:
struct S {
explicit S(int a);
S(const S&);
explicit operator bool() const;
...
ببینید: https://google.github.io/styleguide/cppguide.html#Explicit_Constructors
google-global-names-in-headers
آلودگی فضای نام سراسری (global namespace pollution) در فایلهای هدر را مشخص میکند. در حال حاضر این بررسی فقط روی اعلانها و دستورهای using فعال میشود.
بخش راهنمای شیوهنامه مرتبط عبارت است از: https://google.github.io/styleguide/cppguide.html#Namespaces.
google-objc-avoid-nsobject-new
فراخوانیهای متد +new یا بازنویسی (override) آن را که بر اساس شیوهنامه Objective-C گوگل ممنوع شدهاند، پیدا میکند.
شیوهنامه Objective-C گوگل فراخوانی +new یا بازنویسی آن در پیادهسازی کلاسها را ممنوع کرده و استفاده از متدهای +alloc و -init را برای نمونهسازی اشیاء ترجیح میدهد.
یک مثال:
NSDate *now = [NSDate new]; Foo *bar = [Foo new];
در عوض، کد باید از متدهای +alloc/-init یا متدهای کارخانه کلاس (class factory methods) استفاده کند.
NSDate *now = [NSDate date]; Foo *bar = [[Foo alloc] init];
این بررسی مطابق با قاعده Do Not Use +new در شیوهنامه Objective-C گوگل است https://google.github.io/styleguide/objcguide.html#do-not-use-new.
google-objc-avoid-throwing-exception
موارد پرتاب استثناها (throwing exceptions) در فایلهای Objective-C را مییابد.
به همان دلیلی که در راهنمای سبک C++ گوگل آمده است، ترجیح داده میشود از پرتاب استثناها در کد Objective-C خودداری شود.
قاعده متناظر در شیوهنامه C++: https://google.github.io/styleguide/cppguide.html#Exceptions
در عوض، بهتر است NSError ** ارسال شده و برای مشخص کردن موفقیت یا شکست، BOOL بازگردانده شود.
یک مثال نقض:
- (void)readFile {
if ([self isError]) {
@throw [NSException exceptionWithName:...];
}
}
به جای آن، بازگرداندن خطا از طریق NSError ** ارجحیت دارد:
- (BOOL)readFileWithError:(NSError **)error {
if ([self isError]) {
*error = [NSError errorWithDomain:...];
return NO;
}
return YES;
}
قاعده متناظر در شیوهنامه: https://google.github.io/styleguide/objcguide.html#avoid-throwing-exceptions
google-objc-function-naming
اعلانهای توابع در فایلهای Objective-C را که از الگوی توصیفشده در راهنمای سبک Objective-C گوگل پیروی نمیکنند، مییابد.
قاعده مربوطه در شیوهنامه را میتوانید در اینجا بیابید: https://google.github.io/styleguide/objcguide.html#function-names
تمام نامهای توابع باید به صورت Pascal case باشند. توابعی که رده ذخیرهسازی (storage class) آنها static نیست باید یک پیشوند مناسب داشته باشند.
نمونه کد زیر از این الگو پیروی نمیکند:
static bool is_positive(int i) { return i > 0; }
bool IsNegative(int i) { return i < 0; }
نمونه بالا ممکن است به صورت کد زیر تصحیح شود:
static bool IsPositive(int i) { return i > 0; }
bool *ABCIsNegative(int i) { return i < 0; }
google-objc-global-variable-declaration
اعلانهای متغیرهای سراسری در فایلهای Objective-C را که از الگوی نامگذاری متغیرها در شیوهنامه Objective-C گوگل پیروی نمیکنند، مییابد.
قاعده متناظر در شیوهنامه: https://google.github.io/styleguide/objcguide.html#variable-names
تمام متغیرهای سراسری باید از الگوی g[A-Z].* (متغیرها) یا k[A-Z].* (ثابتها) پیروی کنند. اگر نام متغیر از نام اولیه قابل استنباط باشد، این بررسی نامی مطابق با این الگو را پیشنهاد خواهد داد.
برای کد:
static NSString* myString = @"hello";
اصلاح به شکل زیر خواهد بود:
static NSString* gMyString = @"hello";
مثال دیگری برای متغیر ثابت:
static NSString* const myConstString = @"hello";
اصلاح به شکل زیر خواهد بود:
static NSString* const kMyConstString = @"hello";
اما برای کدهایی با پیشوند کاراکترهای غیرالفبایی مانند:
static NSString* __anotherString = @"world";
این بررسی یک پیام هشدار صادر میکند اما قادر به پیشنهاد اصلاح خودکار نخواهد بود. کاربر باید خودش آن را تصحیح کند.
google-readability-avoid-underscore-in-googletest-name
وجود خط زیر (underscore) را در نامهای مجموعه آزمون (test suite) و نامهای آزمون گوگلتست (googletest) در ماکروهای آزمون زیر بررسی میکند:
- TEST
- TEST_F
- TEST_P
- TYPED_TEST
- TYPED_TEST_P
ماکروی FRIEND_TEST شامل این بررسی نمیشود.
برای نمونه:
TEST(TestSuiteName, Illegal_TestName) {}
TEST(Illegal_TestSuiteName, TestName) {}
باعث اجرای این بررسی میشود. استفاده از خط زیر https://google.github.io/googletest/faq.html#why-should-test-suite-names-and-test-names-not-contain-underscore نه در نام مجموعه آزمون و نه در نامهای آزمون مجاز نیست.
پیشوند DISABLED_، که ممکن است برای غیرفعالسازی مجموعههای آزمون و آزمونهای تکی https://google.github.io/googletest/advanced.html#temporarily-disabling-tests به کار رود، پیش از بررسی وجود خط زیر، از نام مجموعه آزمون و نام آزمون حذف میشود.
این بررسی هیچ اصلاحاتی (fix) پیشنهاد نمیدهد.
google-readability-braces-around-statements
بررسی google-readability-braces-around-statements یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر readability-braces-around-statements <> را ببینید.
google-readability-casting
بررسی google-readability-casting یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر modernize-avoid-c-style-cast <> را ببینید.
موارد استفاده از تبدیل نوعهای سبک C (C-style casts) را پیدا میکند.
https://google.github.io/styleguide/cppguide.html#Casting
نام بررسی متناظر در cpplint.py عبارت است از: readability/casting.
google-readability-function-size
بررسی google-readability-function-size یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر readability-function-size <> را ببینید.
google-readability-namespace-comments
بررسی google-readability-namespace-comments یک نام مستعار است؛ لطفاً برای اطلاعات بیشتر llvm-namespace-comment <> را ببینید.
google-readability-todo
کامنتهای TODO فاقد نام کاربری یا شماره باگ را پیدا میکند.
بخش مرتبط در راهنمای سبک کدنویسی: https://google.github.io/styleguide/cppguide.html#TODO_Comments.
بررسی متناظر در cpplint.py عبارت است از: readability/todo
گزینهها
- Style
- رشتهای که سبک TODO را برای راهنماییهای اصلاح خودکار (fix-it hints) مشخص میکند. مقادیر پذیرفتهشده عبارتند از Hyphen و Parentheses. مقدار پیشفرض Hyphen است.
- گزینه Hyphen اصلاح را به این صورت قالببندی میکند: // TODO: username - details.
- گزینه Parentheses اصلاح را به این صورت قالببندی میکند: // TODO(username): details.
google-runtime-float
موارد استفاده از long double را پیدا کرده و به دلیل عدم قابلیت حمل (portability)، عدم استفاده از آن را پیشنهاد میکند.
قاعده متناظر در راهنمای سبک کدنویسی: https://google.github.io/styleguide/cppguide.html#Floating-Point_Types
google-runtime-int
موارد استفاده از short، long و long long را پیدا کرده و جایگزینی آنها با u?intXX(_t)? را پیشنهاد میدهد.
قاعده متناظر در راهنمای سبک کدنویسی: https://google.github.io/styleguide/cppguide.html#Integer_Types.
بررسی متناظر در cpplint.py عبارت است از: runtime/int.
گزینهها
- UnsignedTypePrefix
- رشتهای که پیشوند انواع بدون علامت (unsigned) را مشخص میکند. مقدار پیشفرض uint است.
- SignedTypePrefix
- رشتهای که پیشوند انواع علامتدار (signed) را مشخص میکند. مقدار پیشفرض int است.
- TypeSuffix
- رشتهای که پسوند نوع را مشخص میکند. مقدار پیشفرض یک رشته خالی است.
google-runtime-operator
سربارگذاریهای عملگر یکانی operator & را پیدا میکند.
https://google.github.io/styleguide/cppguide.html#Operator_Overloading
نام بررسی متناظر در cpplint.py عبارت است از: runtime/operator.
google-upgrade-googletest-case
موارد استفاده از APIهای منسوخشده نگارش 1.9 Google Test که نام آنها شامل case است را پیدا کرده و آنها را با APIهای معادل حاوی suite جایگزین میکند.
تمام نامهای حاوی case در حال جایگزینی هستند تا با معانی "test case" و "test suite" مورد استفاده توسط هیئت بینالمللی ارزیابی صلاحیت تست نرمافزار (ISTQB) و استاندارد ISO 29119 مطابقت داشته باشند.
نامهای جدید بخشی از Google Test نگارش 1.9 (در انتظار انتشار) هستند. توصیه میشود کاربران وابستگی پروژه خود را به نگارش 1.9 بهروزرسانی کنند و سپس از این بررسی برای حذف نامهای منسوخشده بهره ببرند.
APIهای تحت تأثیر عبارتند از:
- توابع عضو testing::Test، testing::TestInfo، testing::TestEventListener، testing::UnitTest و هر نوعی که از این نوعها ارثبری میکند
- ماکروهای TYPED_TEST_CASE، TYPED_TEST_CASE_P، REGISTER_TYPED_TEST_CASE_P و INSTANTIATE_TYPED_TEST_CASE_P
- نام مستعار نوع testing::TestCase
نمونههایی از اصلاحات انجامشده توسط این بررسی:
class FooTest : public testing::Test {
public:
static void SetUpTestCase();
static void TearDownTestCase();
};
TYPED_TEST_CASE(BarTest, BarTypes);
تبدیل میشود به:
class FooTest : public testing::Test {
public:
static void SetUpTestSuite();
static void TearDownTestSuite();
};
TYPED_TEST_SUITE(BarTest, BarTypes);
برای یکپارچگی بهتر کدهای کاربر، این بررسی هم توابع عضو مجازی و هم غیرمجازی با نامهای منطبق را در انواع مشتقشده تغییر نام میدهد. این بررسی در مواردی که امکان اصلاح با اطمینان کامل وجود ندارد، مانند برخی کاربردهای الگوها (template) و ماکروها، تلاش میکند تنها یک هشدار نمایش دهد.
hicpp-avoid-c-arrays
بررسی hicpp-avoid-c-arrays یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به modernize-avoid-c-arrays <> مراجعه کنید. این بررسی بخشی از قاعدهٔ 4.1.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-conversions را اعمال میکند.
hicpp-avoid-goto
بررسی hicpp-avoid-goto یک نام مستعار است، لطفاً برای اطلاعات بیشتر به cppcoreguidelines-avoid-goto <> مراجعه کنید. این بررسی قاعدهٔ 6.3.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/statements را اعمال میکند.
hicpp-braces-around-statements
بررسی hicpp-braces-around-statements یک نام مستعار است، لطفاً برای اطلاعات بیشتر به readability-braces-around-statements <> مراجعه کنید. این بررسی قاعدهٔ 6.1.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/statements را اعمال میکند.
hicpp-deprecated-headers
بررسی hicpp-deprecated-headers یک نام مستعار است، لطفاً برای اطلاعات بیشتر به modernize-deprecated-headers <> مراجعه کنید. این بررسی قاعدهٔ 1.3.3 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/general را اعمال میکند.
hicpp-exception-baseclass
اطمینان حاصل میکند که هر مقداری در یک عبارت throw نمونهای از std::exception باشد.
این بررسی قاعدهٔ 15.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard-exception-handling از استانداردهای کدنویسی High Integrity C++ را اعمال میکند.
class custom_exception {};
void throwing() noexcept(false) {
// Problematic throw expressions.
throw int(42);
throw custom_exception();
}
class mathematical_error : public std::exception {};
void throwing2() noexcept(false) {
// These kind of throws are ok.
throw mathematical_error();
throw std::runtime_error();
throw std::exception();
}
hicpp-explicit-conversions
این بررسی یک نام مستعار برای google-explicit-constructor <> است.
برای اعمال بخشهایی از قاعدهٔ 5.4.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard-expressions به کار میرود. این بررسی الزام میکند که سازندهها و عملگرهای تبدیل با explicit علامتگذاری شوند. سایر اشکال بررسیهای تبدیل نوع (casting) در بخشهای دیگر پیادهسازی شدهاند. برای بررسی اشکال بیشتر تبدیل نوع میتوان از بررسیهای زیر استفاده کرد:
- cppcoreguidelines-pro-type-static-cast-downcast <>
- cppcoreguidelines-pro-type-reinterpret-cast <>
- cppcoreguidelines-pro-type-const-cast <>
- cppcoreguidelines-pro-type-cstyle-cast <>
hicpp-function-size
این بررسی یک نام مستعار برای readability-function-size <> است. برای اعمال چندین بخش مربوط به پیچیدگی توابع مفید است.
hicpp-ignored-remove-result
مطابق با قاعدهٔ 17.5.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-library اطمینان حاصل میکند که نتیجهٔ std::remove، std::remove_if و std::unique نادیده گرفته نشود.
الگوریتمهای تغییردهندهٔ std::remove، std::remove_if و هر دو نسخهٔ سربارگذاریشدهٔ std::unique با جابهجا کردن (swap یا move) عناصر بازهای که روی آن عمل میکنند، کار میکنند. پس از اتمام، آنها یک تکرارکننده (iterator) به آخرین عنصر معتبر برمیگردانند. در بیشتر موارد، رفتار صحیح این است که از این نتیجه به عنوان عملوند اول در فراخوانی std::erase استفاده شود.
این بررسی زیرمجموعهای از bugprone-unused-return-value <> است و بسته به گزینههای استفادهشده، فعال کردن هر دو بررسی میتواند زاید باشد.
گزینهها
- AllowCastToVoid
- کنترل میکند که آیا تبدیل مقادیر بازگشتی به void مجاز است یا خیر. پیشفرض: true.
hicpp-invalid-access-moved
این بررسی یک نام مستعار برای bugprone-use-after-move <> است.
بخشهایی از قاعدهٔ 8.4.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/definitions را برای بررسی این که آیا به اشیاء انتقالیافته (moved-from) دسترسی وجود دارد یا خیر پیادهسازی میکند.
hicpp-member-init
این بررسی یک نام مستعار برای cppcoreguidelines-pro-type-member-init <> است. بررسی مربوط به قاعدهٔ 12.4.2 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions را جهت مقداردهی اولیهٔ اعضای کلاس به ترتیب صحیح پیادهسازی میکند.
hicpp-move-const-arg
بررسی hicpp-move-const-arg یک نام مستعار است، لطفاً برای اطلاعات بیشتر به performance-move-const-arg <> مراجعه کنید. این بررسی قاعدهٔ 17.3.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-library را اعمال میکند.
hicpp-multiway-paths-covered
این بررسی موقعیتهایی را پیدا میکند که مسیرهای کد بهطور کامل پوشش داده نشدهاند. علاوه بر این، پیشنهاد میکند در صورتی که کد واضحتر میشود، به جای switch از if استفاده شود. قاعدهٔ 6.1.2 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/statements و قاعدهٔ 6.1.4 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/statements از استانداردهای کدنویسی High Integrity C++ اعمال میشوند.
زنجیرههای if-else if که شاخهٔ پایانی else را ندارند ممکن است منجر به اجرای غیرمنتظرهٔ برنامه شده و ناشی از یک خطای منطقی باشند. اگر فقدان شاخهٔ else عمدی است، میتوانید آن را همراه با یک کامنت شفافساز خالی بگذارید. این هشدار میتواند در برخی پایگاههای کد پر سر و صدا باشد، بنابراین بهطور پیشفرض غیرفعال است.
void f1() {
int i = determineTheNumber();
if(i > 0) {
// Some Calculation
} else if (i < 0) {
// Precondition violated or something else.
}
// ...
}
استدلالهای مشابهی برای دستورات switch که تمام مسیرهای کد ممکن را پوشش نمیدهند وجود دارد.
// The missing default branch might be a logical error. It can be kept empty
// if there is nothing to do, making it explicit.
void f2(int i) {
switch (i) {
case 0: // something
break;
case 1: // something else
break;
}
// All other numbers?
}
// Violates this rule as well, but already emits a compiler warning (-Wswitch).
enum Color { Red, Green, Blue, Yellow };
void f3(enum Color c) {
switch (c) {
case Red: // We can't drive for now.
break;
case Green: // We are allowed to drive.
break;
}
// Other cases missing
}
قاعدهٔ 6.1.4 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/statements الزام میکند که هر دستور switch دستکم دو برچسب case به غیر از برچسب default داشته باشد. در غیر این صورت، آن switch را میتوانستیم به شکل بهتری با یک دستور if بیان کنیم. دستورات تباهیدهٔ (degenerated) switch بدون هیچ برچسبی نیز شناسایی میشوند.
// Degenerated switch that could be better written as `if`
int i = 42;
switch(i) {
case 1: // do something here
default: // do something else here
}
// Should rather be the following:
if (i == 1) {
// do something here
}
else {
// do something here
}
// A completely degenerated switch will be diagnosed.
int i = 42;
switch(i) {}
- WarnOnMissingElse
- یک فلگ بولی که هشداری را برای شاخههای else ناموجود فعال میکند. پیشفرض false است.
hicpp-named-parameter
این بررسی یک نام مستعار برای readability-named-parameter <> است.
قاعدهٔ 8.2.1 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/definitions.
hicpp-new-delete-operators
این بررسی یک نام مستعار برای misc-new-delete-overloads <> است. قاعدهٔ 12.3.1 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions تا اطمینان حاصل شود عملگرهای new و delete امضای صحیحی دارند.
hicpp-no-array-decay
بررسی hicpp-no-array-decay یک نام مستعار است، لطفاً برای اطلاعات بیشتر cppcoreguidelines-pro-bounds-array-to-pointer-decay <> را ببینید. این بررسی قاعدهٔ 4.1.1 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-conversions.
hicpp-no-assembler
دستورات اسمبلی را بررسی میکند. از بهکارگیری اسمبلی درونخطی باید اجتناب شود زیرا قابلیت حملپذیری کد را محدود میسازد.
این بررسی قاعدهٔ 7.5.1 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/declarations از High Integrity C++ Coding Standard را اعمال میکند.
hicpp-no-malloc
بررسی hicpp-no-malloc یک نام مستعار است، لطفاً برای اطلاعات بیشتر cppcoreguidelines-no-malloc <> را ببینید. این بررسی قاعدهٔ 5.3.2 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard-expressions.
hicpp-noexcept-move
این بررسی یک نام مستعار برای performance-noexcept-move-constructor <> است. قاعدهٔ 12.5.4 را بررسی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions تا انتساب انتقال و سازندهٔ انتقال را noexcept نشانهگذاری کند.
hicpp-signed-bitwise
موارد استفاده از عملیات بیتی روی انواع عددی صحیح علامتدار را پیدا میکند، که ممکن است منجر به رفتار تعریفنشده یا وابسته به پیادهسازی شود.
قاعدهٔ مربوطه در High Integrity C++ Standard، بخش 5.6.1 تعریف شده است https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard-expressions.
گزینهها
- IgnorePositiveIntegerLiterals
- اگر این گزینه روی true تنظیم شود، بررسی روی عملیات بیتی با لیترالهای صحیح مثبت، مانند ~0، 2 << 1 و غیره هشدار نخواهد داد. مقدار پیشفرض false است.
hicpp-special-member-functions
این بررسی یک نام مستعار برای cppcoreguidelines-special-member-functions <> است. بررسی میکند که توابع عضو ویژه امضای صحیحی داشته باشند، بر اساس قاعدهٔ 12.5.7 https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions.
hicpp-static-assert
بررسی hicpp-static-assert یک نام مستعار است، لطفاً برای اطلاعات بیشتر misc-static-assert <> را ببینید. این بررسی قاعدهٔ 7.1.10 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/declarations.
hicpp-undelegated-constructor
این بررسی یک نام مستعار برای bugprone-undelegated-constructor <> است. به صورت جزئی قاعدهٔ 12.4.5 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions برای یافتن فراخوانیهای نابجای سازنده درون یک سازنده دیگر.
struct Ctor {
Ctor();
Ctor(int);
Ctor(int, int);
Ctor(Ctor *i) {
// All Ctor() calls result in a temporary object
Ctor(); // did you intend to call a delegated constructor?
Ctor(0); // did you intend to call a delegated constructor?
Ctor(1, 2); // did you intend to call a delegated constructor?
foo();
}
};
hicpp-uppercase-literal-suffix
بررسی hicpp-uppercase-literal-suffix یک نام مستعار است، لطفاً برای اطلاعات بیشتر readability-uppercase-literal-suffix <> را ببینید.
به صورت جزئی قاعدهٔ 4.2.1 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-conversions تا اطمینان حاصل شود که پسوند U به درستی نوشته شده است.
hicpp-use-auto
بررسی hicpp-use-auto یک نام مستعار است، لطفاً برای اطلاعات بیشتر modernize-use-auto <> را ببینید. این بررسی قاعدهٔ 7.1.8 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/declarations.
hicpp-use-emplace
بررسی hicpp-use-emplace یک نام مستعار است، لطفاً برای اطلاعات بیشتر modernize-use-emplace <> را ببینید. این بررسی قاعدهٔ 17.4.2 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-library.
hicpp-use-equals-default
این بررسی یک نام مستعار برای modernize-use-equals-default <> است. قاعدهٔ 12.5.1 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions تا توابع عضو ویژه را صراحتاً پیشفرض (default) کند.
hicpp-use-equals-delete
این بررسی یک نام مستعار برای modernize-use-equals-delete <> است. قاعدهٔ 12.5.1 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/special-member-functions تا توابع عضو ویژه را صراحتاً پیشفرض یا حذف (delete) کند.
hicpp-use-noexcept
بررسی hicpp-use-noexcept یک نام مستعار است، لطفاً برای اطلاعات بیشتر modernize-use-noexcept <> را ببینید. این بررسی قاعدهٔ 1.3.5 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/general.
hicpp-use-nullptr
بررسی hicpp-use-nullptr یک نام مستعار است، لطفاً برای اطلاعات بیشتر modernize-use-nullptr <> را ببینید. این بررسی قاعدهٔ 2.5.3 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/lexical-conventions.
hicpp-use-override
این بررسی یک نام مستعار برای modernize-use-override <> است. قاعدهٔ 10.2.1 را پیادهسازی میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/derived-classes تا یک تابع مجازی را در هنگام بازنویسی (overriding) با override اعلان کند.
hicpp-vararg
بررسی hicpp-vararg یک نام مستعار است، لطفاً برای اطلاعات بیشتر cppcoreguidelines-pro-type-vararg <> را ببینید. این بررسی قاعدهٔ 14.1.1 را اعمال میکند https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/templates.
linuxkernel-must-check-errs
کد هستهٔ لینوکس را بررسی میکند تا ببیند آیا از نتایج توابع در linux/err.h استفاده میکند یا خیر. همچنین بررسی میکند که آیا کد از نتایج توابعی که مستقیماً مقداری را از یکی از این توابع خطا برمیگردانند، استفاده میکند یا خیر.
این موضوع در هستهٔ لینوکس مهم است زیرا مقادیر بازگشتی ERR_PTR، PTR_ERR، IS_ERR، IS_ERR_OR_NULL، ERR_CAST، و PTR_ERR_OR_ZERO باید بررسی شوند، چرا که اشارهگرهای مثبت و کدهای خطای منفی در یک بستر مشترک استفاده میشوند. این توابع با ویژگی __attribute__((warn_unused_result)) نشانهگذاری شدهاند، اما برخی از نسخههای هسته این هشدار را برای clang فعال ندارند.
مثالها:
/* Trivial unused call to an ERR function */
PTR_ERR_OR_ZERO(some_function_call());
/* A function that returns ERR_PTR. */
void *fn() { ERR_PTR(-EINVAL); }
/* An invalid use of fn. */
fn();
llvm-else-after-return
بررسی llvm-else-after-return یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر به readability-else-after-return <> مراجعه کنید.
llvm-header-guard
گاردهای هدر (header guards) که با شیوهنامه LLVM مطابقت ندارند را پیدا کرده و اصلاح میکند.
llvm-include-order
ترتیب صحیح #includeها را بررسی میکند.
ببینید: https://llvm.org/docs/CodingStandards.html#include-style
llvm-namespace-comment
بررسی google-readability-namespace-comments به عنوان یک نام مستعار برای این بررسی، به اینجا تغییر مسیر میدهد.
بررسی میکند که فضای نامهای (namespaces) طولانی دارای یک کامنت پایانی باشند.
https://llvm.org/docs/CodingStandards.html#namespace-indentation
https://google.github.io/styleguide/cppguide.html#Namespaces
namespace n1 {
void f();
}
// becomes
namespace n1 {
void f();
} // namespace n1
Options
- ShortNamespaceLines
- در صورتی که بدنه فضای نام بیشتر از تعداد خطوط ShortNamespaceLines کد داشته باشد، لازم میدارد که بعد از براکت بسته تعریف فضای نام، یک کامنت پایانی قرار گیرد. مقدار آن یک عدد صحیح بدون علامت است که پیشفرض آن 1U میباشد.
- SpacesBeforeComments
- یک عدد صحیح بدون علامت که تعداد فاصلههای (spaces) قبل از کامنت پایانی تعریف فضای نام را مشخص میکند. پیشفرض 1U است.
- AllowOmittingNamespaceComments
- هنگامی که true باشد، در صورتی که هیچ کامنتی برای فضای نام وجود نداشته باشد، بررسی با موفقیت انجام میشود. این بررسی تنها زمانی با خطا مواجه خواهد شد که کامنت مشخصشده برای فضای نام با آنچه که مورد انتظار است متفاوت باشد. پیشفرض false است.
llvm-prefer-isa-or-dyn-cast-in-conditionals
عبارات شرطی را بررسی کرده و مواردی از cast<> را که به جای بازگرداندن اشارهگر تهی (null pointer) خطا (assert) ایجاد میکنند، و همچنین مواردی از dyn_cast<> را که مقدار بازگشتی آنها دریافت نمیشود، پیدا و جایگزین میکند. علاوه بر این، مواردی را که با الگوی var && isa<X>(var) مطابقت دارند و در آنها var دو بار ارزیابی میشود، پیدا و جایگزین میکند.
// Finds these:
if (auto x = cast<X>(y)) {}
// is replaced by:
if (auto x = dyn_cast<X>(y)) {}
if (cast<X>(y)) {}
// is replaced by:
if (isa<X>(y)) {}
if (dyn_cast<X>(y)) {}
// is replaced by:
if (isa<X>(y)) {}
if (var && isa<T>(var)) {}
// is replaced by:
if (isa_and_nonnull<T>(var.foo())) {}
// Other cases are ignored, e.g.:
if (auto f = cast<Z>(y)->foo()) {}
if (cast<Z>(y)->foo()) {}
if (X.cast(y)) {}
llvm-prefer-register-over-unsigned
موارد استفاده قدیمی از unsigned برای نگهداری vregها و physregها را پیدا کرده و آنها را جهت استفاده از Register بازنویسی میکند.
در حال حاضر این سازوکار با یافتن تمام متغیرهایی از نوع عدد صحیح بدون علامت که مقداردهنده اولیه آنها با یک تبدیل ضمنی (implicit cast) از Register به unsigned آغاز میشود، کار میکند.
void example(MachineOperand &MO) {
unsigned Reg = MO.getReg();
...
}
تبدیل میشود به:
void example(MachineOperand &MO) {
Register Reg = MO.getReg();
...
}
llvm-prefer-static-over-anonymous-namespace
اعلانهای توابع و متغیرها را در داخل فضای نام بینام (anonymous namespace) پیدا کرده و جایگزینی آنها با اعلانهای static را پیشنهاد میکند.
شیوهنامه کدنویسی LLVM https://llvm.org/docs/CodingStandards.html#restrict-visibility توصیه میکند که فضاهای نام بینام تا حد امکان کوچک نگه داشته شوند و فقط برای اعلان کلاسها استفاده گردند. برای توابع و متغیرها، استفاده از مشخصکننده static جهت محدود کردن میدان دید (visibility) اولویت دارد.
به عنوان مثال، کد ناسازگار:
namespace {
class StringSort {
public:
StringSort(...)
bool operator<(const char *RHS) const;
};
// warning: place method definition outside of an anonymous namespace
bool StringSort::operator<(const char *RHS) const {}
// warning: prefer using 'static' for restricting visibility
void runHelper() {}
// warning: prefer using 'static' for restricting visibility
int myVariable = 42;
}
باید به این صورت تبدیل شود:
// Small anonymous namespace for class declaration
namespace {
class StringSort {
public:
StringSort(...)
bool operator<(const char *RHS) const;
};
}
// placed method definition outside of the anonymous namespace
bool StringSort::operator<(const char *RHS) const {}
// used 'static' instead of an anonymous namespace
static void runHelper() {}
// used 'static' instead of an anonymous namespace
static int myVariable = 42;
گزینهها
- AllowVariableDeclarations
- اگر true باشد، اجازه میدهد اعلان متغیرها در فضای نام ناشناس (anonymous namespace) قرار بگیرد. مقدار پیشفرض true است.
- AllowMemberFunctionsInClass
- اگر true باشد، فقط به متدهایی که درون فضای نام ناشناس خارج از کلاس مربوطه تعریف شدهاند اخطار داده میشود. مقدار پیشفرض true است.
llvm-qualified-auto
بررسی llvm-qualified-auto یک نام مستعار (alias) است، لطفاً برای اطلاعات بیشتر readability-qualified-auto <> را ببینید.
llvm-twine-local
به دنبال متغیرهای محلی Twine میگردد که مستعد استفاده پس از آزادسازی (use after free) هستند و معمولاً باید از آنها اجتناب شود.
static Twine Moo = Twine("bark") + "bah";
// becomes
static std::string Moo = (Twine("bark") + "bah").str();
کلاس Twine مالک حافظه محتویات خود نیست، بنابراین توصیه نمیشود از Twine ایجاد شده از رشتههای موقت یا لیترالهای رشتهای استفاده شود.
static Twine getModuleIdentifier(StringRef moduleName) {
return moduleName + "_module";
}
void foo() {
Twine result = getModuleIdentifier(std::string{"abc"} + "def");
// temporary std::string is destroyed here, result is dangling
}
پس از اعمال این نکات اصلاحی (fix-it)، کد به جای Twine از std::string برای متغیرهای محلی استفاده خواهد کرد. با این حال، Twine متدهای زیادی دارد که با std::string ناسازگار هستند، بنابراین ممکن است کاربر پس از اعمال نکات اصلاحی نیاز داشته باشد کد را به صورت دستی تطبیق دهد.
llvm-mlir-op-builder
استفاده از فرم قدیمی و در شرف منسوخشدن OpBuilder::create<T> در MLIR را بررسی کرده و پیشنهاد میکند به جای آن از T::create استفاده شود.
مثال
builder.create<FooOp>(builder.getUnknownLoc(), "baz");
تبدیل میشود به:
FooOp::create(builder, builder.getUnknownLoc(), "baz");
llvm-use-ranges
فراخوانیهای الگوریتمهای تکرارکننده (iterator) در کتابخانه STL را پیدا میکند که میتوانند با الگوریتمهای مبتنی بر بازه (range-based) در LLVM از مسیر llvm/ADT/STLExtras.h جایگزین شوند.
مثال
auto it = std::find(vec.begin(), vec.end(), value);
bool all = std::all_of(vec.begin(), vec.end(),
[](int x) { return x > 0; });
تبدیل میشود به:
auto it = llvm::find(vec, value);
bool all = llvm::all_of(vec, [](int x) { return x > 0; });
الگوریتمهای پشتیبانیشده
فراخوانیهای مربوط به الگوریتمهای STL زیر بررسی میشوند:
std::all_of, std::any_of, std::binary_search, std::copy, std::copy_if, std::count, std::count_if, std::equal, std::fill, std::find, std::find_if, std::find_if_not, std::for_each, std::includes, std::is_sorted, std::lower_bound, std::max_element, std::min_element, std::mismatch, std::none_of, std::partition, std::partition_point, std::remove_if, std::replace, std::stable_sort, std::transform, std::uninitialized_copy, std::unique, std::upper_bound.
این بررسی هنگام اعمال اصلاحات، دستور پیشپردازنده ضروری #include "llvm/ADT/STLExtras.h" را اضافه میکند.
llvmlibc-callee-namespace
بررسی میکند که تمام فراخوانیها به توابعی درون فضای نام صحیح ارجاع داده شوند.
// Implementation inside the LIBC_NAMESPACE namespace.
// Correct if:
// - LIBC_NAMESPACE is a macro
// - LIBC_NAMESPACE expansion starts with `__llvm_libc`
namespace LIBC_NAMESPACE {
// Allow calls with the fully qualified name.
LIBC_NAMESPACE::strlen("hello");
// Allow calls to compiler provided functions.
(void)__builtin_abs(-1);
// Bare calls are allowed as long as they resolve to the correct namespace.
strlen("world");
// Disallow calling into functions in the global namespace.
::strlen("!");
} // namespace LIBC_NAMESPACE
llvmlibc-implementation-in-namespace
بررسی میکند که تمام اعلانها در پیادهسازی llvm-libc درون فضای نام صحیح قرار گرفته باشند.
// Implementation inside the LIBC_NAMESPACE_DECL namespace.
// Correct if:
// - LIBC_NAMESPACE_DECL is a macro
// - LIBC_NAMESPACE_DECL expansion starts with `[[gnu::visibility("hidden")]] __llvm_libc`
namespace LIBC_NAMESPACE_DECL {
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
// Namespaces within LIBC_NAMESPACE_DECL namespace are allowed.
namespace inner {
int localVar = 0;
}
// Functions with C linkage are allowed.
extern "C" void str_fuzz() {}
}
// Incorrect: implementation not in the LIBC_NAMESPACE_DECL namespace.
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
// Incorrect: outer most namespace is not the LIBC_NAMESPACE_DECL macro.
namespace something_else {
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
}
// Incorrect: outer most namespace expansion does not start with `[[gnu::visibility("hidden")]] __llvm_libc`.
#define LIBC_NAMESPACE_DECL custom_namespace
namespace LIBC_NAMESPACE_DECL {
void LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) {}
}
llvmlibc-inline-function-decl
بررسی میکند که تمام توابع inline بهصورت صریح یا ضمنی در فایلهای هدر، با ماکروی LIBC_INLINE نشانهگذاری شده باشند، بهجز توابع ضمنی کلاسها یا توابع حذفشده (deleted). برای اطلاعات بیشتر دربارهٔ این ماکرو، راهنمای سبک libc را در https://libc.llvm.org/dev/code_style.html مشاهده کنید.
llvmlibc-restrict-system-libc-headers
موارد include هدرهای libc سیستم که توسط کامپایلر ارائه نشدهاند را درون پیادهسازیهای llvm-libc پیدا میکند.
#include <stdio.h> // Not allowed because it is part of system libc. #include <stddef.h> // Allowed because it is provided by the compiler. #include "internal/stdio.h" // Allowed because it is NOT part of system libc.
این بررسی ضروری است زیرا include تصادفی هدرهای libc سیستم میتواند منجر به باگهای نامحسوس و سختیاب شود. به عنوان مثال، یک libc سیستم را در نظر بگیرید که ترتیب فیلدهای ساختار dirent آن با llvm-libc کمی متفاوت است. در حالی که این کد با موفقیت کامپایل خواهد شد، اما به دلیل ناسازگاری در ABI میتواند باعث بروز مشکلاتی در زمان اجرا شود.
گزینهها
- Includes
- یک رشته
شامل
فهرستی از
الگوهای
انطباقی (glob)
نامهای
فایل مجاز
برای include که با
کاما از هم
جدا
شدهاند.
مشابه
فهرست
الگوهای -checks
برای اجرای
خود clang-tidy، دو
نویسهٔ عام
(wildcard) عبارتند
از * و -،
بهترتیب
برای شامل
کردن و
مستثنی
کردن
الگوها.
مقدار
پیشفرض -*
است که
تمامی includeها
را غیرمجاز
میکند.
میتوان از این گزینه برای مجاز کردن includeهای امن شناختهشده، مانند هدرهای توسعهٔ لینوکس استفاده کرد. برای جزئیات بیشتر به portability-restrict-system-includes <> مراجعه کنید.
misc-anonymous-namespace-in-header
فضاهای نام بینام (anonymous) را در هدرها پیدا میکند.
فضاهای نام بینام در هدرها میتوانند منجر به نقض قاعدهٔ یکبار تعریف (ODR یا One Definition Rule) شوند، زیرا هر واحد ترجمه که هدر را include کند، نسخهٔ یکتای خود را از نمادها دریافت خواهد کرد. این امر حجم فایل اجرایی باینری را افزایش داده و میتواند خطاهای پیوندزنی (link-time) گیجکنندهای ایجاد کند.
مراجع
این بررسی مطابق با قاعدهٔ استاندارد کدنویسی CERT C++ با شناسهٔ DCL59-CPP. Do not define an unnamed namespace in a header file https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL59-CPP.+Do+not+define+an+unnamed+namespace+in+a+header+file است.
نام بررسی متناظر در cpplint.py: build/namespaces.
misc-confusable-identifiers
دربارهٔ شناسههای شبههانگیز هشدار میدهد؛ یعنی شناسههایی که از نظر ظاهری بسیار شبیه به هم هستند، اما از نویسههای یونیکد متفاوتی استفاده میکنند. این ویژگی حملهٔ بالقوهای را که در CVE-2021-42574 https://www.cve.org/CVERecord?id=CVE-2021-42574 شرح داده شده است شناسایی میکند.
مثال:
int fo; // Initial character is U+0066 (LATIN SMALL LETTER F). int 𝐟o; // Initial character is U+1D41F (MATHEMATICAL BOLD SMALL F) not U+0066 (LATIN SMALL LETTER F).
misc-const-correctness
این بررسی، متغیرهای محلی را که میتوانستند بهصورت const اعلان شوند ولی نشدهاند، شناسایی میکند. اعلان متغیرها بهصورت const در بسیاری از راهنماهای کدنویسی الزامی یا توصیهشده است، مانند: ES.25 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es25-declare-an-object-const-or-constexpr-unless-you-want-to-modify-its-value-later-on از C++ Core Guidelines.
لطفاً توجه داشته باشید که تحلیل این بررسی تنها مبتنی بر نوع داده (type-based) است. متغیرهایی که تغییر نمیکنند اما برای ایجاد یک دستگیرهٔ (handle) غیر-const که ممکن است از حوزه (scope) خارج شود به کار میروند، بهعنوان کاندیدای const تشخیص داده نمیشوند.
// Declare a variable, which is not ``const`` ... int i = 42; // but use it as read-only. This means that `i` can be declared ``const``. int result = i * i; // Before transformation int const result = i * i; // After transformation
این بررسی میتواند مقادیر، اشارهگرها، ارجاعها و مقادیر مورد اشاره (pointees) را تحلیل کند:
// Normal values like built-ins or objects. int potential_const_int = 42; // Before transformation int const potential_const_int = 42; // After transformation int copy_of_value = potential_const_int; MyClass could_be_const; // Before transformation MyClass const could_be_const; // After transformation could_be_const.const_qualified_method(); // References can be declared const as well. int &reference_value = potential_const_int; // Before transformation int const& reference_value = potential_const_int; // After transformation int another_copy = reference_value; // The similar semantics of pointers are analyzed. int *pointer_variable = &potential_const_int; // Before transformation int const*const pointer_variable = &potential_const_int; // After transformation, both pointer itself and pointee are supported. int last_copy = *pointer_variable;
تبدیل خودکار کد فقط روی متغیرهایی اعمال میشود که در اعلانهای تکی اعلام شده باشند. ممکن است ابتدا بخواهید پایگاه کد خود را با readability-isolate-declaration <> آماده کنید.
توجه داشته باشید که بررسی cppcoreguidelines-avoid-non-const-global-variables <> برای اعمال درستی const بر روی تمامی متغیرهای سراسری (globals) وجود دارد.
محدودیتها
این بررسی روی کدهای C اجرا نمیشود.
این بررسی متغیرهای قالببندیشده (templated) یا متغیرهایی را که وابسته به نمونهسازی (instantiation dependent) هستند تحلیل نخواهد کرد. نمونهسازیهای مختلف میتوانند منجر به ویژگیهای درستیِ const متفاوتی شوند و بهطور کلی یافتن تمام نمونهسازیهای یک قالب امکانپذیر نیست. ممکن است از قالب در یک واحد ترجمه (translation unit) مستقل به شکل متفاوتی استفاده شده باشد.
گزینهها
- AnalyzeValues
- فعال یا غیرفعال کردن تحلیل متغیرهای مقداری معمولی، مانند int i = 42;. مقدار پیشفرض true است.
// Warning
int i = 42;
// No warning
int const i = 42;
// Warning
int a[] = {42, 42, 42};
// No warning
int const a[] = {42, 42, 42};
- AnalyzeReferences
- فعال یا غیرفعال کردن تحلیل متغیرهای ارجاعی، مانند int &ref = i;. مقدار پیشفرض true است.
int i = 42; // Warning int& ref = i; // No warning int const& ref = i;
- AnalyzePointers
- فعال یا غیرفعال کردن تحلیل متغیرهای اشارهگر، مانند int *ptr = &i;. برای بررسیهای خاصتر، به WarnPointersAsValues و WarnPointersAsPointers مراجعه کنید. مقدار پیشفرض true است.
- WarnPointersAsValues
- این گزینه پیشنهاد const شدن خود اشارهگر را فعال میکند. مقادیر اشارهگر دو حالت برای const بودن دارند: خود اشارهگر و مقداری که به آن اشاره میشود. مقدار پیشفرض false است.
int value = 42; // Warning const int * pointer_variable = &value; // No warning const int *const pointer_variable = &value;
- WarnPointersAsPointers
- این گزینه
پیشنهاد const
شدن مقداری
را که
اشارهگر
به آن اشاره
میکند،
فعال
میسازد.
مقدار
پیشفرض true
است.
نیازمند این است که AnalyzePointers برابر با true باشد.
int value = 42; // No warning const int *const pointer_variable = &value; // Warning int *const pointer_variable = &value;
- TransformValues
- راهنماییهای اصلاح خودکار (fixit-hints) را برای انواع مقداری فراهم میکند تا در صورت تکاعلانی بودن، const را بهطور خودکار اضافه کند. مقدار پیشفرض true است.
// Before
int value = 42;
// After
int const value = 42;
// Before
int a[] = {42, 42, 42};
// After
int const a[] = {42, 42, 42};
// Result is modified later in its life-time. No diagnostic and fixit hint will be emitted.
int result = value * 3;
result -= 10;
- TransformReferences
- راهنماییهای اصلاح خودکار (fixit-hints) را برای انواع ارجاعی فراهم میکند تا در صورت تکاعلانی بودن، const را بهطور خودکار اضافه کند. مقدار پیشفرض true است.
// This variable could still be a constant. But because there is a non-const reference to // it, it can not be transformed (yet). int value = 42; // The reference 'ref_value' is not modified and can be made 'const int &ref_value = value;' // Before int &ref_value = value; // After int const &ref_value = value; // Result is modified later in its life-time. No diagnostic and fixit hint will be emitted. int result = ref_value * 3; result -= 10;
- TransformPointersAsValues
- راهنماییهای
اصلاح
خودکار (fixit-hints)
را برای
اشارهگرها
در صورتی که
شیء مورد
اشاره
تغییر
نکند،
فراهم
میسازد.
این گزینه
تغییر
نکردن
مقداری را
که به آن
اشاره شده
بررسی
نمیکند!
مقدار
پیشفرض false
است.
نیازمند این است که 'WarnPointersAsValues' برابر با 'true' باشد.
int value = 42;
// Before
const int * pointer_variable = &value;
// After
const int *const pointer_variable = &value;
// Before
const int * a[] = {&value, &value};
// After
const int *const a[] = {&value, &value};
// Before
int *ptr_value = &value;
// After
int *const ptr_value = &value;
int result = 100 * (*ptr_value); // Does not modify the pointer itself.
// This modification of the pointee is still allowed and not diagnosed.
*ptr_value = 0;
// The following pointer may not become a 'int *const'.
int *changing_pointee = &value;
changing_pointee = &result;
- TransformPointersAsPointers
- راهنماییهای
اصلاح
خودکار (fix-it hints)
را برای
اشارهگرها
در صورتی که
مقدار مورد
اشاره
تغییر
نکند،
فراهم
میسازد.
مقدار
پیشفرض false
است.
نیازمند این است که WarnPointersAsPointers برابر با true باشد.
int value = 42;
// Before
int * pointer_variable = &value;
// After
const int * pointer_variable = &value;
// Before
int * a[] = {&value, &value};
// After
const int * a[] = {&value, &value};
- AllowedTypes
- فهرستی از نامهای انواع که با نقطه-ویرگول (semicolon) از یکدیگر جدا شدهاند و از بررسی درستی const مستثنی خواهند شد. عبارتهای منظم (regex) پذیرفته میشوند؛ به عنوان مثال [Rr]ef(erence)?$ با هر نوعی که دارای پسوند Ref، ref، Reference و reference باشد مطابقت دارد. اگر نامی در فهرست شامل توالی :: باشد، با نام کامل نوع (یعنی namespace::Type) مطابقت داده میشود؛ در غیر این صورت تنها با نام نوع (یعنی Type) سنجیده خواهد شد. مقدار پیشفرض یک رشته خالی است.
misc-coroutine-hostile-raii
تشخیص میدهد که چه زمانی اشیایی از انواع خصمانه خاص RAII در طول نقاط تعلیق (suspension points) در یک کوروتین (coroutine) باقی میمانند. این انواع خصمانه شامل انواع قفلپذیر با حوزه مشخص (scoped-lockable) و انواعی هستند که به یک لیست سیاه (denylist) قابل پیکربندی تعلق دارند.
برخی از اشیاء نیازمند آن هستند که در همان رشتهای (thread) که ایجاد شدهاند، نابود شوند. به طور سنتی این الزام اغلب به صورت «باید یک متغیر محلی باشد» بیان میشد، با این فرض که متغیرهای محلی همیشه به این شیوه عمل میکنند. با این حال، در کوروتینهای C++20 این فرض نادرست است، زیرا یک دستور میانی co_await ممکن است باعث تعلیق کوروتین شده و بعداً روی رشته دیگری از سر گرفته (resumed) شود.
طول عمر شیئی که نیاز به نابودی در همان رشته دارد، نباید یک نقطه co_await یا co_yield را در بر بگیرد. اگر شیئی را ایجاد یا نابود میکنید، باید این کار را بدون اجازه دادن به تعلیق کوروتین در این بین انجام دهید.
انواع زیر خصمانه در نظر گرفته میشوند:
- انواع Scoped-lockable: باقی ماندن یک شیء scoped-lockable در طول یک نقطه تعلیق مشکلساز است زیرا قفلی که توسط این شیء نگه داشته شده میتواند توسط رشته متفاوتی آزاد شود. این امر رفتار تعریفنشده (undefined behaviour) خواهد بود. این شامل تمام انواعی میشود که با ویژگی scoped_lockable حاشیهنویسی شدهاند.
- انواعی که به یک لیست سیاه (denylist) قابل پیکربندی تعلق دارند.
// Call some async API while holding a lock.
task coro() {
const std::lock_guard l(&mu_);
// Oops! The async Bar function may finish on a different
// thread from the one that created the lock_guard (and called
// Mutex::Lock). After suspension, Mutex::Unlock will be called on the wrong thread.
co_await Bar();
}
گزینهها
- RAIITypesList
- یک فهرست جداشده با نقطه-ویرگول از انواع واجد شرایط (qualified types) که نباید اجازه داشته باشند در طول نقاط تعلیق باقی بمانند. مثال: my::lockable;a::b;::my::other::lockable مقدار پیشفرض این گزینه std::lock_guard;std::scoped_lock است.
- AllowedAwaitablesList
- یک فهرست
جداشده با
نقطه-ویرگول
از انواع
واجد شرایط
انواع awaitable که
میتوانند
با وجود
اشیاء
خصمانه RAII در
حوزه (scope)، با
امنیت مورد
انتظار (await)
قرار گیرند.
اعمال co_await روی یک عبارت از نوع awaitable در صورتی امن تلقی میشود که نوع awaitable بخشی از این فهرست باشد. اشیاء RAII که در طول چنین عبارت co_await باقی میمانند امن تلقی شده و در نتیجه نشانهگذاری (flag) نمیشوند.
مثال کاربرد:
// Consider option AllowedAwaitablesList = "safe_awaitable"
struct safe_awaitable {
bool await_ready() noexcept { return false; }
void await_suspend(std::coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
auto wait() { return safe_awaitable{}; }
task coro() {
// This persists across both the co_await's but is not flagged
// because the awaitable is considered safe to await on.
const std::lock_guard l(&mu_);
co_await safe_awaitable{};
co_await wait();
}
مثال: my::safe::awaitable;other::awaitable پیشفرض یک رشته خالی است.
- AllowedCallees
- یک فهرست جداشده با نقطه-ویرگول از نام توابع فراخوانیشده (callee) که میتوانند با وجود اشیاء خصمانه RAII در حوزه، با امنیت مورد انتظار قرار گیرند. مثال کاربرد:
// Consider option AllowedCallees = "noop"
task noop() { co_return; }
task coro() {
// This persists across the co_await but is not flagged
// because the awaitable is considered safe to await on.
const std::lock_guard l(&mu_);
co_await noop();
}
مثال: my::safe::await;other::await پیشفرض یک رشته خالی است.
misc-definitions-in-headers
تعاریف توابع و متغیرهای غیر extern و غیر inline را در فایلهای هدر پیدا میکند، که در صورت include شدن این هدرها در چندین واحد ترجمه (translation units)، میتواند منجر به نقض احتمالی ODR (قاعده یک تعریف) شود.
// Foo.h
int a = 1; // Warning: variable definition.
extern int d; // OK: extern variable.
namespace N {
int e = 2; // Warning: variable definition.
}
// Warning: variable definition.
const char* str = "foo";
// OK: internal linkage variable definitions are ignored for now.
// Although these might also cause ODR violations, we can be less certain and
// should try to keep the false-positive rate down.
static int b = 1;
const int c = 1;
const char* const str2 = "foo";
constexpr int k = 1;
namespace { int x = 1; }
// Warning: function definition.
int g() {
return 1;
}
// OK: inline function definition is allowed to be defined multiple times.
inline int e() {
return 1;
}
class A {
public:
int f1() { return 1; } // OK: implicitly inline member function definition is allowed.
int f2();
static int d;
};
// Warning: not an inline member function definition.
int A::f2() { return 1; }
// OK: class static data member declaration is allowed.
int A::d = 1;
// OK: function template is allowed.
template<typename T>
T f3() {
T a = 1;
return a;
}
// Warning: full specialization of a function template is not allowed.
template <>
int f3() {
int a = 1;
return a;
}
template <typename T>
struct B {
void f1();
};
// OK: member function definition of a class template is allowed.
template <typename T>
void B<T>::f1() {}
class CE {
constexpr static int i = 5; // OK: inline variable definition.
};
inline int i = 5; // OK: inline variable definition.
constexpr int f10() { return 0; } // OK: constexpr function implies inline.
// OK: C++14 variable templates are inline.
template <class T>
constexpr T pi = T(3.1415926L);
هنگامی که clang-tidy با گزینه --fix-notes اجرا میشود، این بررسی اصلاحاتی ارائه میدهد که کلمه کلیدی inline را به طور خودکار به توابع کشفشده اضافه میکند. لطفاً توجه داشته باشید که افزودن کلمه کلیدی inline به متغیرها در حال حاضر توسط این بررسی پشتیبانی نمیشود.
misc-header-include-cycle
این بررسی وابستگیهای چرخهای #include را بین فایلهای هدر تعریفشده توسط کاربر تشخیص میدهد.
// Header A.hpp #pragma once #include "B.hpp" // Header B.hpp #pragma once #include "C.hpp" // Header C.hpp #pragma once #include "A.hpp" // Include chain: A->B->C->A
فایلهای هدر بخش مهمی از بسیاری از برنامههای ++C هستند، چرا که روشی برای سازماندهی اعلانها و تعاریف به اشتراک گذاشتهشده میان چندین فایل منبع فراهم میکنند. با این حال، فایلهای هدر زمانی که در چرخههای وابستگی پیچیده گرفتار شوند نیز میتوانند مشکلاتی ایجاد کنند. چنین چرخههایی میتوانند موجب بروز مشکلاتی در زمانهای کامپایل، کامپایلهای مجدد غیرضروری، و دشوارتر شدن درک ساختار کلی کد شوند.
برای رفع این مشکلات، بررسی خاصی توسعه یافته است تا وابستگیهای چرخهای میان فایلهای هدر را که با نام «چرخههای include» نیز شناخته میشوند، شناسایی کند. یک چرخه include زمانی رخ میدهد که فایل هدر A فایل هدر B را include کند، و B (یا هر فایل هدر includeشده بعدی) دوباره فایل هدر A را include کند که منجر به یک چرخه وابستگی دوار میگردد.
این بررسی در سطح پیشپردازنده عمل میکند و بهطور ویژه هدرهای تعریفشده توسط کاربر و وابستگیهای آنها را تحلیل مینماید. این ابزار صرفاً بر شناسایی چرخههای include تمرکز دارد و سایر وابستگیهای نوع یا تابع را نادیده میگیرد. این تحلیل تخصصی به شناسایی و جلوگیری از مشکلات مرتبط با سازماندهی فایلهای هدر کمک میکند.
با تشخیص زودهنگام چرخههای include در فرایند توسعه، توسعهدهندگان میتوانند این مشکلات را پیش از آنکه رفع آنها دشوارتر و زمانبرتر شود شناسایی و برطرف کنند. این امر میتواند به زمانهای کامپایل سریعتر، بهبود کیفیت کد، و در مجموع به پایگاه کدی با قابلیت نگهداری بیشتر منجر شود. علاوه بر این، با اطمینان از سازماندهی فایلهای هدر بهگونهای که از وابستگیهای چرخهای اجتناب شود، توسعهدهندگان میتوانند درک و اصلاح کد خود را در طول زمان آسانتر کنند.
شایان ذکر است که تنها هدرهای تعریفشده توسط کاربر و وابستگیهای آنها تحلیل میشوند، و includeهای سیستمی مانند هدرهای کتابخانه استاندارد و هدرهای کتابخانههای شخص ثالث مستثنی هستند. includeهای سیستمی معمولاً بهخوبی طراحی شده و عاری از چرخههای include هستند، و نادیده گرفتن آنها به تمرکز بر مشکلات بالقوه موجود در پایگاه کد خود پروژه کمک میکند. این محدودیت توانایی تشخیص چرخههای #include را در کد تحلیلشده کاهش نمیدهد.
توسعهدهندگان باید هرگونه هشدار یا بازخورد ارائهشده توسط این راهکار را با دقت بررسی کنند. اگرچه هدف تحلیل شناسایی و جلوگیری از چرخههای include است، اما ممکن است شرایطی وجود داشته باشد که استثناها یا تغییراتی در آن ضروری باشد. هنگام اعمال تغییرات، قضاوت صحیح و در نظر گرفتن زمینه خاص پایگاه کد اهمیت دارد.
گزینهها
- IgnoredFilesList
- روشی را برای مستثنی کردن فایلها/هدرهای خاص از هشدارهای ارائهشده توسط بررسی فراهم میکند. این کار را میتوان با مشخص کردن فهرستی از عبارات منظم یا نام فایلها که با نقطه-ویرگول (semicolon) از هم جدا شدهاند، انجام داد. این گزینه میتواند بهعنوان جایگزینی برای //NOLINT هنگامی که استفاده از آن ممکن نیست به کار رود. مقدار پیشفرض این گزینه یک رشته خالی است که نشان میدهد بهطور پیشفرض هیچ فایلی نادیده گرفته نمیشود.
misc-include-cleaner
مواردی از includeهای استفادهنشده و مفقود را بررسی میکند. یافتهها را تنها برای فایل اصلی یک واحد ترجمه (translation unit) تولید مینماید. یافتهها مطابق با https://clangd.llvm.org/design/include-cleaner هستند.
مثال:
// foo.h
class Foo{};
// bar.h
#include "baz.h"
class Bar{};
// baz.h
class Baz{};
// main.cc
#include "bar.h" // OK: uses class Bar from bar.h
#include "foo.h" // warning: unused include "foo.h"
Bar bar;
Baz baz; // warning: missing include "baz.h"
گزینهها
- IgnoreHeaders
- فهرستی از regexها جداشده با نقطه-ویرگول برای غیرفعال کردن درج/حذف فایلهای هدری که با این regex به عنوان پسوند مطابقت دارند. به عنوان مثال، foo/.* درج/حذف را برای تمام هدرهای زیر دایرکتوری foo غیرفعال میکند. پیشفرض یک رشته خالی است، هیچ هدری نادیده گرفته نخواهد شد.
- DeduplicateFindings
- یک مقدار بولی که مشخص میکند آیا بررسی باید یافتههای تکراری را برای یک نماد یکسان حذف کند یا خیر. پیشفرض true است.
- UnusedIncludes
- یک مقدار بولی که مشخص میکند آیا بررسی باید includeهای استفادهنشده را گزارش کند (includeهایی که مستقیماً استفاده نمیشوند) یا خیر. پیشفرض true است.
- MissingIncludes
- یک مقدار بولی که مشخص میکند آیا بررسی باید includeهای مفقود را گزارش کند (فایلهای هدری که نمادهایی از آنها استفاده شده اما مستقیماً include نشدهاند) یا خیر. پیشفرض true است.
misc-misleading-bidirectional
در مورد توالیهای یونیکد دوجهته (bidirectional) پایاننیافته هشدار میدهد و حملات احتمالی را همانطور که در حمله Trojan Source به آدرس https://www.trojansource.codes شرح داده شده، تشخیص میدهد.
مثال:
#include <iostream>
int main() {
bool isAdmin = false;
/* } if (isAdmin) begin admins only */
std::cout << "You are an admin.\n";
/* end admins only { */
return 0;
}
misc-misleading-identifier
شناسههایی را پیدا میکند که شامل نویسههای یونیکد با جهت راستبهچپ هستند، که میتواند گمراهکننده باشد زیرا ممکن است فهم یک خط عبارت کامل را تغییر دهند، همانطور که در Trojan Source به آدرس https://trojansource.codes شرح داده شده است.
نمونهای از چنین کدهای گمراهکنندهای در ادامه آمده است:
#include <stdio.h>
short int א = (short int)0;
short int ג = (short int)12345;
int main() {
int א = ג; // a local variable, set to zero?
printf("ג is %d\n", ג);
printf("א is %d\n", א);
}
misc-misplaced-const
این بررسی زمانی را تشخیص میدهد که یک توصیفکننده const بهجای اشارهشونده (pointee)، روی یک typedef/ using از نوع اشارهگر اعمال شده باشد؛ زیرا چنین ساختارهایی اغلب برای توسعهدهندگان گمراهکننده هستند چرا که const بهجای اشارهشونده، به خود اشارهگر اعمال میشود.
بهعنوان مثال، در کد زیر، نوع حاصل int * const است نه const int *:
typedef int *int_ptr;
void f(const int_ptr ptr) {
*ptr = 0; // potentially quite unexpectedly the int can be modified here
ptr = 0; // does not compile
}
اگر نوع زیربنایی typedef/using اشارهگری به یک نوع const یا اشارهگر تابع باشد، این بررسی هشداری نمیدهد. دلیل این امر آن است که توصیفکننده const کمتر ممکن است اشتباه گرفته شود، چرا که روی نوع اشارهشونده زیربنایی زائد (یا غیرمجاز) خواهد بود.
misc-multiple-inheritance
اگر کلاسی از چندین کلاس که تماماً مجازی محض (pure virtual) نیستند ارثبری کند، هشدار میدهد.
بهعنوان مثال، اعلان کلاسی که از چندین کلاس انضمامی (concrete) ارثبری کند غیرمجاز است:
class Base_A {
public:
virtual int foo() { return 0; }
};
class Base_B {
public:
virtual int bar() { return 0; }
};
// Warning
class Bad_Child1 : public Base_A, Base_B {};
کلاسی که از کلاسهای مجازی محض ارثبری کند مجاز است:
class Interface_A {
public:
virtual int foo() = 0;
};
class Interface_B {
public:
virtual int bar() = 0;
};
// No warning
class Good_Child1 : public Interface_A, Interface_B {
virtual int foo() override { return 0; }
virtual int bar() override { return 0; }
};
منابع
ویژگیهای غیرمجاز در Fuchsia را در این نشانی مشاهده کنید: https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/cxx?hl=en
misc-new-delete-overloads
بررسی cert-dcl54-cpp بهعنوان نام مستعار این بررسی به اینجا هدایت میشود.
این بررسی توابع عملگر بازنویسیشدهٔ (overloaded) new() و عملگر delete() را که تابع ذخیرهسازی آزاد (free store) متناظر آنها در همان حوزه تعریف نشده است، پرچمگذاری میکند. برای مثال، این بررسی پیادهسازی عملگر غیرپایگانی (non-placement) new() در یک کلاس را زمانی که کلاس یک تابع عملگر غیرپایگانی delete() را نیز بهطور همزمان تعریف نکرده باشد، پرچمگذاری میکند.
این بررسی عملگرهای تعریفشده بهصورت ضمنی، عملگرهای حذفشده (deleted) یا خصوصی (private)، یا عملگرهای جایابی (placement) را پرچمگذاری نمیکند.
این بررسی با قانون DCL54-CPP از استاندارد کدنویسی CERT C++ مطابقت دارد: Overload allocation and deallocation functions as a pair in the same scope https://www.securecoding.cert.org/confluence/display/cplusplus/DCL54-CPP.+Overload+allocation+and+deallocation+functions+as+a+pair+in+the+same+scope.
misc-no-recursion
توابع با اتصالات قوی را مییابد (با تحلیل نمودار فراخوانی برای SCCها یا مؤلفههای بهشدت متصل که حلقه هستند)، هر تابع موجود در چرخه را شناسایی و عیبیابی میکند، و یک نمونه از حلقه نمودار فراخوانی ممکن (بازگشت یا recursion) را نمایش میدهد.
منابع:
- استاندارد کدنویسی CERT C++، قاعده DCL56-CPP: Avoid cycles during initialization of static objects https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL56-CPP.+Avoid+cycles+during+initialization+of+static+objects.
- استاندارد کدنویسی سازمانی JPL برای زبان برنامهنویسی C (JPL DOCID D-60411)، قاعده 2.4 Do not use direct or indirect recursion.
- مشخصات OpenCL، نسخه 1.2، قاعده 6.9 Restrictions: i. Recursion is not supported. https://www.khronos.org/registry/OpenCL/specs/opencl-1.2.pdf.
محدودیتها
- این بررسی فراخوانیهای انجامشده از طریق اشارهگرهای تابع را مدیریت نمیکند
- این بررسی مخربهای C++ را مدیریت نمیکند
misc-non-copyable-objects
بررسی cert-fio38-c بهعنوان نام مستعار این بررسی به اینجا هدایت میشود.
ارجاعزداییها (dereferences) و اعلانهای غیر اشارهگری اشیائی را که نباید با مقدار (by value) منتقل شوند، مانند شیءهای FILE در C یا شیءهای pthread_mutex_t در POSIX، پرچمگذاری میکند.
منابع
این بررسی با قانون FIO38-C از استاندارد کدنویسی CERT C++ مطابقت دارد: Do not copy a FILE object https://www.securecoding.cert.org/confluence/display/c/FIO38-C.+Do+not+copy+a+FILE+object.
misc-non-private-member-variables-in-classes
بررسی cppcoreguidelines-non-private-member-variables-in-classes بهعنوان نام مستعار این بررسی به اینجا هدایت میشود.
کلاسهایی را مییابد که علاوهبر توابع عضو غیرایستای اعلانشده توسط کاربر، شامل اعضای دادهای غیرایستا نیز هستند، و تمامی اعضای دادهای را که با مشخصکننده دسترسی غیر از public اعلان شدهاند عیبیابی میکند. این اعضای دادهای باید بهصورت private اعلان شوند و از طریق توابع عضو دسترسی داشته باشند، بهجای اینکه در معرض کلاسهای مشتقشده یا مصرفکنندگان کلاس قرار گیرند.
گزینهها
- IgnoreClassesWithAllMemberVariablesBeingPublic
- در صورت تنظیم روی true، امکان نادیدهگرفتن کامل کلاسها را فراهم میکند اگر تمام متغیرهای عضو در آن کلاس با مشخصکننده دسترسی public اعلان شده باشند. پیشفرض false است.
- IgnorePublicMemberVariables
- در صورت تنظیم روی true، امکان نادیدهگرفتن (عدم عیبیابی) تمام متغیرهای عضو اعلانشده با مشخصکننده دسترسی public را فراهم میکند. پیشفرض false است.
misc-override-with-different-visibility
بازنویسیهای (overrides) توابع مجازی را که دارای سطح دسترسی (visibility) متفاوتی نسبت به تابع موجود در کلاس پایه هستند شناسایی میکند. این مورد به عنوان مثال شامل حالتی میشود که در آن یک تابع مجازی به عنوان private اعلان شده اما در یک زیرکلاس بازنویسی شده و به صورت public اعلان شده باشد. تغییر شناساییشده عبارت است از تغییر سطح دسترسی ناشی از کلیدواژههای public، protected و private در توابع مجازی بازنویسیشده. این بررسی برای هر تابع مجازی عادی و به صورت اختیاری برای مخربها (destructors) یا عملگرها اعمال میشود. استفاده از کلیدواژه using توسط این بررسی به عنوان تغییر سطح دسترسی در نظر گرفته نمیشود.
class A {
public:
virtual void f_pub();
private:
virtual void f_priv();
};
class B: public A {
public:
void f_priv(); // warning: changed visibility from private to public
private:
void f_pub(); // warning: changed visibility from public to private
};
class C: private A {
// no warning: f_pub becomes private in this case but this is from the
// private inheritance
};
class D: private A {
public:
void f_pub(); // warning: changed visibility from private to public
// 'f_pub' would have private access but is forced to be
// public
};
اگر سطح دسترسی به این شیوه تغییر کند، میتواند نشاندهنده طراحی نادرست یا خطای برنامهنویسی باشد.
اگر یک تابع مجازی در یک زیرکلاس خصوصی (private) باشد اما در کلاس پایه عمومی (public) باشد، همچنان میتوان از طریق اشارهگری به زیرکلاس در صورتی که اشارهگر به نوع پایه تبدیل شود، به آن دسترسی داشت. احتمالاً میتوان به جای آن از ارثبری خصوصی (private inheritance) استفاده کرد.
یک تابع مجازی محافظتشده (protected) که در یک زیرکلاس به صورت عمومی (public) درمیآید ممکن است کاربردهای معتبری داشته باشد، اما تأثیر مشابه (نه دقیقاً یکسان) را میتوان با کلیدواژه using به دست آورد.
گزینهها
- DisallowedVisibilityChange
- کنترل میکند که چه نوع تغییری در سطح دسترسی توسط این بررسی شناسایی شود. مقادیر ممکن عبارتند از any، widening، narrowing. برای نمونه، گزینه widening تنها در صورتی هشدار تولید میکند که سطح دسترسی از محدودتر (private) به کمتر محدودکننده (public) تغییر یابد. مقدار پیشفرض any است.
- CheckDestructors
- اگر روی true تنظیم شود، این بررسی روی مخربها نیز اعمال میشود. در غیر این صورت مخربها توسط این بررسی نادیده گرفته میشوند. مقدار پیشفرض false است.
- CheckOperators
- اگر روی true تنظیم شود، این بررسی روی عملگرهای سربارگذاریشده C++ (به عنوان توابع عضو مجازی) نیز اعمال میشود. این مورد شامل سایر توابع عضو ویژه (مانند تبدیلات) نیز میشود. این گزینه احتمالاً تنها در موارد نادری کاربرد دارد زیرا عملگرها و تبدیلات به ندرت توابع مجازی هستند. مقدار پیشفرض false است.
- IgnoredFunctions
- این گزینه میتواند برای نادیده گرفتن بررسی در توابع مشخص استفاده شود. برای پیکربندی این گزینه، باید فهرستی از نامهای توابع که با نقطه-ویرگول (semicolon) از هم جدا شدهاند ارائه شود. این فهرست میتواند شامل عبارات باقاعده (regular expressions) باشد، از این طریق میتوان تمام توابع یک کلاس خاص (مانند MyClass::.*) یا یک تابع خاص از هر کلاسی (مانند my_function یا ::.*::my_function) را انتخاب کرد. تطبیق نام توابع بر اساس کلاس پایه انجام میشود. مقدار پیشفرض یک رشته خالی است.
misc-predictable-rand
درباره استفاده از std::rand() هشدار میدهد. مولدهای اعداد شبهتصادفی از الگوریتمهای ریاضی برای تولید توالیای از اعداد با ویژگیهای آماری خوب استفاده میکنند، اما اعداد تولیدشده واقعاً تصادفی نیستند. تابع std::rand() یک دانه یا سید (عدد) میگیرد، یک عملیات ریاضی روی آن اجرا میکند و نتیجه را برمیگرداند. با دستکاری دانه (seed)، نتیجه میتواند قابل پیشبینی باشد.
مراجع
این بررسی با قواعد استانداردهای کدنویسی CERT C زیر مطابقت دارد: MSC30-C. Do not use the rand() function for generating pseudorandom numbers https://wiki.sei.cmu.edu/confluence/display/c/MSC30-C.+Do+not+use+the+rand%28%29+function+for+generating+pseudorandom+numbers. MSC50-CPP. Do not use std::rand() for generating pseudorandom numbers https://wiki.sei.cmu.edu/confluence/display/cplusplus/MSC50-CPP.+Do+not+use+std%3A%3Arand%28%29+for+generating+pseudorandom+numbers.
misc-redundant-expression
عبارتهای زائد را که معمولاً خطاهایی ناشی از کپی-پیست هستند شناسایی میکند.
بسته به عملگر، عبارتها ممکن است:
- زائد باشند،
- همواره true باشند،
- همواره false باشند،
- همواره یک مقدار ثابت (صفر یا یک) باشند.
مثالها:
((x+1) | (x+1)) // (x+1) is redundant (p->x == p->x) // always true (p->x < p->x) // always false (speed - speed + 1 == 12) // speed - speed is always zero int b = a | 4 | a // identical expr on both sides ((x=1) | (x=1)) // expression is identical (DEFINE_1 | DEFINE_1) // same macro on the both sides ((DEF_1 + DEF_2) | (DEF_1+DEF_2)) // expressions differ in spaces only
اعداد ممیز شناور (Floats) مدیریت میشوند مگر در حالتی که NaNها به این صورت بررسی شوند:
int TestFloat(float F) {
if (F == F) // Identical float values used
return 1;
return 0;
}
int TestFloat(float F) {
// Testing NaN.
if (F != F && F == F) // does not warn
return 1;
return 0;
}
misc-static-assert
بررسی cert-dcl03-c به عنوان یک نام مستعار برای این بررسی به اینجا تغییر مسیر میدهد.
در صورتی که شرط در زمان کامپایل قابل ارزیابی باشد، assert() را با static_assert() جایگزین میکند.
شرط static_assert() در زمان کامپایل ارزیابی میشود که ایمنتر و کارآمدتر است.
misc-throw-by-value-catch-by-reference
بررسیهای cert-err09-cpp و cert-err61-cpp به عنوان نامهای مستعار این بررسی به اینجا تغییر مسیر میدهند.
نقضهای قاعدهٔ "Throw by value, catch by reference" را مییابد که به عنوان مثال در کتاب "C++ Coding Standards" نوشتهٔ H. Sutter و A. Alexandrescu، و همچنین در قاعدهٔ ERR61-CPP از استاندارد کدنویسی CERT C++ با عنوان Catch exceptions by lvalue reference https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR61-CPP.+Catch+exceptions+by+lvalue+reference مطرح شده است.
- استثناها:
- پرتاب رشتههای لیترال با وجود اشارهگر بودن پرچمگذاری نخواهد شد. آنها در معرض تکهتکهشدن (slicing) نیستند و استفاده از رشتههای لیترال امری مصطلح است.
- گرفتن اشارهگرهای نویسهای (char، wchar_t، انواع نویسهای یونیکد) به منظور فراهم کردن امکان دریافت رشتههای لیترال پرچمگذاری نخواهد شد.
- مقدارهای نامگذاریشدهای که منتقل شدهاند (moved named values) به عنوان پرتاب نکردن یک متغیر موقت ناشناس پرچمگذاری نخواهند شد. در این حالت میتوان مطمئن بود که کاربر میداند که شیء در خارج از بلوکهای catch رسیدگیکننده به خطا قابل دسترسی نیست.
- پرتاب پارامترهای تابع به عنوان عدم پرتاب یک متغیر موقت ناشناس پرچمگذاری نخواهد شد. این امر اجازه میدهد تا از توابع کمکی برای پرتاب استفاده شود.
- پرتاب مجدد (re-throw) متغیرهای استثنای دریافتشده به عنوان عدم پرتاب یک متغیر موقت ناشناس پرچمگذاری نخواهد شد. هرچند این کار معمولاً تنها با نوشتن throw; انجامپذیر است، اما در کدهای واقعی به اندازه کافی رخ میدهد.
گزینهها
- CheckThrowTemporaries
- شناسایی نقضهای توصیهٔ ERR09-CPP در CERT با عنوان Throw anonymous temporaries را فعال میکند. پیشفرض true است.
- WarnOnLargeObject
- همچنین برای هر شیء بزرگ و بدیهی (trivial) که بر اساس مقدار گرفته شده باشد هشدار میدهد. گرفتن یک شیء بزرگ بر اساس مقدار خطرناک نیست، اما تأثیر منفی بر عملکرد میگذارد. حداکثر اندازهٔ شیئی که دریافت آن بدون هشدار مجاز است را میتوان با استفاده از گزینهٔ MaxSize تنظیم کرد. پیشفرض false است.
- MaxSize
- حداکثر اندازهٔ شیئی که دریافت آن بدون هشدار مجاز است را مشخص میکند. تنها زمانی اعمال میشود که WarnOnLargeObject روی true تنظیم شده باشد. اگر این گزینه توسط کاربر روی std::numeric_limits<uint64_t>::max() تنظیم شود، به مقدار پیشفرض بازمیگردد. پیشفرض اندازهٔ size_t است.
misc-unconventional-assign-operator
اعلانهای عملگرهای انتساب با انواع بازگشتی و/یا انواع آرگومان نادرست، و تعاریف با نوع بازگشتی درست اما عبارتهای return اشتباه را پیدا میکند.
- نوع بازگشتی باید Class& باشد.
- انتساب میتواند از نوع کلاس بر اساس مقدار، ارجاع lvalue ثابت (const)، ارجاع rvalue غیرثابت، یا از یک نوع کاملاً متفاوت (مانند int) باشد.
- عملگرهای private و حذفشده (deleted) نادیده گرفته میشوند.
- عملگر همیشه باید *this را برگرداند.
misc-uniqueptr-reset-release
عبارت unique_ptr::reset(release()) را پیدا کرده و با std::move() جایگزین میکند.
مثال:
std::unique_ptr<Foo> x, y; x.reset(y.release()); -> x = std::move(y);
اگر y از قبل یک rvalue باشد، std::move() اضافه نخواهد شد. x و y میتوانند std::unique_ptr<Foo>* نیز باشند.
گزینهها
- IncludeStyle
- رشتهای که مشخص میکند کدام سبک include استفاده شود: llvm یا google. پیشفرض llvm است.
misc-unused-alias-decls
اعلانهای بلااستفادهٔ نام مستعارِ فضای نام (namespace alias) را پیدا میکند.
namespace my_namespace {
class C {};
}
namespace unused_alias = ::my_namespace;
misc-unused-parameters
پارامترهای استفادهنشدهٔ توابع را مییابد. پارامترهای استفادهنشده ممکن است نشاندهندهٔ یک باگ در کد باشند (برای مثال زمانی که به اشتباه از یک پارامتر دیگر استفاده شده باشد). اصلاحات پیشنهادی یا نام پارامتر را کامنت میکنند یا در صورتی که تمام فراخوانهای تابع در همان واحد ترجمه (translation unit) باشند و امکان بهروزرسانی آنها وجود داشته باشد، پارامتر را کاملاً حذف میکنند.
این بررسی مشابه عیبیابی کامپایلر با فلگ -Wunused-parameter است و میتواند برای آمادهسازی یک پایگاهکد جهت فعالسازی آن عیبیابی مورد استفاده قرار گیرد. به طور پیشفرض این بررسی مداراگرتر است (به StrictMode <#cmdoption-arg-StrictMode> مراجعه کنید).
void a(int i) { /*some code that doesn't use `i`*/ }
// becomes
void a(int /*i*/) { /*some code that doesn't use `i`*/ }
static void staticFunctionA(int i);
static void staticFunctionA(int i) { /*some code that doesn't use `i`*/ }
// becomes
static void staticFunctionA()
static void staticFunctionA() { /*some code that doesn't use `i`*/ }
گزینهها
- StrictMode
- هنگامی که روی false باشد (مقدار پیشفرض)، این بررسی پارامترهایی که به وضوح استفاده نشدهاند را نادیده میگیرد؛ یعنی زمانی که بدنه تابع مربوطه خالی باشد (و در مورد سازندهها - هیچ مقداردهی اولیهای در سازنده وجود نداشته باشد). زمانی که بدنه تابع خالی است، بعید است که یک پارامتر استفادهنشده از دید یک خواننده انسان دور بماند و اساساً جایی برای پنهان شدن یک باگ وجود ندارد.
- IgnoreVirtual
- تعیین میکند که آیا پارامترهای متدهای مجازی (virtual) باید بررسی شوند یا خیر. برای نادیده گرفتن آنها، مقدار را روی true تنظیم کنید. مقدار پیشفرض false است.
misc-unused-using-decls
اعلانهای using بدون استفاده را مییابد.
اعلانهای using استفادهنشده در فایلهای هدر تشخیص داده نخواهند شد، چرا که این اعلانها بخشی از API عمومی هدر محسوب میشوند. پسوندهای مجاز فایلهای هدر را میتوان از طریق گزینه سراسری HeaderFileExtensions پیکربندی کرد.
مثال:
// main.cpp
namespace n { class C; }
using n::C; // Never actually used.
misc-use-anonymous-namespace
مواردی از توابع یا متغیرهای static اعلانشده در دامنه عمومی (global scope) را مییابد که میتوان آنها را به جای آن به یک فضای نام ناشناس (anonymous namespace) منتقل کرد.
فضاهای نام ناشناس طبق استاندارد C++ «جایگزین برتر» هستند. استفاده از static ابتدا برای منسوخ شدن (deprecation) پیشنهاد شد، اما بعداً برای حفظ سازگاری با C از حالت منسوخ خارج شد [1]. عبارت static یک مفهوم چندمعنایی است که در زمینههای مختلف معانی متفاوتی دارد، بنابراین میتواند باعث سردرگمی شود.
موارد زیر در استفاده از static بررسی و گزارش نخواهند شد:
- توابع یا متغیرها در فایلهای هدر، زیرا فضاهای نام ناشناس در هدرها یک الگوی نامناسب (antipattern) در نظر گرفته میشوند. پسوندهای مجاز فایلهای هدر را میتوان از طریق گزینه سراسری HeaderFileExtensions پیکربندی کرد.
- متغیرهای const یا constexpr، زیرا آنها از پیش در C++ دارای پیوند درونی (internal linkage) ضمنی هستند.
مثالها:
// Bad
static void foo();
static int x;
// Good
namespace {
void foo();
int x;
} // namespace
[1] Undeprecating static https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1012
misc-use-internal-linkage
متغیرها، توابع و کلاسهایی را شناسایی میکند که میتوان آنها را به عنوان static علامتگذاری کرد یا (در C++) به یک فضای نام ناشناس منتقل نمود تا پیوند درونی (internal linkage) اعمال شود.
هر موجودیتی که فقط درون یک فایل مورد استفاده قرار میگیرد باید پیوند درونی داشته باشد. انجام این کار اطلاعات بیشتری در اختیار کامپایلر قرار میدهد و به آن اجازه میدهد کدهای مرده (dead code) را بهتر حذف کرده و بهینهسازیهای تهاجمیتری انجام دهد.
مثال:
int v1; // can be marked as static
void fn1() {} // can be marked as static
// already declared as extern
extern int v2;
void fn3(); // without function body in all declaration, maybe external linkage
void fn3();
// === C++-specific ===
struct S1 {}; // can be moved into anonymous namespace
namespace {
// already in anonymous namespace
int v2;
void fn2();
struct S2 {};
}
// export declarations
export void fn4() {}
export namespace t { void fn5() {} }
export int v2;
export class C {};
گزینهها
- FixMode
- نوع اصلاحی که این بررسی باید ارائه دهد را تعیین میکند. مقدار پیشفرض UseStatic است.
- None اصلاح خودکار انجام ندهد.
- UseStatic افزودن static برای متغیرها و توابع دارای پیوند درونی.
- AnalyzeFunctions
- آیا پیشنهاد اعمال پیوند درونی برای توابع ارائه شود یا خیر. مقدار پیشفرض true است.
- AnalyzeVariables
- آیا پیشنهاد اعمال پیوند درونی برای متغیرها ارائه شود یا خیر. مقدار پیشفرض true است.
- AnalyzeTypes
- (فقط در C++) آیا پیشنهاد اعمال پیوند درونی برای انواع تعریفشده توسط کاربر (structها، classها، unionها و enumها) ارائه شود یا خیر. مقدار پیشفرض true است.
modernize-avoid-bind
این بررسی کاربردهای std::bind و boost::bind را مییابد و آنها را با عبارات لامبدا (lambdas) جایگزین میکند. لامبداها از تسخیر مقداری (value-capture) استفاده خواهند کرد، مگر اینکه تسخیر ارجاعی (reference capture) صراحتاً با استفاده از std::ref یا boost::ref درخواست شده باشد.
از انواع مختلف فراخوانپذیرها (callables) از جمله متدهای عضو (member functions)، شیءهای تابعی (function objects) و توابع آزاد (free functions) و تمامی حالتهای مختلف آنها پشتیبانی میکند. هر چیزی که بتوان به عنوان آرگومان اول به bind پاس داد باید قابل تشخیص باشد. در حال حاضر، تنها حالت شناختهشدهای که در آن اصلاح خودکار (fix-it) پشتیبانی نمیشود این است که یک placeholder یکسان چندین بار در فهرست پارامترها مشخص شده باشد.
فرض کنید کد زیر وجود دارد:
int add(int x, int y) { return x + y; }
سپس کد:
void f() {
int x = 2;
auto clj = std::bind(add, x, _1);
}
با این کد جایگزین میشود:
void f() {
int x = 2;
auto clj = [=](auto && arg1) { return add(x, arg1); };
}
خواندن کدهای شامل std::bind میتواند دشوار باشد و به دلیل اطلاعات مربوط به نوع داده (type information) که در لامبداهای معادل تولید نمیشود، میتواند منجر به ایجاد فایلهای شیء (object files) و فایلهای اجرایی باینری با حجم بزرگتر گردد.
گزینهها
- PermissiveParameterList
- اگر این گزینه روی true تنظیم شود، بررسی عبارت auto&&... را به انتهای هر فهرست پارامتر جاینگهدار (placeholder) اضافه میکند. بدون این گزینه، ممکن است یک تبدیل اصلاحی (fix-it) در مواردی که نتیجهٔ bind در بستر یک فانکتور با نوع حذفشده (type erased) مانند std::function استفاده میشود که آرگومانهای ناهمسان را میپذیرد، تبدیل نادرستی انجام دهد. مقدار پیشفرض false است.
برای مثال:
int add(int x, int y) { return x + y; }
int foo() {
std::function<int(int,int)> ignore_args = std::bind(add, 2, 2);
return ignore_args(3, 3);
}
کد معتبری است و مقدار 4 را برمیگرداند. مقادیر واقعی ارسالشده به ignore_args به سادگی نادیده گرفته میشوند. بدون PermissiveParameterList، این کد به صورت زیر تبدیل میشود:
int add(int x, int y) { return x + y; }
int foo() {
std::function<int(int,int)> ignore_args = [] { return add(2, 2); }
return ignore_args(3, 3);
}
که کامپایل نخواهد شد، زیرا لامبدا شامل یک operator() که ۲ آرگومان بپذیرد نیست. در صورت فعال بودن فهرست پارامتر مجاز (permissive parameter list)، در عوض کد زیر تولید میشود:
int add(int x, int y) { return x + y; }
int foo() {
std::function<int(int,int)> ignore_args = [](auto&&...) { return add(2, 2); }
return ignore_args(3, 3);
}
که درست است.
اجرای این بررسی نیازمند استفاده از C++14 یا بالاتر است.
modernize-avoid-c-arrays
بررسی cppcoreguidelines-avoid-c-arrays به عنوان یک نام مستعار برای این بررسی به اینجا هدایت میشود.
بررسی hicpp-avoid-c-arrays به عنوان یک نام مستعار برای این بررسی به اینجا هدایت میشود.
انواع آرایههای به سبک C را پیدا کرده و استفاده از std::array<> / std::vector<> را توصیه میکند. تمامی انواع آرایههای C شناسایی و عیبیابی میشوند.
برای پارامترهایی با نوع آرایهٔ ناکامل به سبک C، بهتر است از std::span / gsl::span به عنوان جایگزین استفاده شود.
با این حال، اصلاحات خودکار (fix-it) در فایلهای سرآیند بالقوه خطرناک هستند و بنابراین در حال حاضر ارائه نمیشوند.
int a[] = {1, 2}; // warning: do not declare C-style arrays, use 'std::array' instead
int b[1]; // warning: do not declare C-style arrays, use 'std::array' instead
void foo() {
int c[b[0]]; // warning: do not declare C VLA arrays, use 'std::vector' instead
}
template <typename T, int Size>
class array {
T d[Size]; // warning: do not declare C-style arrays, use 'std::array' instead
int e[1]; // warning: do not declare C-style arrays, use 'std::array' instead
};
array<int[4], 2> d; // warning: do not declare C-style arrays, use 'std::array' instead
using k = int[4]; // warning: do not declare C-style arrays, use 'std::array' instead
با این حال، کدهای extern "C" نادیده گرفته میشوند، زیرا اشتراکگذاری چنین سرآیندهایی بین کدهای C و کدهای C++ رایج است.
// Some header
extern "C" {
int f[] = {1, 2}; // not diagnosed
int j[1]; // not diagnosed
inline void bar() {
{
int j[j[0]]; // not diagnosed
}
}
}
به طور مشابه، تابع main() نادیده گرفته میشود. پارامتر دوم و سوم آن میتواند char* argv[] یا char** argv باشد، اما نمیتواند std::array<> باشد.
گزینهها
- AllowStringArrays
- هنگامی که روی true تنظیم شود (پیشفرض false است)، متغیرهایی از نوع آرایهٔ نویسهای با طول استنتاجشده که مستقیماً از رشتههای لفظی (string literals) مقداردهی اولیه شدهاند، نادیده گرفته میشوند. این گزینه بر مواردی که طول قابل استنتاج نیست و مشابه اشارهگرها هستند، مانند اعضای کلاس و پارامترها، تأثیری ندارد. مثال:
const char name[] = "Some name";
modernize-avoid-c-style-cast
موارد استفاده از تبدیل نوعهای به سبک C (C-style casts) را پیدا میکند.
تبدیل نوعهای به سبک C میتوانند تبدیلات گوناگونی را انجام دهند (const_cast، static_cast، reinterpret_cast، یا ترکیبی از آنها). این امر آنها را خطرناک میسازد زیرا هدف تبدیل مشخص نیست، و ممکن است به طور خاموش تبدیلات ناامنی بین انواع ناسازگار انجام دهند.
این بررسی مشابه -Wold-style-cast است، اما در برخی موارد اصلاحات خودکار را پیشنهاد میدهد. مکانهای گزارششده نباید با موارد تولیدشده توسط -Wold-style-cast تفاوتی داشته باشند.
مثالها
class A {
public:
std::string v;
};
A a;
double *num = (double*)(&a); // Compiles! Hides danger
// num = static_cast<double*>(&a); // Won't compile (good!)
num = reinterpret_cast<double*>(&a); // Compiles, danger is explicit
مراجع
نام بررسی متناظر در cpplint.py: readability/casting.
modernize-avoid-setjmp-longjmp
تمام عبارتهای فراخوانی شامل setjmp() و longjmp() را در کد C++ علامتگذاری میکند.
به جای آنها باید از مدیریت استثنا با throw و catch استفاده شود.
مراجع
این بررسی با قاعدهٔ استاندارد کدنویسی CERT C++ مطابقت دارد: ERR52-CPP. Do not use setjmp() or longjmp() https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=88046492.
modernize-avoid-variadic-functions
تمام تعاریف توابع (نه اعلانها) از توابع با آرگومان متغیر (variadic) به سبک C را پیدا میکند.
به جای توابع با آرگومان متغیر به سبک C، باید از بسته پارامترهای تابع (parameter pack) در C++ استفاده شود.
مراجع
این بررسی با قاعدهٔ استاندارد کدنویسی CERT C++ مطابقت دارد: DCL50-CPP. Do not define a C-style variadic function https://www.securecoding.cert.org/confluence/display/cplusplus/DCL50-CPP.+Do+not+define+a+C-style+variadic+function.
modernize-concat-nested-namespaces
استفاده از فضاهای نام تودرتو مانند namespace a { namespace b { ... } } را بررسی کرده و پیشنهاد تغییر آن به ساختار موجزتر معرفیشده در C++17 را میدهد: namespace a::b { ... }. فضاهای نام درونخطی (inline) تغییر داده نمیشوند.
به عنوان مثال:
namespace n1 {
namespace n2 {
void t();
}
}
namespace n3 {
namespace n4 {
namespace n5 {
void t();
}
}
namespace n6 {
namespace n7 {
void t();
}
}
}
// in c++20
namespace n8 {
inline namespace n9 {
void t();
}
}
به شکل زیر تغییر خواهد یافت:
namespace n1::n2 {
void t();
}
namespace n3 {
namespace n4::n5 {
void t();
}
namespace n6::n7 {
void t();
}
}
// in c++20
namespace n8::inline n9 {
void t();
}
modernize-deprecated-headers
برخی از فایلهای هدر کتابخانهٔ C در C++ منسوخ شدهاند و دیگر استفاده از آنها در پایگاههای کد C++ توصیه نمیشود. برخی از آنها هیچ تأثیری در C++ ندارند. برای جزئیات بیشتر به بخش [depr.c.headers] در استاندارد C++14 مراجعه کنید.
این بررسی، هدرهای کتابخانه استاندارد C را با معادلهای C++ آنها جایگزین کرده و موارد زائد را حذف میکند.
// C++ source file... #include <assert.h> #include <stdbool.h> // becomes #include <cassert> // No 'stdbool.h' here.
نکتهٔ مهم: استاندارد تضمین نمیکند که هدرهای C++ دقیقاً تمام همان توابع را در فضای نام سراسری (global namespace) اعلان کنند. این بررسی در شکل فعلی خود میتواند کدهایی را که از نمادهای کتابخانه در فضای نام سراسری استفاده میکنند، با شکست مواجه کند.
- <assert.h>
- <complex.h>
- <ctype.h>
- <errno.h>
- <fenv.h> // deprecated since C++11
- <float.h>
- <inttypes.h>
- <limits.h>
- <locale.h>
- <math.h>
- <setjmp.h>
- <signal.h>
- <stdarg.h>
- <stddef.h>
- <stdint.h>
- <stdio.h>
- <stdlib.h>
- <string.h>
- <tgmath.h> // deprecated since C++11
- <time.h>
- <uchar.h> // deprecated since C++11
- <wchar.h>
- <wctype.h>
اگر استاندارد مشخصشده قدیمیتر از C++11 باشد، این بررسی فقط هدرهای منسوخشده پیش از C++11 را جایگزین میکند، در غیر این صورت -- تمام هدرهایی که در فهرست قبلی آمدهاند جایگزین خواهند شد.
این هدرها هیچ اثری در C++ ندارند:
- <iso646.h>
- <stdalign.h>
- <stdbool.h>
این بررسیکننده از دستورات include درون بلوکهای extern "C" { ... } چشمپوشی میکند، چرا که ممکن است یک کتابخانه بخواهد رابط برنامهنویسی (API) مشخصی را برای کتابخانههای C و C++ ارائه دهد.
// C++ source file...
extern "C" {
#include <assert.h> // Left intact.
#include <stdbool.h> // Left intact.
}
گزینهها
- CheckHeaderFile
- ابزار clang-tidy نمیتواند تشخیص دهد که آیا فایل هدر include شده توسط فایل منبع C++ کنونیِ تحت تحلیل، توسط هیچ فایل منبع C دیگری include نشده است یا خیر. بنابراین، برای جلوگیری از مثبت کاذب (false-positive) و راهنماییهای تصحیح (fixit-hints) نادرست، از صدور گزارشها در فایلهای هدر صرفنظر میکنیم. کاربر در صورتی که بداند فایلهای هدر در پروژه فقط توسط فایلهای منبع C++ استفاده میشوند، میتواند این گزینه را روی true تنظیم کند. مقدار پیشفرض false است.
modernize-deprecated-ios-base-aliases
استفاده از نوعهای عضو منسوخشدهی std::ios_base را شناسایی کرده و مواردی را که معادلِ غیرمنسوخ دارند، جایگزین میکند.
| نوع عضو منسوخشده | جایگزین |
| std::ios_base::io_state | std::ios_base::iostate |
| std::ios_base::open_mode | std::ios_base::openmode |
| std::ios_base::seek_dir | std::ios_base::seekdir |
| std::ios_base::streamoff | |
| std::ios_base::streampos |
modernize-loop-convert
این بررسی حلقههای for(...; ...; ...) را به استفاده از حلقههای جدید بر مبنای بازه (range-based) در C++11 تبدیل میکند.
سه نوع حلقه قابل تبدیل هستند:
- حلقهها روی آرایههای ایستا تخصیصیافته.
- حلقهها روی کانتینرها، با استفاده از تکرارکنندهها (iterators).
- حلقهها روی کانتینرهای شبهآرایه، با استفاده از operator[] و at().
گزینه MinConfidence
risky
در حلقههایی که عبارت کانتینر پیچیدهتر از صرفاً یک ارجاع به یک عبارت اعلانشده (متغیر، تابع، enum و غیره) باشد، و بخشی از آن در جای دیگری از حلقه ظاهر شود، به دلیل افزایش خطر تغییر در معناشناسی کد (semantics)، سطح اطمینان نسبت به تبدیل کاهش مییابد. تبدیلها برای این حلقهها به عنوان risky علامتگذاری میشوند، و بنابراین تنها در صورتی تبدیل خواهند شد که حداقل سطح اطمینانِ لازم روی risky تنظیم شده باشد.
int arr[10][20]; int l = 5; for (int j = 0; j < 20; ++j) int k = arr[l][j] + l; // using l outside arr[l] is considered risky for (int i = 0; i < obj.getVector().size(); ++i) obj.foo(10); // using 'obj' is considered risky
برای مثالی از تبدیل نادرست در حالتی که حداقل سطح اطمینان لازم روی risky تنظیم شده باشد، بخش Range-based loops evaluate end() only once را مشاهده کنید.
reasonable (پیشفرض)
اگر یک حلقه پس از هر تکرار .end() یا .size() را فراخوانی کند، تبدیل آن حلقه به عنوان reasonable علامتگذاری میشود، و در نتیجه اگر سطح اطمینان لازم روی reasonable (پیشفرض) یا پایینتر تنظیم شده باشد تبدیل خواهد شد.
// using size() is considered reasonable for (int i = 0; i < container.size(); ++i) cout << container[i];
safe
هر حلقهٔ دیگری که با معیارهای بالا برای علامتگذاری به عنوان risky یا reasonable مطابقت نداشته باشد، به عنوان safe علامتگذاری میشود، و بنابراین اگر سطح اطمینان لازم روی safe یا پایینتر تنظیم شده باشد تبدیل خواهد شد.
int arr[] = {1,2,3};
for (int i = 0; i < 3; ++i)
cout << arr[i];
مثال
حالت اصلی:
const int N = 5;
int arr[] = {1,2,3,4,5};
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// safe conversion
for (int i = 0; i < N; ++i)
cout << arr[i];
// reasonable conversion
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
cout << *it;
// reasonable conversion
for (vector<int>::iterator it = begin(v); it != end(v); ++it)
cout << *it;
// reasonable conversion
for (vector<int>::iterator it = std::begin(v); it != std::end(v); ++it)
cout << *it;
// reasonable conversion
for (int i = 0; i < v.size(); ++i)
cout << v[i];
// reasonable conversion
for (int i = 0; i < size(v); ++i)
cout << v[i];
پس از اعمال بررسی با حداقل سطح اطمینان تنظیمشده روی reasonable (پیشفرض):
const int N = 5;
int arr[] = {1,2,3,4,5};
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// safe conversion
for (auto & elem : arr)
cout << elem;
// reasonable conversion
for (auto & elem : v)
cout << elem;
// reasonable conversion
for (auto & elem : v)
cout << elem;
پشتیبانی از تکرارگر معکوس (Reverse Iterator)
این مبدل همچنین قادر به تبدیل حلقههای تکرارگری است که از rbegin و rend برای پیمایش رو به عقب روی یک نگهدارنده (container) استفاده میکنند. بهطور پیشفرض، این عمل در حالت C++20 با استفاده از کتابخانه ranges بهصورت خودکار انجام خواهد شد، با این حال میتوان این بررسی را طوری پیکربندی کرد که بدون C++20 نیز با مشخص کردن تابعی برای معکوس کردن بازه و در صورت تمایل فایل هدر محل تعریف آن تابع، کار کند.
گزینهها
- UseCxx20ReverseRanges
- هنگامی که روی true تنظیم شود، در صورت قرار داشتن در حالت C++20 یا بالاتر، حلقهها را با استفاده از std::views::reverse تبدیل میکند. مقدار پیشفرض true است.
- MakeReverseRangeFunction
- تابعی را مشخص میکند که برای معکوس کردن یک جفت تکرارگر به کار میرود؛ این تابع باید کلاسی با متدهای rbegin و rend را بپذیرد و کلاسی با متدهای begin و end برگرداند که به ترتیب متدهای rbegin و rend را فراخوانی کنند. نمونههای متداول std::views::reverse و llvm::reverse هستند. مقدار پیشفرض یک رشته خالی است.
- MakeReverseRangeHeader
- فایل هدری را مشخص میکند که MakeReverseRangeFunction در آن اعلان شده است. برای مثالهای قبلی، این گزینه به ترتیب روی range/v3/view/reverse.hpp و llvm/ADT/STLExtras.h تنظیم خواهد شد. اگر این گزینه یک رشته خالی باشد و MakeReverseRangeFunction تنظیم شده باشد، بررسی با این فرض ادامه مییابد که تابع هماکنون در واحد ترجمه موجود است. میتوان این مقدار را درون براکتهای زاویهدار قرار داد تا مشخص شود include به عنوان یک include سیستمی اضافه شود. مقدار پیشفرض یک رشته خالی است.
- IncludeStyle
- رشتهای که سبک include مورد استفاده را مشخص میکند: llvm یا google. مقدار پیشفرض llvm است.
محدودیتها
موقعیتهای خاصی وجود دارد که در آنها ممکن است ابزار بهصورت نادرست دست به تبدیلهایی بزند که موجب حذف اطلاعات و تغییر معناشناسی (semantics) کد شود. کاربران ابزار باید از رفتار و محدودیتهای این بررسی که در موارد زیر شرح داده شده است آگاه باشند.
کامنتهای درون سرآیند حلقه
کامنتهای درون سرآیند حلقه اصلی نادیده گرفته شده و هنگام تبدیل حذف میشوند.
for (int i = 0; i < N; /* This will be deleted */ ++i) { }
حلقههای مبتنی بر بازه end() را تنها یکبار ارزیابی میکنند
حلقه for مبتنی بر بازه C++11 متد .end() را در طول مقداردهی اولیه حلقه تنها یک بار فراخوانی میکند. اگر در حلقه اصلی .end() پس از هر تکرار فراخوانی شود، معناشناسی حلقه تبدیلشده ممکن است متفاوت باشد.
// The following is semantically equivalent to the C++11 range-based for loop,
// therefore the semantics of the header will not change.
for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
// Instead of calling .end() after each iteration, this loop will be
// transformed to call .end() only once during the initialization of the loop,
// which may affect semantics.
for (iterator it = container.begin(); it != container.end(); ++it) { }
همانطور که در بالا توضیح داده شد، فراخوانی توابع عضو نگهدارنده درون بدنه حلقه risky (پرخطر) در نظر گرفته میشود. اگر تابع عضو فراخوانیشده موجب تغییر نگهدارنده شود، به دلیل اینکه .end() تنها یکبار فراخوانی میشود، معناشناسی حلقه تبدیلشده متفاوت خواهد بود.
bool flag = false;
for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
// Add a copy of the first element to the end of the vector.
if (!flag) {
// This line makes this transformation 'risky'.
vec.push_back(*it);
flag = true;
}
cout << *it;
}
کد اصلی بالا محتویات نگهدارنده را شامل عنصری که به تازگی اضافه شده است چاپ میکند، در حالی که حلقه تبدیلشده که در زیر نشان داده شده، تنها محتویات اولیه را چاپ کرده و عنصر اضافه شده جدید را چاپ نخواهد کرد.
bool flag = false;
for (auto & elem : vec) {
// Add a copy of the first element to the end of the vector.
if (!flag) {
// This line makes this transformation 'risky'
vec.push_back(elem);
flag = true;
}
cout << elem;
}
همچنین اگر .end() دارای اثرات جانبی (side effects) باشد، معناشناسی تحت تأثیر قرار خواهد گرفت. به عنوان مثال، در حالتی که فراخوانیهای .end() لاگ میشوند، اگر .end() در ابتدا پس از هر تکرار فراخوانی میشد، معناشناسی در حلقه تبدیلشده تغییر خواهد کرد.
iterator end() {
num_of_end_calls++;
return container.end();
}
عملگر بازتعریفشده operator->() با اثرات جانبی
به طور مشابه، اگر operator->() طوری بارگذاری اضافه (overload) شده باشد که اثرات جانبی، مانند لاگگیری، داشته باشد، معناشناسی تغییر خواهد کرد. اگر از operator->() تکرارگر در حلقه اصلی استفاده شده باشد، به دلیل بازارجاعی ضمنی (implicit dereference) به عنوان بخشی از حلقه for مبتنی بر بازه، با <container element>.<member> جایگزین خواهد شد. بنابراین هرگونه اثر جانبی operator->() بارگذاری اضافه شده دیگر اجرا نخواهد شد.
for (iterator it = c.begin(); it != c.end(); ++it) {
it->func(); // Using operator->()
}
// Will be transformed to:
for (auto & elem : c) {
elem.func(); // No longer using operator->()
}
اشارهگرها و ارجاعها به کانتینرها
اگرچه بخش عمدهای از تحلیل ریسک این بررسی به تعیین اینکه آیا تکرارگر (iterator) یا کانتینر درون حلقه تغییر یافته است یا خیر اختصاص دارد، اما دور زدن این تحلیل از طریق دسترسی و تغییر کانتینر به وسیله یک اشارهگر یا ارجاع ممکن است.
اگر کانتینر به جای استفاده از اشارهگر یا ارجاع به طور مستقیم استفاده میشد، تبدیل زیر تنها در سطح risky اعمال میشد، چرا که فراخوانی یک تابع عضو از کانتینر به عنوان risky در نظر گرفته میشود. این بررسی نمیتواند عبارتهای مرتبط با کانتینر را که متفاوت از عبارت استفاده شده در سرآیند حلقه هستند شناسایی کند، بنابراین تبدیل زیر در نهایت در سطح safe انجام میشود.
vector<int> vec;
vector<int> *ptr = &vec;
vector<int> &ref = vec;
for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
if (!flag) {
// Accessing and modifying the container is considered risky, but the risk
// level is not raised here.
ptr->push_back(*it);
ref.push_back(*it);
flag = true;
}
}
OpenMP
از آنجا که حلقههای for بر پایه بازه (range-based) تنها از نسخه OpenMP 5 به بعد در دسترس هستند، این بررسی نباید بر روی کدهایی با نیاز به سازگاری با نسخههای پیشین OpenMP 5 استفاده شود. این بررسی بهعمد هیچ تلاشی برای مستثنی کردن عیبیابیهای نادرست در حلقههای for مربوط به نسخههای قبل از OpenMP 5 انجام نمیدهد.
برای جلوگیری از اعمال شدن (و خراب کردن) این بررسی روی حلقههای for متعلق به OpenMP، در عین حال که همچنان بر روی حلقههای for غیر OpenMP اعمال شود، استفاده از NOLINT (به بخش Suppressing Undesired Diagnostics <#clang-tidy-nolint> مراجعه کنید) روی حلقههای for خاص پیشنهاد میشود.
modernize-macro-to-enum
گروههایی از ماکروهای مجاور را با یک enum ناشناس بدون دامنه (unscoped anonymous enum) جایگزین میکند. استفاده از یک enum ناشناس بدون دامنه تضمین میکند در هر جایی که قبلاً از توکن ماکرو استفاده شده بود، نام شمارنده (enumerator) بتواند با اطمینان استفاده شود.
این بررسی میتواند برای اعمال راهنمای هسته C++ با عنوان Enum.1: Prefer enumerations over macros https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#enum1-prefer-enumerations-over-macros، تحت محدودیتهای ذکر شده در زیر به کار رود.
ماکروهای بالقوه برای جایگزینی باید محدودیتهای زیر را برآورده کنند:
- ماکروها فقط باید به توکنهای لیترال صحیح (integral literal) یا عباراتی از توکنهای لیترال بسط یابند. این عبارت میتواند شامل هر یک از عملگرهای یگانی -، +، ~ یا !، هر یک از عملگرهای دوتایی ,، -، +، *، /، %، &، |، ^، <، >، <=، >=، ==، !=، ||، &&، <<، >> یا <=>، عملگر سهگانه ?: و افزونه GNU آن https://gcc.gnu.org/onlinedocs/gcc/Conditionals.html باشد. عبارتهای داخل پرانتز نیز شناخته میشوند. این امر بیشتر عبارتهای معتبر را شناسایی میکند. به ویژه، عبارتهای دارای عملگر sizeof شناخته نمیشوند.
- ماکروها باید در خطوط متوالی فایل مبدأ تعریف شوند، یا تنها خطوط توضیحات (comment) بین تعاریف ماکروها وجود داشته باشد.
- همه ماکروها باید در همان یک فایل مبدأ تعریف شده باشند.
- ماکروها نباید در داخل یک بلوک کامپایل شرطی تعریف شده باشند. (include guardهای شرطی از این محدودیت مستثنی هستند.)
- ماکروها نباید در مجاورت سایر دستورات پیشپردازنده تعریف شده باشند.
- ماکروها نباید در هیچ دستور شرطی پیشپردازنده استفاده شده باشند.
- ماکروها نباید به عنوان آرگومان برای ماکروهای دیگر استفاده شوند.
- ماکروها نباید لغو تعریف (undefined) شده باشند.
- ماکروها باید در بالاترین سطح (top-level) تعریف شده باشند، نه درون هیچ اعلان یا تعریفی.
هر دستهای از ماکروها که محدودیتهای بالا را برآورده کند به عنوان مجموعهای از مقادیر مناسب برای جایگزینی با یک enum ناشناس در نظر گرفته میشود. از آنجا، توسعهدهنده میتواند در صورت تمایل نامی به enum ناشناس بدهد و بازآفرینی کد (refactoring) را به یک enum دارای دامنه (scoped enum) ادامه دهد. توضیحات موجود در همان خط تعریف ماکرو یا بین تعاریف متوالی ماکرو در خروجی حفظ میشوند. هیچ قالببندی خاصی در جایگزینیهای ارائه شده اعمال نمیشود، اگرچه clang-tidy میتواند به صورت اختیاری تمام اصلاحات را قالببندی کند.
هشدار:
مثالها:
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
#define TM_NONE (-1) // No method selected.
#define TM_ONE 1 // Use tailored method one.
#define TM_TWO 2 // Use tailored method two. Method two
// is preferable to method one.
#define TM_THREE 3 // Use tailored method three.
تبدیل میشود به:
enum {
RED = 0xFF0000,
GREEN = 0x00FF00,
BLUE = 0x0000FF
};
enum {
TM_NONE = (-1), // No method selected.
TM_ONE = 1, // Use tailored method one.
TM_TWO = 2, // Use tailored method two. Method two
// is preferable to method one.
TM_THREE = 3 // Use tailored method three.
};
این بررسی ساخت اشیاء std::shared_ptr را از طریق فراخوانی صریح سازنده به همراه یک عبارت new پیدا کرده و آن را با فراخوانی std::make_shared جایگزین میکند.
auto my_ptr = std::shared_ptr<MyPair>(new MyPair(1, 2)); // becomes auto my_ptr = std::make_shared<MyPair>(1, 2);
این بررسی همچنین فراخوانیهای std::shared_ptr::reset() به همراه یک عبارت new را پیدا کرده و آن را با فراخوانی std::make_shared جایگزین مینماید.
my_ptr.reset(new MyPair(1, 2)); // becomes my_ptr = std::make_shared<MyPair>(1, 2);
گزینهها
- MakeSmartPtrFunction
- رشتهای که نام تابع make-shared-ptr را مشخص میکند. مقدار پیشفرض std::make_shared است.
- MakeSmartPtrFunctionHeader
- رشتهای که هدر مربوط به تابع make-shared-ptr را مشخص میکند. مقدار پیشفرض <memory> است.
- IncludeStyle
- رشتهای که شیوه درج include استفادهشده را مشخص میکند: llvm یا google. مقدار پیشفرض llvm است.
- IgnoreMacros
- اگر روی true تنظیم شود، این بررسی هشداری درون ماکروها صادر نخواهد کرد. پیشفرض true است.
- IgnoreDefaultInitialization
- اگر روی false تنظیم شود، بررسی ویرایشهایی را که مقداردهی اولیه پیشفرض (default initialization) را به مقداردهی اولیه مقداری (value initialization) تبدیل میکنند پیشنهاد نمیدهد، زیرا این کار میتواند باعث افت کارایی شود. مقدار پیشفرض true است.
modernize-make-unique
این بررسی ساخت اشیاء std::unique_ptr را از طریق فراخوانی صریح سازنده به همراه یک عبارت new پیدا کرده و آن را با فراخوانی std::make_unique (معرفیشده در C++14) جایگزین میکند.
auto my_ptr = std::unique_ptr<MyPair>(new MyPair(1, 2)); // becomes auto my_ptr = std::make_unique<MyPair>(1, 2);
این بررسی همچنین فراخوانیهای std::unique_ptr::reset() به همراه یک عبارت new را پیدا کرده و آن را با فراخوانی std::make_unique جایگزین مینماید.
my_ptr.reset(new MyPair(1, 2)); // becomes my_ptr = std::make_unique<MyPair>(1, 2);
گزینهها
- MakeSmartPtrFunction
- رشتهای که نام تابع make-unique-ptr را مشخص میکند. مقدار پیشفرض std::make_unique است.
- MakeSmartPtrFunctionHeader
- رشتهای که هدر مربوط به تابع make-unique-ptr را مشخص میکند. مقدار پیشفرض <memory> است.
- IncludeStyle
- رشتهای که شیوه درج include استفادهشده را مشخص میکند: llvm یا google. مقدار پیشفرض llvm است.
- IgnoreMacros
- اگر روی true تنظیم شود، این بررسی هشداری درون ماکروها صادر نخواهد کرد. پیشفرض true است.
- IgnoreDefaultInitialization
- اگر روی false تنظیم شود، بررسی ویرایشهایی را که مقداردهی اولیه پیشفرض (default initialization) را به مقداردهی اولیه مقداری (value initialization) تبدیل میکنند پیشنهاد نمیدهد، زیرا این کار میتواند باعث افت کارایی شود. مقدار پیشفرض true است.
modernize-min-max-use-initializer-list
فراخوانیهای تو در توی std::min و std::max را در صورت امکان با یک لیست مقداردهی اولیه (initializer list) جایگزین میکند.
برای مثال، کد زیر را در نظر بگیرید:
int a = std::max(std::max(i, j), k);
این بررسی کد بالا را به شکل زیر تبدیل خواهد کرد:
int a = std::max({i, j, k});
ملاحظات مربوط به کارایی
اگرچه این بررسی کد را سادهتر کرده و خوانایی آن را افزایش میدهد، اما ممکن است به دلیل نیاز به کپی کردن اشیاء در initializer list، برای انواع غیربدیهی (non-trivial) منجر به افت کارایی شود.
برای جلوگیری از این مشکل، توصیه میشود برای انواع غیربدیهی از std::ref یا std::cref استفاده شود:
std::string b = std::max({std::ref(i), std::ref(j), std::ref(k)});
گزینهها
- IncludeStyle
- رشتهای که مشخص میکند کدام سبک درج فایلهای سرآیند (include-style) استفاده شود، llvm یا google. مقدار پیشفرض llvm است.
- IgnoreNonTrivialTypes
- یک متغیر بولی که تعیین میکند آیا انواع غیربدیهی نادیده گرفته شوند یا خیر. مقدار پیشفرض true است.
- IgnoreTrivialTypesOfSizeAbove
- یک مقدار صحیح که اندازهای (به بایت) را مشخص میکند که انواع بدیهی بزرگتر از آن نادیده گرفته میشوند. مقدار پیشفرض 32 است.
modernize-pass-by-value
با افزوده شدن مفاهیم انتقال (move semantics) به زبان و بهروزرسانی کتابخانه استاندارد با سازندههای انتقال برای انواع داده متعدد، اکنون دریافت مستقیم یک آرگومان از طریق مقدار (pass-by-value) به جای دریافت با مرجع ثابت (const-reference) و سپس کپی کردن آن، رویکرد مفیدی به شمار میرود. این بررسی به کامپایلر اجازه میدهد تا بهترین شیوه را برای ساخت رونوشت (کپی) انتخاب کند.
این تبدیل معمولاً زمانی مفید است که کد فراخواننده یک rvalue را ارسال کرده و فرض بر این باشد که ساخت با انتقال (move construction) عملیاتی کمهزینه و ارزان است. این مثال کوتاه نحوه ساخته شدن مقدار را نشان میدهد:
void foo(std::string s);
std::string get_str();
void f(const std::string &str) {
foo(str); // lvalue -> copy construction
foo(get_str()); // prvalue -> move construction
}
نکته:
استفاده از Pass-by-value در سازندهها
استفاده از پارامترهای سازنده با مرجع ثابت (const-references) که در فیلدهای کلاس کپی میشوند را جایگزین میکند. سپس پارامتر با استفاده از std::move() انتقال مییابد.
از آنجا که std::move() یک تابع کتابخانهای است که در <utility> اعلان شده، ممکن است اضافه کردن این دستور include لازم باشد. این بررسی در صورت لزوم دایرکتیو include را اضافه خواهد کرد.
#include <string>
class Foo {
public:
- Foo(const std::string &Copied, const std::string &ReadOnly)
- : Copied(Copied), ReadOnly(ReadOnly)
+ Foo(std::string Copied, const std::string &ReadOnly)
+ : Copied(std::move(Copied)), ReadOnly(ReadOnly)
{}
private:
std::string Copied;
const std::string &ReadOnly;
};
std::string get_cwd();
void f(const std::string &Path) {
// The parameter corresponding to 'get_cwd()' is move-constructed. By
// using pass-by-value in the Foo constructor we managed to avoid a
// copy-construction.
Foo foo(get_cwd(), Path);
}
اگر پارامتر بیش از یک بار استفاده شده باشد، هیچ تبدیلی صورت نمیگیرد، زیرا اشیاء منتقلشده دارای وضعیت نامعینی هستند. این بدان معناست که کد زیر بدون تغییر باقی خواهد ماند:
#include <string>
void pass(const std::string &S);
struct Foo {
Foo(const std::string &S) : Str(S) {
pass(S);
}
std::string Str;
};
محدودیتها
وضعیتی که در آن کد تولیدشده ممکن است نادرست باشد زمانی است که شیء ارجاعشده قبل از تخصیص در فهرست اولیهسازی (init-list)، از طریق یک مرجع "مخفی" دستخوش تغییر شود.
مثال:
std::string s("foo");
struct Base {
Base() {
s = "bar";
}
};
struct Derived : Base {
- Derived(const std::string &S) : Field(S)
+ Derived(std::string S) : Field(std::move(S))
{ }
std::string Field;
};
void f() {
- Derived d(s); // d.Field holds "bar"
+ Derived d(s); // d.Field holds "foo"
}
نکتهای درباره تجزیه با تأخیر الگو (delayed template parsing)
هنگامی که قابلیت تجزیه با تأخیر الگو فعال باشد، سازندههایی که بخشی از زمینههای قالببندیشده هستند؛ یعنی سازندههای قالببندیشده، سازندهها در کلاسهای الگو، سازندههای کلاسهای درونیِ کلاسهای الگو و غیره، تبدیل نمیشوند. قابلیت تجزیه با تأخیر الگو بهطور پیشفرض در ویندوز بهعنوان یکی از قابلیتهای الحاقی مایکروسافت فعال است: Clang Compiler User's Manual - Microsoft extensions https://clang.llvm.org/docs/UsersManual.html#microsoft-extensions.
تجزیه با تأخیر الگو میتواند با استفاده از فلگ -fdelayed-template-parsing فعال و با -fno-delayed-template-parsing غیرفعال شود.
مثال:
template <typename T> class C {
std::string S;
public:
= // using -fdelayed-template-parsing (default on Windows)
= C(const std::string &S) : S(S) {}
+ // using -fno-delayed-template-parsing (default on non-Windows systems)
+ C(std::string S) : S(std::move(S)) {}
};
همچنین ببینید:
گزینهها
- IncludeStyle
- رشتهای که مشخص میکند کدام سبک include استفاده شود، llvm یا google. مقدار پیشفرض llvm است.
- ValuesOnly
- وقتی true باشد، این بررسی فقط درباره پارامترهای کپیشدهای هشدار میدهد که از پیش به صورت مقداری ارسال شدهاند. مقدار پیشفرض false است.
modernize-raw-string-literal
این بررسی بهطور گزینشی لیترالهای رشتهای حاوی نویسههای اسکیپشده را با لیترالهای رشتهای خام (raw string literals) جایگزین میکند.
مثال:
const char *const Quotes{"embedded \"quotes\""};
const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"};
const char *const SingleLine{"Single line.\n"};
const char *const TrailingSpace{"Look here -> \n"};
const char *const Tab{"One\tTwo\n"};
const char *const Bell{"Hello!\a And welcome!"};
const char *const Path{"C:\\Program Files\\Vendor\\Application.exe"};
const char *const RegEx{"\\w\\([a-z]\\)"};
تبدیل میشود به:
const char *const Quotes{R"(embedded "quotes")"};
const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"};
const char *const SingleLine{"Single line.\n"};
const char *const TrailingSpace{"Look here -> \n"};
const char *const Tab{"One\tTwo\n"};
const char *const Bell{"Hello!\a And welcome!"};
const char *const Path{R"(C:\Program Files\Vendor\Application.exe)"};
const char *const RegEx{R"(\w\([a-z]\))"};
وجود هر یک از نویسههای اسکیپ زیر میتواند موجب تبدیل رشته به لیترال رشتهای خام شود: \\، \'، \"، \?، و اسکیپهای اکتال یا هگزادسیمال برای نویسههای اسکی (ASCII) قابل چاپ.
یک لیترال رشتهای که تنها حاوی خطهای جدید اسکیپشده باشد، روشی رایج برای نوشتن خطوط خروجی متنی است. اضافه کردن خطهای جدید فیزیکی با لیترالهای رشتهای خام در این حالت احتمالاً به خوانایی آسیب میزند. این لیترالهای رشتهای دستنخورده باقی میمانند.
یک تب افقی، form feed، یا تب عمودیِ اسکیپشده از تبدیل لیترال رشتهای جلوگیری میکند. وجود تب افقی، form feed یا تب عمودی در کد مبدأ از نظر بصری چندان واضح نیست.
گزینهها
- DelimiterStem
- جداکننده سفارشی برای اسکیپ نویسهها در لیترالهای رشتهای خام. در ساختار زیر استفاده میشود: R"stem_delimiter(contents)stem_delimiter". مقدار پیشفرض lit است.
- ReplaceShorterLiterals
- کنترل جایگزینی لیترالهای رشتهای غیرخام کوتاهتر با لیترالهای رشتهای خام طولانیتر. تنظیم این گزینه روی true این جایگزینی را فعال میکند. مقدار پیشفرض false است (لیترالهای کوتاهتر جایگزین نمیشوند).
modernize-redundant-void-arg
پیدا کردن و حذف لیست آرگومانهای زائد void.
- مثالها:
-
کد اولیه کد پس از اعمال اصلاحات int f(void); int f(); int (*f(void))(void); int (*f())(); typedef int (*f_t(void))(void); typedef int (*f_t())(); void (C::*p)(void); void (C::*p)(); C::C(void) {} C::C() {} C::~C(void) {} C::~C() {}
modernize-replace-auto-ptr
این بررسی موارد استفاده از کلاس منسوخشدهٔ std::auto_ptr را با std::unique_ptr (معرفیشده در C++11) جایگزین میکند. انتقال مالکیت، که توسط سازنده کپی و عملگر انتساب انجام میشد، با استفاده از فراخوانیهای صریح std::move() تغییر مییابد تا با نحوه استفاده از std::unique_ptr مطابقت داشته باشد.
مثال مهاجرت:
-void take_ownership_fn(std::auto_ptr<int> int_ptr);
+void take_ownership_fn(std::unique_ptr<int> int_ptr);
void f(int x) {
- std::auto_ptr<int> a(new int(x));
- std::auto_ptr<int> b;
+ std::unique_ptr<int> a(new int(x));
+ std::unique_ptr<int> b;
- b = a;
- take_ownership_fn(b);
+ b = std::move(a);
+ take_ownership_fn(std::move(b));
}
از آنجا که std::move() یک تابع کتابخانهای اعلانشده در <utility> است، ممکن است افزودن این دستور include ضروری باشد. این بررسی در صورت لزوم، دستور include را اضافه خواهد کرد.
محدودیتها
- اگر تغییر هدرها فعال نباشد یا اجازه تغییر یک هدر داده نشده باشد، این بررسی کد خرابی (خطای کامپایل) ایجاد میکند که در آن کد هدرها بدون تغییر باقی مانده در حالی که کد مصرفکنندهٔ آنها تغییر خواهد کرد.
- کد کاربری که یک ارجاع به std::auto_ptr برگرفته از کدی که قابل مهاجرت نیست (مانند هدری از یک کتابخانه شخص ثالث) اعلان میکند، پس از مهاجرت منجر به خطای کامپایل خواهد شد. زیرا نوع ارجاع به std::unique_ptr تغییر مییابد اما نوع برگرداندهشده توسط کتابخانه تغییر نخواهد کرد که این امر باعث اتصال یک ارجاع به std::unique_ptr از یک std::auto_ptr میشود. این الگو منطقی نیست و معمولاً std::auto_ptr به صورت مقداری ذخیره میشود (در غیر این صورت هدف از استفاده از آنها به جای ارجاع یا اشارهگر چیست؟).
// <3rd-party header...> std::auto_ptr<int> get_value(); const std::auto_ptr<int> & get_ref(); // <calling code (with migration)...> -std::auto_ptr<int> a(get_value()); +std::unique_ptr<int> a(get_value()); // ok, unique_ptr constructed from auto_ptr -const std::auto_ptr<int> & p = get_ptr(); +const std::unique_ptr<int> & p = get_ptr(); // won't compile
- •
- قالبهای نمونهسازینشده (Non-instantiated templates) تغییر داده نمیشوند.
template <typename X>
void f() {
std::auto_ptr<X> p;
}
// only 'f<int>()' (or similar) will trigger the replacement.
گزینهها
- IncludeStyle
- یک رشته که سبک include مورد استفاده را مشخص میکند: llvm یا google. پیشفرض llvm است.
modernize-replace-disallow-copy-and-assign-macro
بسطهای ماکروی DISALLOW_COPY_AND_ASSIGN(Type) را پیدا کرده و آنها را با یک سازنده کپی حذفشده (deleted) و یک عملگر انتساب حذفشده جایگزین میکند.
پیش از معرفی کلمه کلیدی delete در C++11، این یک روال معمول بود که سازنده کپی و عملگر انتساب به عنوان اعضای خصوصی (private) اعلان شوند. این کار عملاً آنها را برای API عمومی کلاس غیرقابلاستفاده میکرد.
با ظهور کلمه کلیدی delete در C++11 میتوانیم دسترسی private سازنده کپی و عملگر انتساب را رها کرده و این متدها را به طور کامل حذف (delete) کنیم.
هنگام اجرای این بررسی بر روی کدی مانند این:
class Foo {
private:
DISALLOW_COPY_AND_ASSIGN(Foo);
};
به این صورت تبدیل خواهد شد:
class Foo {
private:
Foo(const Foo &) = delete;
const Foo &operator=(const Foo &) = delete;
};
محدودیتها
- •
- توجه داشته باشید که مثال مهاجرت بالا بخش دسترسی private را دستنخورده باقی میگذارد. ممکن است بخواهید بررسی modernize-use-equals-delete <> را اجرا کنید تا هشدارهایی برای توابع حذفشده در بخشهای private دریافت کنید.
گزینهها
- MacroName
- رشتهای که نام ماکرویی را که بسط آن جایگزین خواهد شد مشخص میکند. پیشفرض DISALLOW_COPY_AND_ASSIGN است.
ببینید: https://en.cppreference.com/w/cpp/language/function#Deleted_functions
modernize-replace-random-shuffle
این بررسی موارد وقوع std::random_shuffle را یافته و آن را با std::shuffle جایگزین میکند. در C++17 دیگر std::random_shuffle در دسترس نخواهد بود و بنابراین باید آن را جایگزین کنیم.
در زیر دو مثال از اینکه چه نوع رخدادهایی پیدا میشوند و دو مثال از آنچه با آن جایگزین خواهد شد آورده شده است.
std::vector<int> v; // First example std::random_shuffle(vec.begin(), vec.end()); // Second example std::random_shuffle(vec.begin(), vec.end(), randomFunc);
هر دوی این مثالها با عبارت زیر جایگزین خواهند شد:
std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
مثال دوم همچنین هشداری دریافت میکند مبنی بر اینکه randomFunc دیگر مانند گذشته پشتیبانی نمیشود، بنابراین اگر کاربر همان قابلیت را میخواهد، باید پیادهسازی randomFunc را تغییر دهد.
یک نکته که در اینجا باید به آن توجه داشت این است که مقداردهی اولیه std::random_device بسیار سنگین است. بنابراین اگر از این کد در جایی که کارایی حیاتی است استفاده میکنید، احتمالاً ترجیح میدهید آن را در جای دیگری مقداردهی اولیه کنید.
نکته دیگر این است که کیفیت تولید بذر (seeding) اصلاح پیشنهادی بسیار ضعیف است: std::mt19937 دارای وضعیت داخلی با ۶۲۴ عدد صحیح ۳۲ بیتی است، اما تنها با یک عدد صحیح بذردهی میشود. بنابراین اگر به تصادفی بودن با کیفیت بالاتری نیاز دارید، باید بذردهی بهتری را در نظر بگیرید، برای مثال:
std::shuffle(v.begin(), v.end(), []() {
std::mt19937::result_type seeds[std::mt19937::state_size];
std::random_device device;
std::uniform_int_distribution<typename std::mt19937::result_type> dist;
std::generate(std::begin(seeds), std::end(seeds), [&] { return dist(device); });
std::seed_seq seq(std::begin(seeds), std::end(seeds));
return std::mt19937(seq);
}());
modernize-return-braced-init-list
فراخوانیهای صریح سازنده را در دستور return با یک فهرست مقداردهی اولیه قلابدار (braced initializer list) جایگزین میکند. به این ترتیب، نوع بازگشتی به صورت بیهوده در تعریف تابع و عبارت return تکرار نمیشود.
Foo bar() {
Baz baz;
return Foo(baz);
}
// transforms to:
Foo bar() {
Baz baz;
return {baz};
}
modernize-shrink-to-fit
ترفندهای کپی و تعویض (copy and swap) را در نگهدارندههای دارای قابلیت کاهش اندازه با فراخوانی متد shrink_to_fit() جایگزین میکند.
متد shrink_to_fit() خواناتر و کارآمدتر از ترفند کپی و تعویض برای کاهش ظرفیت یک نگهدارنده با قابلیت کوچکشدن است. توجه داشته باشید که متد shrink_to_fit() تنها در C++11 به بعد در دسترس است.
modernize-type-traits
صفات نوع (type traits) کتابخانه استاندارد را از قالب traits<...>::type و traits<...>::value به ترتیب به traits_t<...> و traits_v<...> تبدیل میکند.
برای مثال:
std::is_integral<T>::value std::is_same<int, float>::value typename std::add_const<T>::type std::make_signed<unsigned>::type
به این صورت تبدیل خواهد شد:
std::is_integral_v<T> std::is_same_v<int, float> std::add_const_t<T> std::make_signed_t<unsigned>
Options
- IgnoreMacros
- اگر true
باشد، صفات
(traits)
تعریفشده
در ماکروها
را
عیبیابی
نمیکند.
نکته: اصلاحات خودکار (Fixes) هرگز برای کدهای داخل ماکروها صادر نخواهند شد.
#define IS_SIGNED(T) std::is_signed<T>::value
مقدار پیشفرض false است.
Limitations
در حال حاضر استفاده از صفات نوع با مشخصکنندههای نام تودرتو (مانند std::chrono::is_clock و std::chrono::treat_as_floating_point) را بررسی و عیبیابی نمیکند.
modernize-unary-static-assert
این بررسی، هرگونه اعلان static_assert با یک لیترال رشتهای خالی را تشخیص داده و یک یادداشت اصلاحی (fix-it note) برای جایگزینی آن اعلان با یک اعلان تکآرگومانی static_assert ارائه میدهد.
این بررسی تنها برای کدهای C++17 و بالاتر قابل اعمال است.
کد زیر:
void f_textless(int a) {
static_assert(sizeof(a) <= 10, "");
}
جایگزین میشود با:
void f_textless(int a) {
static_assert(sizeof(a) <= 10);
}
modernize-use-auto
این بررسی وظیفه دارد برای اعلان متغیرها از مشخصکننده نوع auto استفاده کند تا خوانایی و نگهداریپذیری کد را بهبود بخشد. برای مثال:
std::vector<int>::iterator I = my_container.begin(); // transforms to: auto I = my_container.begin();
مشخصکننده نوع auto تنها در شرایطی اعمال خواهد شد که نوع متغیر با نوع عبارت مقداردهی اولیه مطابقت داشته باشد. به عبارت دیگر، auto باید همان نوعی را استنتاج کند که در ابتدا در متن مبدأ نوشته شده بود. با این حال، نباید هر شرایطی تغییر داده شود:
int val = 42; InfoStruct &I = SomeObject.getInfo(); // Should not become: auto val = 42; auto &I = SomeObject.getInfo();
در این مثال، استفاده از auto برای انواع توکار خوانایی را افزایش نمیدهد. در شرایط دیگر، باعث میشود کد کمتر خودتوضیح (self-documenting) باشد که این امر خوانایی و نگهداریپذیری را تضعیف میکند. در نتیجه، auto فقط در شرایط خاصی که در زیر توضیح داده شده استفاده و اعمال میشود.
تکرارکنندهها (Iterators)
مشخصکنندههای نوع تکرارکننده معمولاً طولانی بوده و بهویژه در ساختارهای حلقه، بهوفور استفاده میشوند. از آنجا که توابع تولیدکننده تکرارکنندهها ساختاری مشترک دارند، میتوان مشخصکننده نوع را بدون مبهم کردن مفهوم کد جایگزین کرد و همزمان خوانایی و نگهداریپذیری آن را ارتقا داد.
for (std::vector<int>::iterator I = my_container.begin(),
E = my_container.end();
I != E; ++I) {
}
// تبدیل میشود به
for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
}
این بررسی تنها زمانی مشخصکنندههای نوع تکرارکننده را جایگزین میکند که همه شرایط زیر برقرار باشند:
- •
- تکرارکننده مربوط به یکی از کانتینرهای استاندارد در فضای نام std باشد:
- array
- deque
- forward_list
- list
- vector
- map
- multimap
- set
- multiset
- unordered_map
- unordered_multimap
- unordered_set
- unordered_multiset
- queue
- priority_queue
- stack
- •
- تکرارکننده یکی از انواع تکرارکننده مجاز برای کانتینرهای استاندارد باشد:
- iterator
- reverse_iterator
- const_iterator
- const_reverse_iterator
- •
- علاوه بر استفاده مستقیم از انواع تکرارکننده، استفاده از typedefها یا روشهای دیگر ارجاع به این انواع نیز مجاز است. با این حال، انواع وابسته به پیادهسازی که نوعی مانند std::vector<int>::iterator خود یک typedef برای آنهاست، تبدیل نخواهند شد. مثالهای زیر را در نظر بگیرید:
// استفادههای مستقیم زیر از انواع تکرارکننده تبدیل خواهند شد.
std::vector<int>::iterator I = MyVec.begin();
{
using namespace std;
list<int>::iterator I = MyList.begin();
}
// مشخصکننده نوع برای J به auto تبدیل میشود زیرا یک typedef
// به یک نوع تکرارکننده استاندارد است.
typedef std::map<int, std::string>::const_iterator map_iterator;
map_iterator J = MyMap.begin();
// نوع تکرارکننده وابسته به پیادهسازی زیر که ممکن است
// std::vector<int>::iterator یک typedef برای آن باشد، تبدیل نخواهد شد.
__gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
- •
- مقداردهنده اولیه متغیری که تعریف میشود، یک لیست مقداردهی درون آک getUser (braced initializer list) نباشد. در غیر این صورت، استفاده از auto باعث میشود نوع متغیر به عنوان std::initializer_list استنتاج گردد.
عبارتهای New
اغلب هنگامی که یک اشارهگر تعریف شده و با new مقداردهی اولیه میشود، نوع مورد اشاره دو بار نوشته میشود: یک بار در نوع اعلان و بار دیگر در عبارت new. در این حالت، میتوان نوع اعلان را با auto جایگزین کرد تا خوانایی و نگهداریپذیری بهبود یابد.
TypeName *my_pointer = new TypeName(my_param); // تبدیل میشود به auto *my_pointer = new TypeName(my_param);
این بررسی همچنین در صورت برقراری شرایط زیر، نوع اعلان را در اعلانهای چندگانه جایگزین خواهد کرد:
- همه متغیرهای اعلانشده نوع یکسانی داشته باشند (یعنی همگی اشارهگرهایی به یک نوع مشابه باشند).
- همه متغیرهای اعلانشده با یک عبارت new مقداردهی اولیه شوند.
- انواع تمامی عبارتهای new با نوع مورد اشاره در اعلان یکسان باشد.
TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName; // تبدیل میشود به auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
عبارتهای تبدیل نوع (Cast expressions)
اغلب، زمانی که یک متغیر تعریف شده و با یک تبدیل نوع مقداردهی اولیه میشود، نوع متغیر دو بار نوشته میشود: یک بار در نوع اعلان و بار دیگر در عبارت تبدیل نوع. در این حالت، نوع اعلان میتواند با auto جایگزین شود که خوانایی و قابلیت نگهداری را بهبود میبخشد.
TypeName *my_pointer = static_cast<TypeName>(my_param); // becomes auto *my_pointer = static_cast<TypeName>(my_param);
این بررسی تبدیلهای static_cast، dynamic_cast، const_cast، reinterpret_cast، تبدیلهای تابعی، تبدیلهای سبک C و الگوهای تابعی که مانند تبدیل نوع رفتار میکنند (مانند llvm::dyn_cast، boost::lexical_cast و gsl::narrow_cast) را مدیریت میکند. فراخوانیهای الگوهای تابعی در صورتی به عنوان تبدیل نوع در نظر گرفته میشوند که نخستین آرگومان الگو صریح و از نوع یک type باشد، و تابع آن نوع، یا اشارهگر یا ارجاعی به آن را برگرداند.
محدودیتها
- اگر مقداردهی اولیه یک سازنده تبدیل صریح (explicit conversion constructor) باشد، این بررسی مشخصکننده نوع را جایگزین نخواهد کرد، حتی اگر انجام آن ایمن باشد.
- تکرارکنندههای تعریفشده توسط کاربر (User-defined iterators) در حال حاضر پشتیبانی نمیشوند.
گزینهها
- MinTypeNameLength
- اگر این گزینه روی مقداری غیر از صفر تنظیم شود (پیشفرض 5 است)، بررسی از نامهای انواعی که طولی کمتر از مقدار گزینه دارند صرفنظر میکند. این گزینه فقط بر روی عبارتها تأثیر میگذارد، نه تکرارکنندهها. فاصلههای بین نامهای انواع چندکلمهای (long int) به عنوان یکی در نظر گرفته میشوند. اگر گزینه RemoveStars (به زیر مراجعه کنید) روی true تنظیم شده باشد، نمادهای * در نوع نیز به عنوان بخشی از نام نوع شمرده میشوند.
// MinTypeNameLength = 0, RemoveStars=0 int a = static_cast<int>(foo()); // ---> auto a = ... // length(bool *) = 4 bool *b = new bool; // ---> auto *b = ... unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ... // MinTypeNameLength = 5, RemoveStars=0 int a = static_cast<int>(foo()); // ---> int a = ... bool b = static_cast<bool>(foo()); // ---> bool b = ... bool *pb = static_cast<bool*>(foo()); // ---> bool *pb = ... unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ... // length(long <on-or-more-spaces> int) = 8 long int d = static_cast<long int>(foo()); // ---> auto d = ... // MinTypeNameLength = 5, RemoveStars=1 int a = static_cast<int>(foo()); // ---> int a = ... // length(int * * ) = 5 int **pa = static_cast<int**>(foo()); // ---> auto pa = ... bool b = static_cast<bool>(foo()); // ---> bool b = ... bool *pb = static_cast<bool*>(foo()); // ---> auto pb = ... unsigned c = static_cast<unsigned>(foo()); // ---> auto c = ... long int d = static_cast<long int>(foo()); // ---> auto d = ...
- RemoveStars
- اگر این گزینه روی true تنظیم شود (پیشفرض false است)، بررسی هنگام جایگزینی نامهای نوع با auto، ستارهها را از انواع اشارهگر غیر typedef حذف میکند. در غیر این صورت، بررسی ستارهها را باقی میگذارد. برای مثال:
TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName; // RemoveStars = 0 auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName; // RemoveStars = 1 auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;
modernize-use-bool-literals
لیترالهای عددی صحیح که به bool تبدیل میشوند را پیدا میکند.
bool p = 1; bool f = static_cast<bool>(1); std::ios_base::sync_with_stdio(0); bool x = p ? 1 : 0; // transforms to bool p = true; bool f = true; std::ios_base::sync_with_stdio(false); bool x = p ? true : false;
گزینهها
- IgnoreMacros
- اگر روی true تنظیم شود، بررسی در داخل ماکروها هشداری صادر نخواهد کرد. پیشفرض true است.
modernize-use-constraints
جایگزینی std::enable_if با بندهای requires در C++20.
دستور std::enable_if یک سازوکار SFINAE برای انتخاب الگوهای تابع یا کلاس مطلوب بر اساس صفات نوع (type traits) یا سایر نیازمندیها است. enable_if تعداد آرگومانهای فراالگو (meta-arity) را تغییر داده و اثرات جانبی نامطلوب دیگری https://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0225r0.html در کد دارد. استاندارد C++20 مفاهیم (concepts) و قیود (constraints) را به عنوان راهحلی تمیزتر و ارائهشده توسط زبان برای دستیابی به همان نتیجه معرفی میکند.
این بررسی برخی از الگوهای رایج std::enable_if را که میتوان با بندهای requires در C++20 جایگزین کرد پیدا میکند. ابزار میتواند برخی از این الگوها را بهصورت خودکار جایگزین کند، در غیر این صورت، پیامی تشخیصی بدون جایگزینی صادر میکند. این ابزار الگوهای std::enable_if زیر را شناسایی میکند
- 1.
- عبارت std::enable_if در نوع بازگشتی یک تابع
- 2.
- عبارت std::enable_if به عنوان پارامتر الگوی پایانی برای الگوهای تابع
سایر کاربردها، برای مثال در الگوهای کلاس برای پارامترهای تابع، در حال حاضر توسط این ابزار پشتیبانی نمیشوند. گونههای دیگر مانند boost::enable_if در حال حاضر توسط این ابزار پشتیبانی نمیشوند.
در زیر نمونههایی از کدهای استفادهکننده از std::enable_if آمده است.
// enable_if in function return type
template <typename T>
std::enable_if_t<T::some_trait, int> only_if_t_has_the_trait() { ... }
// enable_if in the trailing template parameter
template <typename T, std::enable_if_t<T::some_trait, int> = 0>
void another_version() { ... }
template <typename T>
typename std::enable_if<T::some_value, Obj>::type existing_constraint() requires (T::another_value) {
return Obj{};
}
template <typename T, std::enable_if_t<T::some_trait, int> = 0>
struct my_class {};
ابزار، کد بالا را با موارد زیر جایگزین خواهد کرد:
// warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
template <typename T>
int only_if_t_has_the_trait() requires T::some_trait { ... }
// warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
template <typename T>
void another_version() requires T::some_trait { ... }
// The tool will emit a diagnostic for the following, but will
// not attempt to replace the code.
// warning: use C++20 requires constraints instead of enable_if [modernize-use-constraints]
template <typename T>
typename std::enable_if<T::some_value, Obj>::type existing_constraint() requires (T::another_value) {
return Obj{};
}
// The tool will not emit a diagnostic or attempt to replace the code.
template <typename T, std::enable_if_t<T::some_trait, int> = 0>
struct my_class {};
نکته:
modernize-use-default
نام این بررسی به modernize-use-equals-default <> تغییر یافته است.
modernize-use-default-member-init
این بررسی، مقداردهندههای اولیه اعضا در سازندهها را به مقداردهندههای پیشفرض جدید اعضا در C++11 تبدیل میکند. سایر مقداردهندههای اولیه اعضا که با مقداردهنده اولیه پیشفرض عضو مطابقت دارند، حذف میشوند. این کار میتواند کد تکراری را کاهش داده یا امکان استفاده از '= default' را فراهم کند.
struct A {
A() : i(5), j(10.0) {}
A(int i) : i(i), j(10.0) {}
int i;
double j;
};
// becomes
struct A {
A() {}
A(int i) : i(i) {}
int i{5};
double j{10.0};
};
نکته:
گزینهها
- UseAssignment
- اگر این گزینه روی true تنظیم شود (پیشفرض false است)، بررسی اعضا را با انتساب مقداردهی اولیه میکند. برای مثال:
struct A {
A() {}
A(int i) : i(i) {}
int i = 5;
double j = 10.0;
};
- IgnoreMacros
- اگر این گزینه روی true تنظیم شود (پیشفرض true است)، بررسی هشداری درباره اعضای تعریفشده درون ماکروها صادر نخواهد کرد.
modernize-use-designated-initializers
لیستهای مقداردهی اولیه برای انواع تجمیعی (aggregate types) را که میتوان به جای آن به صورت مقداردهندههای اولیه مشخصشده (designated initializers) نوشت، پیدا میکند.
با لیستهای مقداردهی اولیه ساده، هنگام اضافه کردن فیلدهای جدید در میان یک struct یا کلاس، بروز باگها بسیار آسان است. همین سردرگمی ممکن است هنگام تغییر ترتیب فیلدها نیز پیش بیاید.
استاندارد C++20 از نحو مقداردهنده اولیه مشخصشده برای انواع تجمیعی پشتیبانی میکند. با بهکارگیری آن، همیشه میتوانیم مطمئن شویم که تجمیعها به درستی ساخته شدهاند، زیرا به هر متغیری که مقداردهی میشود با نام آن ارجاع داده میشود.
مثال:
struct S { int i, j; };
یک نوع تجمیعی است که باید به شکل زیر مقداردهی اولیه شود
S s{.i = 1, .j = 2};
به جای
S s{1, 2};
که میتواند در صورت جابهجا شدن i و j در اعلان S به راحتی مشکلساز شود.
حتی هنگام کامپایل در نسخهای از زبان قدیمیتر از C++20، بسته به کامپایلر شما، مقداردهندههای مشخصشده احتمالاً پشتیبانی میشوند. بنابراین، این بررسی بهطور پیشفرض به C99/C++20 و بالاتر محدود شده است. برای دریافت پشتیبانی از نشاندهندههای ترکیبی در لیست مقداردهی اولیه در C گزینه -Wc99-designator و برای پشتیبانی از مقداردهندههای مشخصشده در حالتهای زبانی قدیمیتر C++ گزینه -Wc++20-designator را بررسی کنید.
گزینهها
- IgnoreMacros
- مقدار false مشخص میکند که اجزای لیستهای مقداردهی اولیه که از ماکروها بسط یافتهاند بررسی نشوند. مقدار پیشفرض true است.
- IgnoreSingleElementAggregates
- مقدار false مشخص میکند که حتی مقداردهندههای اولیه برای انواع تجمیعی تنها با یک عنصر نیز باید بررسی شوند. مقدار پیشفرض true است. مقداردهیهای اولیه std::array همیشه مستثنی هستند، زیرا این نوع یک انتزاع کتابخانه استاندارد است و قرار نیست با مقداردهندههای اولیه مشخصشده مقداردهی شود.
- RestrictToPODTypes
- مقدار true مشخص میکند که فقط انواع Plain Old Data (POD) باید بررسی شوند. این امر باعث میشود که بررسی حتی در استانداردهای قدیمیتر C++ نیز قابل اعمال باشد. مقدار پیشفرض false است.
- StrictCStandardCompliance
- هنگامی که روی false تنظیم شود، بررسی خود را به C99 و بالاتر محدود نمیکند. مقدار پیشفرض true است.
- StrictCppStandardCompliance
- هنگامی که روی false تنظیم شود، بررسی خود را به C++20 و بالاتر محدود نمیکند. مقدار پیشفرض true است.
modernize-use-emplace
این بررسی، درج مواردی در یک نگهدارنده به سبک STL را که از طریق فراخوانی متدهای push_back، push یا push_front با یک متغیر موقت صریحاً ساختهشده از نوع عنصر نگهدارنده انجام شده است، نشانهگذاری میکند. در این حالت، متدهای معادل متناظر emplace منجر به کدی کوتاهتر و بهطور بالقوه کارآمدتر میشوند. در حال حاضر این بررسی از insert پشتیبانی نمیکند. همچنین از توابع insert برای نگهدارندههای انجمنی نیز پشتیبانی نمیکند، زیرا جایگزینی insert با emplace ممکن است باعث افت سرعت شود https://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html، اما ممکن است در آینده با افزودن گزینهای این پشتیبانی اضافه شود.
گزینههای ContainersWithPushBack، ContainersWithPush و ContainersWithPushFront به ترتیب برای مشخص کردن انواع نگهدارندههایی که از عملیات push_back، push و push_front پشتیبانی میکنند به کار میروند. مقادیر پیشفرض این گزینهها به شرح زیر است:
- ContainersWithPushBack: std::vector، std::deque و std::list.
- ContainersWithPush: std::stack، std::queue و std::priority_queue.
- ContainersWithPushFront: std::forward_list، std::list و std::deque.
این بررسی همچنین مواردی را که در آنها یک متد شبیه به emplace بهطور نادرست استفاده شده است گزارش میدهد، مانند استفاده از emplace_back در حالی که یک سازنده نیز همزمان فراخوانی میشود. این کار باعث ایجاد یک شیء موقت میشود که در بهترین حالت نیازمند انتقال (move) و در بدترین حالت نیازمند کپی (copy) است. تقریباً همه توابع شبیه به emplace در STL تحت پوشش این مورد قرار دارند، به استثنای try_emplace در std::map و std::unordered_map، زیرا رفتار آن کمی با بقیه متفاوت است. نگهدارندههای بیشتری را میتوان با گزینه EmplacyFunctions اضافه کرد، مشروط بر اینکه نگهدارنده یک نوع value_type تعریف کند و توابع شبیه به emplace یک شیء از نوع value_type بسازند.
قبل:
std::vector<MyClass> v; v.push_back(MyClass(21, 37)); v.emplace_back(MyClass(21, 37)); std::vector<std::pair<int, int>> w; w.push_back(std::pair<int, int>(21, 37)); w.push_back(std::make_pair(21L, 37L)); w.emplace_back(std::make_pair(21L, 37L));
بعد:
std::vector<MyClass> v; v.emplace_back(21, 37); v.emplace_back(21, 37); std::vector<std::pair<int, int>> w; w.emplace_back(21, 37); w.emplace_back(21L, 37L); w.emplace_back(21L, 37L);
بهطور پیشفرض، این بررسی قادر است فراخوانیهای غیرضروری std::make_pair و std::make_tuple را از فراخوانیهای push_back روی نگهدارندههای std::pair و std::tuple حذف کند. انواع سفارشی شبهتاپل را میتوان با گزینه TupleTypes تغییر داد؛ توابع ساخت سفارشی را نیز میتوان با گزینه TupleMakeFunctions تعیین کرد.
حالت دیگر زمانی است که آرگومانهایی را ارسال میکنیم که به نوعی در داخل نگهدارنده تبدیل خواهند شد.
قبل:
std::vector<boost::optional<std::string> > v;
v.push_back("abc");
بعد:
std::vector<boost::optional<std::string> > v;
v.emplace_back("abc");
در برخی موارد تبدیل معتبر خواهد بود، اما کد از نظر کنترل استثناها امن (exception safe) نخواهد بود. در چنین حالتی فراخوانیهای push_back جایگزین نخواهند شد.
std::vector<std::unique_ptr<int>> v; v.push_back(std::unique_ptr<int>(new int(0))); auto *ptr = new int(1); v.push_back(std::unique_ptr<int>(ptr));
علت این است که جایگزینی آن با emplace_back در صورتی که پیش از جاگذاری (emplacement) استثنایی رخ دهد (برای مثال، حافظه کافی برای افزودن عنصر جدید وجود نداشته باشد)، ممکن است باعث نشت حافظه (leak) این اشارهگر شود.
برای اطلاعات بیشتر، بخش ۴۲ از کتاب «Effective Modern C++» نوشته اسکات مایرز با عنوان «Consider emplacement instead of insertion» را مطالعه کنید.
اشارهگرهای هوشمند پیشفرضی که در نظر گرفته میشوند عبارتند از: std::unique_ptr، std::shared_ptr و std::auto_ptr. برای تعیین سایر اشارهگرهای هوشمند یا کلاسهای دیگر از گزینه SmartPointers استفاده کنید.
این بررسی همچنین در صورتی که هر یک از آرگومانهای فراخوانی سازنده شامل موارد زیر باشد عمل نخواهد کرد:
- یک میدان بیتی (bit-field) باشد (میدانهای بیتی نمیتوانند به rvalue/universal reference متصل شوند)
- یک عبارت new باشد (به منظور جلوگیری از نشت حافظه)
- اگر آرگومان از طریق تبدیل مشتق به پایه (derived-to-base cast) تبدیل شود.
این بررسی برای اجرا به C++11 یا بالاتر نیاز دارد.
گزینهها
- ContainersWithPushBack
- فهرستی جداشده با نقطه ویرگول (semicolon) از نام کلاسهای نگهدارندههای سفارشی که از push_back پشتیبانی میکنند.
- ContainersWithPush
- فهرستی جداشده با نقطه ویرگول از نام کلاسهای نگهدارندههای سفارشی که از push پشتیبانی میکنند.
- ContainersWithPushFront
- فهرستی جداشده با نقطه ویرگول از نام کلاسهای نگهدارندههای سفارشی که از push_front پشتیبانی میکنند.
- IgnoreImplicitConstructors
- هنگامی که برابر با true باشد، بررسی آرگومانهای بهطور ضمنی ساختهشده در push_back را نادیده میگیرد؛ به عنوان مثال:
std::vector<std::string> v;
v.push_back("a"); // Ignored when IgnoreImplicitConstructors is `true`.
پیشفرض برابر با false است.
- SmartPointers
- فهرستی جداشده با نقطه ویرگول از نام کلاسهای اشارهگرهای هوشمند سفارشی.
- TupleTypes
- فهرستی جداشده با نقطه ویرگول از نام کلاسهای شبه std::tuple.
- TupleMakeFunctions
- فهرستی جداشده با نقطه ویرگول از نام توابع شبه std::make_tuple. این فراخوانیهای توابع از فراخوانیهای push_back حذف شده و به emplace_back تبدیل میشوند.
- EmplacyFunctions
- فهرستی جداشده با نقطه ویرگول از نگهدارندهها بدون پارامترهای قالبشان به همراه یک متد شبه emplace از نگهدارنده. به عنوان مثال: vector::emplace_back. این متدها از نظر استفاده نادرست بررسی خواهند شد و در صورت ایجاد غیرضروری یک شیء موقت، بررسی آن را گزارش میکند. تمام نگهدارندههای STL با چنین توابع عضوی بهطور پیشفرض پشتیبانی میشوند.
مثال
std::vector<MyTuple<int, bool, char>> x; x.push_back(MakeMyTuple(1, false, 'x')); x.emplace_back(MakeMyTuple(1, false, 'x'));
تبدیل میشود به:
std::vector<MyTuple<int, bool, char>> x; x.emplace_back(1, false, 'x'); x.emplace_back(1, false, 'x');
هنگامی که TupleTypes روی MyTuple، TupleMakeFunctions روی MakeMyTuple و EmplacyFunctions روی vector::emplace_back تنظیم شده باشد.
modernize-use-equals-default
این بررسی بدنههای پیشفرض توابع عضوی خاص را با = default; جایگزین میکند. اعلانات صریح پیشفرضشده توابع (explicitly defaulted) فرصتهای بهینهسازی بیشتری را فراهم میکنند، زیرا ممکن است کامپایلر این توابع را به عنوان توابع ناچیز (trivial) در نظر بگیرد.
struct A {
A() {}
~A();
};
A::~A() {}
// becomes
struct A {
A() = default;
~A();
};
A::~A() = default;
نکته:
گزینهها
- IgnoreMacros
- اگر روی true تنظیم شود، بررسی در داخل ماکروها هشداری صادر نمیکند و اعضای خاصی را که بدنه آنها حاوی ماکروها یا دستورات پیشپردازنده است نادیده میگیرد. پیشفرض true است.
modernize-use-equals-delete
توابع عضوی خاص و خصوصیِ پیادهسازینشده را شناسایی کرده و استفاده از = delete را برای آنها توصیه میکند. علاوه بر این، انتقال هرگونه تابع عضو حذفشده از بخش private به بخش public را پیشنهاد میدهد.
پیش از معرفی C++11، روش اصلی برای عملاً «پاک کردن» یک تابع خاص شامل اعلان آن به صورت private بدون ارائه تعریف بود. این رویکرد به خطای کامپایلر (هنگام تلاش برای فراخوانی یک تابع خصوصی) یا خطای پیونددهنده (به دلیل ارجاع تعریفنشده) منجر میشد.
با این حال، پس از پیدایش C++11، رویکرد مرسومتری برای دستیابی به این هدف پدید آمد. این روش شامل نشانهگذاری توابع به صورت = delete و نگهداشتن آنها در بخش public کلاس است.
برای جلوگیری از هشدارهای نادرست (false positives)، این بررسی تنها در یک واحد ترجمه (translation unit) فعال است که تمام توابع عضوی دیگر در آن پیادهسازی شده باشند. این بررسی با افزودن = delete اصلاحات جزئی تولید میکند، اما انتقال دستی توابع به بخش public بر عهده کاربر است.
// Example: bad
class A {
private:
A(const A&);
A& operator=(const A&);
};
// Example: good
class A {
public:
A(const A&) = delete;
A& operator=(const A&) = delete;
};
- IgnoreMacros
- اگر این گزینه روی true تنظیم شود (پیشفرض true است)، بررسی درباره توابعی که داخل ماکروها اعلان شدهاند هشداری نمیدهد.
modernize-use-integer-sign-comparison
مقایسههای بین اعداد صحیح علامتدار و بدون علامت را در صورت وجود، با معادل امن C++20 آنها یعنی std::cmp_* جایگزین میکند.
این بررسی تنها برای C++20 یا بالاتر جایگزینی ارائه میدهد، در غیر این صورت مشکل را برجسته کرده و انتظار دارد کاربر آن را به صورت دستی برطرف کند.
نمونههایی از اصلاحات ایجادشده توسط بررسی:
unsigned int func(int a, unsigned int b) {
return a == b;
}
تبدیل میشود به
#include <utility>
unsigned int func(int a, unsigned int b) {
return std::cmp_equal(a, b);
}
گزینهها
- IncludeStyle
- رشتهای که شیوه درج include را مشخص میکند، llvm یا google. پیشفرض llvm است.
- EnableQtSupport
- جایگزین q20::cmp_* در C++17 را برای برنامههای مبتنی بر Qt در دسترس قرار میدهد. پیشفرض false است.
modernize-use-nodiscard
صفتهای [[nodiscard]] (معرفیشده در C++17) را به توابع عضو اضافه میکند تا در زمان کامپایل مشخص شود که مقادیر بازگشتی کدام توابع نباید نادیده گرفته شوند.
توابع عضو برای بررسی شدن توسط این بررسی، باید شرایط زیر را داشته باشند:
- فاقد صفتهای [[nodiscard]]، [[noreturn]]، __attribute__((warn_unused_result))، [[clang::warn_unused_result]] یا [[gcc::warn_unused_result]] باشند،
- نوع بازگشتی آنها غیر از void باشد،
- انواع بازگشتی غیرقالب (non-template) داشته باشند،
- تابع عضو از نوع const باشد،
- توابع با تعداد آرگومان متغیر (non-variadic) نباشند،
- فاقد پارامترهای ارجاعی غیرثابت (non-const reference) باشند،
- فاقد پارامترهای اشارهگر باشند،
- فاقد پارامترهای قالبی (template parameters) باشند،
- فاقد پارامترهای تابعی قالبی باشند،
- عضوی از کلاسی با متغیرهای عضو از نوع mutable نباشند،
- فاقد لامبداها (Lambdas) باشند،
- فاقد توابع تبدیل (conversion functions) باشند.
چنین توابعی هیچ راهی برای تغییر وضعیت یا انتقال مقادیر به غیر از نوع بازگشتی ندارند. مگر اینکه توابع عضو از طریق فراخوانیهای خارجی (مانند ورودی/خروجی I/O) وضعیتی را تغییر دهند.
مثال
bool empty() const; bool empty(int i) const;
تبدیل میشود به:
[[nodiscard]] bool empty() const; [[nodiscard]] bool empty(int i) const;
گزینهها
- ReplacementString
- یک ماکرو را برای استفاده به جای [[nodiscard]] مشخص میکند. این گزینه زمانی مفید است که از کدهای منبعی نگهداری میکنید که باید با کامپایلرهای قدیمیتر از C++17 کامپایل شوند.
مثال
bool empty() const; bool empty(int i) const;
تبدیل میشود به:
NO_DISCARD bool empty() const; NO_DISCARD bool empty(int i) const;
در صورتی که گزینهٔ ReplacementString روی NO_DISCARD تنظیم شده باشد.
نکته:
نکته:
modernize-use-noexcept
این بررسی مشخصههای استثنای پویای (dynamic exception specifications) منسوخشده را با مشخصه مناسب noexcept (معرفیشده در C++11) جایگزین میکند. بهطور پیشفرض، این بررسی throw() را با noexcept، و throw(<exception>[,...]) یا throw(...) را با noexcept(false) جایگزین میکند.
مثال
void foo() throw();
void bar() throw(int) {}
تبدیل میشود به:
void foo() noexcept;
void bar() noexcept(false) {}
گزینهها
- ReplacementString
- کاربران میتوانند از ReplacementString <#cmdoption-arg-ReplacementString> برای مشخص کردن یک ماکرو جهت استفاده به جای noexcept استفاده کنند. این ویژگی زمانی مفید است که از کدهای منبعی نگهداری میشود که از نشانهگذاری مشخصه استثنای سفارشی دیگری غیر از noexcept استفاده میکنند. راهنماهای Fix-it تنها برای مشخصههای بدون پرتاب استثنا (non-throwing) تولید خواهند شد.
مثال
void bar() throw(int); void foo() throw();
تبدیل میشود به:
void bar() throw(int); // No fix-it generated. void foo() NOEXCEPT;
اگر گزینهٔ ReplacementString <#cmdoption-arg-ReplacementString> روی NOEXCEPT تنظیم شده باشد.
بهطور پیشفرض فعال است، غیرفعال کردن آن باعث ایجاد راهنماییهای fix-it میشود که مشخصههای پویای پرتاب استثنا، مانند throw(<something>) را کاملاً و بدون ارائهٔ متن جایگزین حذف میکند، بهجز برای مخربها (destructors) و عملگرهای delete که بهطور پیشفرض noexcept(true) هستند.
مثال
void foo() throw(int) {}
struct bar {
void foobar() throw(int);
void operator delete(void *ptr) throw(int);
void operator delete[](void *ptr) throw(int);
~bar() throw(int);
}
تبدیل میشود به:
void foo() {}
struct bar {
void foobar();
void operator delete(void *ptr) noexcept(false);
void operator delete[](void *ptr) noexcept(false);
~bar() noexcept(false);
}
اگر گزینهٔ UseNoexceptFalse روی false تنظیم شده باشد.
modernize-use-nullptr
این بررسی، استفاده از ثابتهای اشارهگر تهی (مانند NULL، 0) را برای استفاده از کلیدواژهٔ جدید C++11 و C23 یعنی nullptr تبدیل میکند.
مثال
void assignment() {
char *a = NULL;
char *b = 0;
char c = 0;
}
int *ret_ptr() {
return 0;
}
تبدیل میشود به:
void assignment() {
char *a = nullptr;
char *b = nullptr;
char c = 0;
}
int *ret_ptr() {
return nullptr;
}
گزینهها
- IgnoredTypes
- فهرستی جداشده با نقطه ویرگول (semicolon) از عبارات منظم (regex) برای مطابقت با انواع اشارهگرهایی که تبدیلهای ضمنی (implicit casts) برای آنها نادیده گرفته خواهد شد. مقدار پیشفرض: std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec.
- NullMacros
- فهرستی جداشده با کاما از نامهای ماکرو که همراه با NULL تبدیل خواهند شد. بهطور پیشفرض این بررسی تنها ماکروی NULL را جایگزین میکند و از هرگونه ماکروی مشابه تعریفشده توسط کاربر صرفنظر خواهد کرد.
مثال
#define MY_NULL (void*)0
void assignment() {
void *p = MY_NULL;
}
تبدیل میشود به:
#define MY_NULL NULL
void assignment() {
int *p = nullptr;
}
اگر گزینهٔ NullMacros روی MY_NULL تنظیم شده باشد.
modernize-use-override
کلیدواژهٔ override (معرفیشده در C++11) را به توابع مجازی بازنویسیشده اضافه میکند و virtual را از این توابع حذف مینماید چرا که دیگر نیازی به آن نیست.
استفاده از virtual در پیادهسازیهای کلاسهای غیر پایه به این منظور استفاده میشد که به کاربر نشان دهد یک تابع مجازی است. کامپایلرهای C++ از وجود آن برای مشخص کردن یک تابع بازنویسیشده استفاده نمیکردند.
در C++11 کلیدواژههای override و final معرفی شدند تا توابع بازنویسیشده بهدرستی علامتگذاری شوند. وجود آنها به کامپایلرها اجازه میدهد بررسی کنند که آیا یک تابع بازنویسیشده بهدرستی پیادهسازی کلاس پایه را بازنویسی (override) میکند یا خیر.
این قابلیت میتواند مفید باشد چرا که کامپایلرها میتوانند در زمان کامپایل خطایی ایجاد کنند هرگاه:
- امضای تابع پیادهسازیشده در کلاس پایه تغییر کند.
- کاربر بازنویسی را با امضای صحیح ایجاد نکرده باشد.
گزینهها
- IgnoreDestructors
- اگر روی true تنظیم شود، این بررسی مخربها (destructors) را عیبیابی نخواهد کرد. پیشفرض false است.
- IgnoreTemplateInstantiations
- اگر روی true تنظیم شود، به این بررسی دستور میدهد که بازنویسیهای توابع مجازی (virtual function overrides) که بخشی از مصداقسازیهای الگو (template instantiations) هستند را نادیده بگیرد. پیشفرض false است.
- AllowOverrideAndFinal
- اگر روی true تنظیم شود، این بررسی override را به عنوان افزونگی همراه با final تشخیص نخواهد داد. این مورد زمانی مفید است که کد توسط کامپایلری با پرچمهای بررسی هشدار/خطا کامپایل میشود که صریحاً نیاز به override روی اعضای بازنویسیشده دارند، مانند gcc -Wsuggest-override/gcc -Werror=suggest-override. پیشفرض false است.
- OverrideSpelling
- یک ماکرو را برای استفاده به جای override مشخص میکند. این گزینه هنگام نگهداری کدهای منبعی که نیاز به کامپایل با کامپایلرهای قبل از C++11 نیز دارند مفید است.
- FinalSpelling
- یک ماکرو را برای استفاده به جای final مشخص میکند. این گزینه هنگام نگهداری کدهای منبعی که نیاز به کامپایل با کامپایلرهای قبل از C++11 نیز دارند مفید است.
نکته:
modernize-use-ranges
فراخوانیهای الگوریتمهای تکرارکننده (iterator) کتابخانهٔ استاندارد را که میتوانند با نسخهٔ ranges جایگزین شوند، شناسایی میکند.
مثال
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(),
std::begin(Items2), std::end(Items2));
تبدیل میشود به:
auto Iter1 = std::ranges::find(Items, 0); auto AreSame = std::ranges::equal(Items1, Items2);
الگوریتمهای پشتیبانیشده
فراخوانیهای مربوط به الگوریتمهای زیر از کتابخانهٔ std بررسی میشوند:
std::adjacent_find، std::all_of، std::any_of، std::binary_search، std::copy_backward، std::copy_if، std::copy، std::destroy، std::equal_range، std::equal، std::fill، std::find_end، std::find_if_not، std::find_if، std::find، std::for_each، std::generate، std::includes، std::inplace_merge، std::iota، std::is_heap_until، std::is_heap، std::is_partitioned، std::is_permutation، std::is_sorted_until، std::is_sorted، std::lexicographical_compare، std::lower_bound، std::make_heap، std::max_element، std::merge، std::min_element، std::minmax_element، std::mismatch، std::move_backward، std::move، std::next_permutation، std::none_of، std::partial_sort_copy، std::partition_copy، std::partition_point، std::partition، std::pop_heap، std::prev_permutation، std::push_heap، std::remove_copy_if، std::remove_copy، std::remove، std::remove_if، std::replace_if، std::replace، std::reverse_copy، std::reverse، std::rotate، std::rotate_copy، std::sample، std::search، std::set_difference، std::set_intersection، std::set_symmetric_difference، std::set_union، std::shift_left، std::shift_right، std::sort_heap، std::sort، std::stable_partition، std::stable_sort، std::transform، std::uninitialized_copy، std::uninitialized_default_construct، std::uninitialized_fill، std::uninitialized_move، std::uninitialized_value_construct، std::unique_copy، std::unique، std::upper_bound.
نکته: برخی از الگوریتمهای range برای vector<bool> به C++23 نیاز دارند زیرا از تکرارکنندههای پروکسی (proxy iterators) استفاده میکند.
پیمایش معکوس (Reverse Iteration)
اگر فراخوانیها با استفاده از تکرارکنندههای معکوس بر روی کانتینرها انجام شوند، کد با استفاده از آداپتور std::views::reverse اصلاح خواهد شد.
auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
std::crbegin(Items2), std::crend(Items2));
تبدیل میشود به:
auto AreSame = std::ranges::equal(std::views::reverse(Items1),
std::views::reverse(Items2));
گزینهها
- IncludeStyle
- رشتهای که مشخص میکند کدام سبک include استفاده شود، llvm یا google. مقدار پیشفرض llvm است.
- UseReversePipe
- هنگامی که true باشد (پیشفرض false است)، اصلاحاتی که شامل دامنههای معکوس هستند به جای نحو تابعی، از نحو آداپتور پایپ (pipe) استفاده خواهند کرد.
std::find(Items.rbegin(), Items.rend(), 0);
تبدیل میشود به:
std::ranges::find(Items | std::views::reverse, 0);
modernize-use-scoped-lock
موارد استفاده از std::lock_guard را پیدا کرده و جایگزینی آنها با جایگزین C++17 یعنی std::scoped_lock را پیشنهاد میدهد.
اصلاحات خودکار (Fix-its) برای اعلانهای تکی std::lock_guard ارائه میشوند و برای اعلانهای چندگانه std::lock_guard که میتوانند با یک اعلان تکی از std::scoped_lock جایگزین شوند، هشدار صادر میشود.
مثالها
اعلان تکی std::lock_guard:
std::mutex M; std::lock_guard<std::mutex> L(M);
تبدیل میشود به:
std::mutex M; std::scoped_lock L(M);
اعلان تکی std::lock_guard همراه با std::adopt_lock:
std::mutex M; std::lock(M); std::lock_guard<std::mutex> L(M, std::adopt_lock);
تبدیل میشود به:
std::mutex M; std::lock(M); std::scoped_lock L(std::adopt_lock, M);
اعلانهای چندگانه std::lock_guard تنها هشدار صادر میکنند:
std::mutex M1, M2; std::lock(M1, M2); std::lock_guard Lock1(M1, std::adopt_lock); // warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard' std::lock_guard Lock2(M2, std::adopt_lock); // note: additional 'std::lock_guard' declared here
محدودیتها
این بررسی در صورتی که std::lock_guard بهطور ضمنی از طریق پارامتر template استفاده شده باشد، هشداری صادر نخواهد کرد:
template <template <typename> typename Lock>
void TemplatedLock() {
std::mutex M;
Lock<std::mutex> L(M); // no warning
}
void instantiate() {
TemplatedLock<std::lock_guard>();
}
گزینهها
- WarnOnSingleLocks
- هنگامی که true باشد، بررسی روی اعلانهای تکی std::lock_guard هشدار خواهد داد. اگر میخواهید فقط برای اعلانهای چندگانه std::lock_guard که میتوانند با یک std::scoped_lock تکی جایگزین شوند هشدار دریافت کنید، این گزینه را روی false تنظیم کنید. مقدار پیشفرض true است.
- WarnOnUsingAndTypedef
- هنگامی که true باشد، بررسی در صورت استفاده از std::lock_guard در زمینه using یا typedef هشدار صادر میکند. مقدار پیشفرض true است.
template <typename T> using Lock = std::lock_guard<T>; // warning: use 'std::scoped_lock' instead of 'std::lock_guard' using LockMutex = std::lock_guard<std::mutex>; // warning: use 'std::scoped_lock' instead of 'std::lock_guard' typedef std::lock_guard<std::mutex> LockDef; // warning: use 'std::scoped_lock' instead of 'std::lock_guard' using std::lock_guard; // warning: use 'std::scoped_lock' instead of 'std::lock_guard'
modernize-use-starts-ends-with
روشهای غیرمستقیم و متداول بیان starts_with و ends_with را بررسی میکند و پیشنهاد میدهد در صورت در دسترس بودن، آنها را با روش سادهتر جایگزین کنید. بهویژه، این قابلیت با std::string و std::string_view کار خواهد کرد.
سناریوهای تحت پوشش:
| عبارت | جایگزین |
| u.find(v) == 0 | u.starts_with(v) |
| u.find(v, 0) == 0 | u.starts_with(v) |
| u.find(v, 0, v.size()) == 0 | u.starts_with(v) |
| u.rfind(v, 0) != 0 | !u.starts_with(v) |
| u.rfind(v, 0, v.size()) != 0 | !u.starts_with(v) |
| u.compare(0, v.size(), v) == 0 | u.starts_with(v) |
| u.substr(0, v.size()) == v | u.starts_with(v) |
| v != u.substr(0, v.size()) | !u.starts_with(v) |
| u.compare(u.size() - v.size(), v.size(), v) == 0 | u.ends_with(v) |
| u.rfind(v) == u.size() - v.size() | u.ends_with(v) |
modernize-use-std-format
فراخوانیهای absl::StrFormat، یا توابع دیگر از طریق گزینههای پیکربندی را به std::format در C++20 یا تابع دیگری از طریق یک گزینه پیکربندی تبدیل میکند، رشته قالببندی را بهدرستی تغییر داده و فراخوانیهای غیرضروری به std::string::c_str() و std::string::data() را حذف مینماید.
برای نمونه، خطوطی مانند
return absl::StrFormat("The %s is %3d", description.c_str(), value);
را به این شکل تبدیل میکند:
return std::format("The {} is {:3}", description, value);
این بررسی از همان الگوریتم تبدیل رشته قالببندی موجود در modernize-use-std-print <> استفاده میکند و کاستیها و رفتار آن در ترکیب با ماکروها در مستندات همان بررسی شرح داده شده است.
گزینهها
extern std::string strprintf(const char *format, ...);
int i = -42;
unsigned int u = 0xffffffff;
return strprintf("%u %d\n", i, u);
به شکل زیر تبدیل خواهد شد
return std::format("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));
تا اطمینان حاصل شود خروجی همچنان نمایش بدون علامت -42 و نمایش علامتدار 0xffffffff باقی میماند (اغلب به ترتیب 4294967254 و -1). اگر false باشد (که مقدار پیشفرض است)، این تبدیل نوعها اضافه نخواهند شد که ممکن است سبب تغییر در خروجی شود. توجه داشته باشید که این گزینه برای مقدار پیشفرض StrFormatLikeFunctions تفاوتی ایجاد نمیکند، زیرا absl::StrFormat یک بسته پارامتر تابع (function parameter pack) دریافت میکند و تابع variadic نیست.
- StrFormatLikeFunctions
- فهرستی از عبارات باقاعده (regular expressions) جداشده با نقطهویرگول (semicolon) که با نامهای (کاملاً واجد شرایط) توابع برای جایگزینی مطابقت دارند؛ با این شرط که اولین پارامتر شامل رشته قالببندی سبک printf باشد و آرگومانهایی که باید قالببندی شوند بلافاصله پس از آن قرار گیرند. نامهای واجد شرایط توابع عضو نیز پشتیبانی میشوند، اما نام تابع جایگزین باید فاقد صلاحیت (unqualified) باشد. مقدار پیشفرض absl::StrFormat است.
- ReplacementFormatFunction
- تابعی که بهجای پیشفرض std::format، برای جایگزینی تابع تنظیمشده توسط گزینه StrFormatLikeFunctions استفاده خواهد شد. انتظار میرود که این تابع رابطی سازگار با std::format ارائه دهد. یک گزینه مناسب fmt::format خواهد بود.
- FormatHeader
- هدر فایلی که باید برای اعلان ReplacementFormatFunction درج شود تا در صورت نیاز یک دایرکتیو #include افزوده گردد. اگر ReplacementFormatFunction برابر با std::format باشد، این گزینه بهطور پیشفرض <format> خواهد بود، در غیر این صورت پیشفرض این گزینه خالی بوده و هیچ دایرکتیو #include اضافه نخواهد شد.
modernize-use-std-numbers
ثابتها و فراخوانیهای توابع ریاضی را که میتوان با ثابتهای ریاضی C++20 از هدر numbers جایگزین کرد مییابد و پیشنهادهای اصلاحی (fix-it hints) ارائه میدهد. این بررسی استفاده از متغیرهایی با آن مقدار را مطابقت نمیدهد، بلکه در عوض، جایگزینی برای تعریف آن متغیرها ارائه میکند. فراخوانی توابعی که با الگوی نحوه محاسبه ثابت مطابقت دارند، تطبیق داده شده و با ثابت std::numbers جایگزین میشوند. استفاده از ماکروها بهجای تغییر تعریف ماکرو، با ثابت متناظر std::numbers جایگزین میشود.
فهرست زیر از ثابتهای هدر numbers پشتیبانی میشوند:
- e
- log2e
- log10e
- pi
- inv_pi
- inv_sqrtpi
- ln2
- ln10
- sqrt2
- sqrt3
- inv_sqrt3
- egamma
- phi
این فهرست در حال حاضر شامل تمام ثابتها تا C++20 است.
جایگزینیها از نوع ثابت منطبقشده استفاده میکنند و میتوانند تبدیلهای صریح (explicit casts) را حذف کنند؛ یعنی جابجایی بین std::numbers::e، std::numbers::e_v<float> و std::numbers::e_v<long double> در صورت لزوم انجام میشود.
double sqrt(double);
double log2(double);
void sink(auto&&) {}
void floatSink(float);
#define MY_PI 3.1415926
void foo() {
const double Pi = 3.141592653589; // const double Pi = std::numbers::pi
const auto Use = Pi / 2; // no match for Pi
static constexpr double Euler = 2.7182818; // static constexpr double Euler = std::numbers::e;
log2(exp(1)); // std::numbers::log2e;
log2(Euler); // std::numbers::log2e;
1 / sqrt(MY_PI); // std::numbers::inv_sqrtpi;
sink(MY_PI); // sink(std::numbers::pi);
floatSink(MY_PI); // floatSink(std::numbers::pi);
floatSink(static_cast<float>(MY_PI)); // floatSink(std::numbers::pi_v<float>);
}
گزینهها
- DiffThreshold
- یک مقدار ممیز شناور که آستانه تشخیص را برای زمانی که لیترالها با یک ثابت مطابقت دارند تعیین میکند. یک مقدار لیترال با یک ثابت مطابقت دارد اگر عبارت abs(literal - constant) < DiffThreshold به true ارزیابی شود. مقدار پیشفرض 0.001 است.
- IncludeStyle
- رشتهای که مشخص میکند کدام سبک include استفاده میشود: llvm یا google. مقدار پیشفرض llvm است.
modernize-use-std-print
فراخوانیهای printf، fprintf، absl::PrintF و absl::FPrintf را با اصلاح مناسب رشته قالببندی (format string)، به فراخوانیهای معادل std::print یا std::println در C++23 تبدیل میکند. توابع جایگزینشده و توابع جایگزین را میتوان از طریق گزینههای پیکربندی سفارشیسازی کرد. برای هر آرگومانی که نتیجه فراخوانی std::string::c_str() و std::string::data() باشد، این فراخوانی غیرضروری به روشی مشابه با بررسی readability-redundant-string-cstr حذف خواهد شد.
به عبارت دیگر، خطوطی مانند:
fprintf(stderr, "The %s is %3d\n", description.c_str(), value);
را به این صورت تبدیل میکند:
std::println(stderr, "The {} is {:3}", description, value);
اگر گزینههای ReplacementPrintFunction یا ReplacementPrintlnFunction روی مقادیر پیشفرض خود باقی بمانند یا تنظیم شوند، این بررسی فقط با -std=c++23 یا بالاتر فعال میشود.
ماکروهایی که با PRI و __PRI از <inttypes.h> شروع میشوند باز (expand) میشوند، کاراکترهای گریز (escaping) مدیریت شده و رشتههای مجاور به یکدیگر متصل میشوند تا قبل از تبدیل رشته قالببندی، یک StringLiteral واحد تشکیل دهند. استفاده از هرگونه ماکروی دیگر در رشته قالببندی باعث صدور پیام هشدار شده و هیچ تبدیلی انجام نخواهد شد. رشته قالببندی تبدیلشده همیشه یک رشته لیترال واحد خواهد بود.
این بررسی عملکرد خوبی دارد، اما بینقص نیست. بهویژه:
- فرض میکند که رشته قالببندی برای آرگومانها صحیح است. اگر هنگام کامپایل با -Wformat هشداری دریافت کنید، احتمال بروز رفتار نادرست وجود دارد.
- در نقطهای که بررسی اجرا میشود، درخت نحو انتزاعی (AST) حاوی یک StringLiteral واحد برای رشته قالببندی است که در آن کاراکترهای گریز باز شدهاند. این بررسی تلاش میکند دنبالههای گریز را بازسازی کند؛ ممکن است آنها دقیقاً همانگونه که نوشته شده بودند نباشند (به عنوان مثال، "\x41\x0a" به "A\n" و "ab" "cd" به "abcd" تبدیل میشود).
- از پهنای فیلد، دقت، آرگومانهای مکانی، صفرهای ابتدایی، علامت + ابتدایی، تراز کردن و فرمهای جایگزین پشتیبانی میکند.
- استفاده از هرگونه فلگ یا مشخصهکننده پشتیبانینشده باعث میشود که کل عبارت دستنخورده باقی بماند و یک هشدار صادر شود. ویژگیهای خاص پشتیبانینشده عبارتند از:
- فلگ %' برای جداکننده هزارگان.
- افزونه %m در glibc.
- •
- تابع printf و توابع مشابه تعداد کاراکترهای چاپشده را برمیگردانند، اما std::print چنین نمیکند. این بدان معناست که هر فراخوانی که از مقدار بازگشتی استفاده کند، تبدیل نخواهد شد. متأسفانه این موضوع در حال حاضر شامل تبدیل صریح به void نیز میشود. کاستیهای این بررسی بدین معناست که فراخوانیهای درون عبارتهای مرکب GCC (compound statements) حتی اگر مقدار حاصل استفاده نشود، قابل تبدیل نیستند.
اگر تبدیل ناقص یا ناامن باشد، کل فراخوانی بدون تغییر رها خواهد شد.
اگر فراخوانی برای تبدیل مناسب تشخیص داده شود:
- توابع printf، fprintf، absl::PrintF، absl::FPrintF و هر تابعی که با گزینه PrintfLikeFunctions یا FprintfLikeFunctions مشخص شده باشد، در صورتی که رشته قالببندی به \n ختم شود با تابع مشخصشده توسط گزینه ReplacementPrintlnFunction و در غیر این صورت با ReplacementPrintFunction جایگزین میشوند.
- رشته قالببندی برای استفاده از زبان std::formatter بازنویسی میشود. اگر یک \n در انتهای رشته قالببندی یافت شود که قبل از آن r نباشد، آن کاراکتر حذف شده و بهجای ReplacementPrintFunction از ReplacementPrintlnFunction استفاده میشود.
- هر آرگومانی که متناظر با مشخصهکنندههای %p بوده و std::formatter آن را نمیپذیرد، در یک static_cast به const void * قرار میگیرد.
- هر آرگومانی که متناظر با مشخصهکنندههای %s بوده و نوع آرگومان signed char یا unsigned char باشد، در یک reinterpret_cast<const char *> قرار میگیرد.
- در صورت فعال بودن StrictMode، هر آرگومانی که علامتدار بودن (signedness) آن با پارامتر متناظر در رشته قالببندی متفاوت باشد، در یک static_cast مناسب قرار میگیرد.
- هر آرگومانی که به یک فراخوانی std::string::c_str() یا std::string::data() ختم شود، آن فراخوانی از آن حذف خواهد شد.
گزینهها
int i = -42;
unsigned int u = 0xffffffff;
printf("%u %d\n", i, u);
به این صورت تبدیل خواهد شد:
std::print("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u));
تا اطمینان حاصل شود که خروجی همچنان نمایش بدون علامت -42 و نمایش علامتدار 0xffffffff خواهد بود (اغلب به ترتیب 4294967254 و -1). در صورت تنظیم روی false (که پیشفرض است)، این تبدیل نوعها اضافه نخواهند شد که ممکن است باعث تغییر در خروجی شود.
- PrintfLikeFunctions
- فهرستی جدا شده با نقطهویرگول از عبارات باقاعده (regular expressions) که با نامهای (کاملاً واجد شرایط / fully qualified) توابعی که باید جایگزین شوند مطابقت دارد؛ با این شرط که اولین پارامتر شامل رشته قالببندی سبک printf باشد و آرگومانهایی که باید قالببندی شوند بلافاصله پس از آن قرار گیرند. نام توابع عضو واجد شرایط پشتیبانی میشوند، اما نام تابع جایگزین باید فاقد صلاحیت (unqualified) باشد. اگر نه این گزینه و نه FprintfLikeFunctions تنظیم نشده باشند، مقدار پیشفرض printf; absl::PrintF خواهد بود، در غیر این صورت یک رشته خالی است.
- FprintfLikeFunctions
- فهرستی جدا شده با نقطهویرگول از عبارات باقاعده که با نامهای (کاملاً واجد شرایط) توابعی که باید جایگزین شوند مطابقت دارد؛ با این شرط که پارامتر اول حفظ شود، پارامتر دوم شامل رشته قالببندی سبک printf باشد و آرگومانهایی که باید قالببندی شوند بلافاصله پس از آن قرار گیرند. نام توابع عضو واجد شرایط پشتیبانی میشوند، اما نام تابع جایگزین باید فاقد صلاحیت باشد. اگر نه این گزینه و نه PrintfLikeFunctions تنظیم نشده باشند، مقدار پیشفرض fprintf;absl::FPrintF خواهد بود، در غیر این صورت یک رشته خالی است.
- ReplacementPrintFunction
- تابعی که در حین تبدیل به جای std::print پیشفرض برای جایگزینی printf، fprintf و غیره استفاده خواهد شد، زمانی که رشته قالب اصلی به \n ختم نمیشود. انتظار میرود که این تابع رابطی سازگار با std::print ارائه دهد. یک کاندیدای مناسب fmt::print خواهد بود.
- ReplacementPrintlnFunction
- تابعی که در حین تبدیل به جای std::println پیشفرض برای جایگزینی printf، fprintf و غیره استفاده خواهد شد، زمانی که رشته قالب اصلی به \n ختم میشود. انتظار میرود که این تابع رابطی سازگار با std::println ارائه دهد. یک کاندیدای مناسب fmt::println خواهد بود.
- PrintHeader
- هدر فایلی
که باید
برای اعلان
ReplacementPrintFunction
گنجانده
شود تا در
صورت نیاز
دستور #include
اضافه شود.
اگر ReplacementPrintFunction
برابر با
std::print باشد،
این گزینه
به طور
پیشفرض
روی <print>
تنظیم
میشود؛ در
غیر این
صورت، این
گزینه به
طور
پیشفرض
تهی خواهد
بود و هیچ
دستور #include
اضافهای
درج
نمیشود.
امضای توابع و عبارات lambda را برای استفاده از نوع بازگشتی پسآیند (trailing return type، معرفیشده در C++11) بازنویسی میکند. این تبدیل صرفاً جنبه نگارشی دارد. نوع بازگشتی قبل از نام تابع با auto جایگزین شده و پس از فهرست پارامترهای تابع (و توصیفکنندهها) درج میشود.
مثال
int f1();
inline int f2(int arg) noexcept;
virtual float f3() const && = delete;
auto lambda = []() {};
تبدیل میشود به:
auto f1() -> int;
inline auto f2(int arg) -> int noexcept;
virtual auto f3() const && -> float = delete;
auto lambda = []() -> void {};
محدودیتها
دستههای زیر از انواع بازگشتی در حال حاضر قابل بازنویسی نیستند:
- اشارهگرهای تابع (function pointers)
- اشارهگرهای توابع عضو (member function pointers)
- اشارهگرهای اعضا (member pointers)
نامهای بدون تعیین دامنه (unqualified names) در نوع بازگشتی ممکن است پس از بازنویسی به اشتباه به موجودیتهای متفاوتی اشاره کنند. جلوگیری از چنین خطاهایی نیازمند یک جستجوی کامل (lookup) برای تمام نامهای بدون تعیین دامنه موجود در نوع بازگشتی، در حوزهٔ (scope) مکان نوع بازگشتی پسآیند است. این مکان برای مثال شامل نام پارامترهای تابع و اعضای کلاس دربرگیرنده (شامل تمام کلاسهای به ارث رسیده) میشود. چنین جستجویی در حال حاضر پیادهسازی نشده است.
با فرض قطعهکد زیر:
struct S { long long value; };
S f(unsigned S) { return {S * 2}; }
class CC {
int S;
struct S m();
};
S CC::m() { return {0}; }
یک بازنویسی بیدقت خروجی زیر را تولید خواهد کرد:
struct S { long long value; };
auto f(unsigned S) -> S { return {S * 2}; } // error
class CC {
int S;
auto m() -> struct S;
};
auto CC::m() -> S { return {0}; } // error
کامپایل این کد با شکست مواجه میشود زیرا S در متن تابع f به پارامتر همنام تابع اشاره دارد. بهطور مشابه، S در متن تابع m به عضو همنام کلاس اشاره میکند. این بررسی در حال حاضر تنها قادر به شناسایی و جلوگیری از تداخل با نام پارامترهای تابع است.
گزینهها
- TransformFunctions
- هنگامی که روی true تنظیم شود، اعلانهای توابع برای استفاده از نوع بازگشتی پسآیند تبدیل خواهند شد. مقدار پیشفرض true است.
- TransformLambdas
- نحوه تبدیل عبارات lambda برای استفاده از نوع بازگشتی پسآیند را کنترل میکند. مقادیر مجاز عبارتند از:
- all - تمام عبارات lambda فاقد نوع بازگشتی صریح را برای استفاده از نوع بازگشتی پسآیند تبدیل میکند. اگر نوع قابل استنتاج نباشد، از C++14 به بعد از auto استفاده شده و در غیر این صورت یک پیام عمومی صادر خواهد شد.
- all_except_auto - تمام عبارات lambda را بهجز مواردی که نوع بازگشتی آنها قابل استنتاج نیست تبدیل میکند.
- none - هیچیک از عبارات lambda را تبدیل نمیکند.
مقدار پیشفرض all است.
modernize-use-transparent-functors
فانکتورهای شفاف (transparent functors) را به فانکتورهای غیرشفاف ترجیح دهید. هنگام استفاده از فانکتورهای شفاف، نیازی به تکرار نوع داده نیست. کد خواناتر شده، نگهداری آن آسانتر میشود و کمتر در معرض خطا قرار میگیرد. همچنین امکان رخ دادن تبدیلهای ناخواسته وجود ندارد.
// Non-transparent functor std::map<int, std::string, std::greater<int>> s; // Transparent functor. std::map<int, std::string, std::greater<>> s; // Non-transparent functor using MyFunctor = std::less<MyType>;
با این حال، این تبدیل همیشه ایمن نیست. مورد زیر برای حفظ معناشناسی دستنخورده باقی میماند.
// Non-transparent functor std::map<const char *, std::string, std::greater<std::string>> s;
گزینهها
- SafeMode
- اگر این گزینه روی true تنظیم شود، این بررسی مواردی را که در آنها تضمین نمیشود استفاده از فانکتور شفاف نتایجی دقیقاً مشابه کد اصلی ایجاد کند، عیبیابی نمیکند. مقدار پیشفرض برای این گزینه false است.
اجرای این بررسی نیازمند استفاده از C++14 یا بالاتر است.
modernize-use-uncaught-exceptions
این بررسی فراخوانیهای تابع std::uncaught_exception را هشدار داده و آنها را با فراخوانیهای std::uncaught_exceptions جایگزین میکند، چرا که std::uncaught_exception از نسخهٔ C++17 منسوخ (deprecated) شده است.
در زیر چند نمونه از مواردی که شناسایی شده و نحوهٔ جایگزینی آنها آورده شده است.
#define MACRO1 std::uncaught_exception
#define MACRO2 std::uncaught_exception
int uncaught_exception() {
return 0;
}
int main() {
int res;
res = uncaught_exception();
// No warning, since it is not the deprecated function from namespace std
res = MACRO2();
// Warning, but will not be replaced
res = std::uncaught_exception();
// Warning and replaced
using std::uncaught_exception;
// Warning and replaced
res = uncaught_exception();
// Warning and replaced
}
پس از اعمال اصلاحات، کد به صورت زیر خواهد بود:
#define MACRO1 std::uncaught_exception
#define MACRO2 std::uncaught_exception
int uncaught_exception() {
return 0;
}
int main() {
int res;
res = uncaught_exception();
res = MACRO2();
res = std::uncaught_exceptions();
using std::uncaught_exceptions;
res = uncaught_exceptions();
}
modernize-use-using
این بررسی استفاده از typedef را به کلیدواژهٔ using تبدیل میکند.
قبل:
typedef int variable;
class Class{};
typedef void (Class::* MyPtrType)() const;
typedef struct { int a; } R_t, *R_p;
بعد:
using variable = int;
class Class{};
using MyPtrType = void (Class::*)() const;
using R_t = struct { int a; };
using R_p = R_t*;
بررسیکننده از typedef درون بلوکهای extern "C" { ... } صرفنظر میکند.
extern "C" {
typedef int InExternC; // Left intact.
}
اجرای این بررسی نیازمند استفاده از C++11 یا بالاتر است.
گزینهها
- IgnoreMacros
- اگر روی true تنظیم شود، بررسی در داخل ماکروها هشداری صادر نخواهد کرد. پیشفرض برابر true است.
- IgnoreExternC
- اگر روی true تنظیم شود، بررسی در داخل حوزهٔ extern "C" هشداری صادر نخواهد کرد. پیشفرض برابر false است.
mpi-buffer-deref
این بررسی بررسی میکند که آیا یک بافر ارسالشده به تابع MPI (Message Passing Interface) به اندازهٔ کافی ارجاعزدایی (dereference) شده است یا خیر. بافرها باید به صورت یک اشارهگر تکی یا آرایه ارسال شوند. از آنجا که ساختار توابع MPI نوع بافر خود را void * مشخص میکنند، ممکن است بافرهایی با ارجاعزدایی ناکافی، مانند اشارهگرهای دوگانه یا آرایههای چندبعدی، بدون اینکه هیچ هشداری از سوی کامپایلر صادر شود، ارسال گردند.
مثالها:
// A double pointer is passed to the MPI function. char *buf; MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); // A multidimensional array is passed to the MPI function. short buf[1][1]; MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD); // A pointer to an array is passed to the MPI function. short *buf[1]; MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
mpi-type-mismatch
این بررسی تطابق میان نوع بافر و جفتهای نوعدادهٔ MPI (رابط تبادل پیام) را برای توابع MPI استفادهشده بررسی میکند. تمام انواعدادههای MPI که توسط استاندارد MPI (نسخهٔ 3.1) تعریف شدهاند، توسط این بررسی ارزیابی میشوند. تعریفهای typedef سفارشی کاربر، انواعدادههای سفارشی MPI و مقادیر ثابت اشارهگر تهی (null pointer) در فرایند راستیآزمایی نادیده گرفته میشوند.
مثال:
// In this case, the buffer type matches MPI datatype. char buf; MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); // In the following case, the buffer type does not match MPI datatype. int buf; MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
objc-assert-equals
استفادههای نادرست از XCTAssertEqual و XCTAssertNotEqual را یافته و آنها را با XCTAssertEqualObjects یا XCTAssertNotEqualObjects جایگزین میکند.
این کار موجب پایداری بیشتر آزمونها میشود، زیرا بسیاری از آنها به شکل نادرستی بر برابری اشارهگرها برای رشتههایی که مقادیر یکسان دارند تکیه میکنند؛ فرضیهای که توسط زبان تضمین نشده است.
objc-avoid-nserror-init
راهاندازیهای اولیهٔ نادرست شیءهای NSError را مییابد.
طبق مستندات توسعهدهندگان Apple، همواره باید از متد کارخانهای errorWithDomain:code:userInfo: برای ساخت شیءهای جدید NSError به جای [NSError alloc] init] استفاده کنیم؛ در غیر این صورت در زمان اجرا منجر به دریافت پیام هشدار خواهد شد.
اطلاعات مرتبط پیرامون ساخت NSError: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/CreateCustomizeNSError/CreateCustomizeNSError.html
objc-dealloc-in-category
پیادهسازیهای -dealloc را در دستهبندیهای (categories) زبان Objective-C مییابد. پیادهسازی دستهبندی هرگونه -dealloc را در پیادهسازی کلاس بازنویسی (override) خواهد کرد که بالقوه باعث ایجاد مشکلاتی میشود.
کلاسها -dealloc را برای انجام اقدامات مهم آزادسازی یک شیء پیادهسازی میکنند. اگر یک دستهبندی روی آن کلاس متد -dealloc را پیادهسازی کند، پیادهسازی خود کلاس را بازنویسی کرده و ممکن است رفتارهای غیرمنتظرهای در آزادسازی حافظه رخ دهد.
objc-forbidden-subclassing
کلاسهای Objective-C که زیرکلاس (subclass) کلاسهایی هستند که برای ارثبری طراحی نشدهاند را مییابد.
بهطور پیشفرض، شامل فهرستی از کلاسهای Objective-C است که در مستندات رسمی آنها اعلام شده از زیرکلاسسازی پشتیبانی نمیکنند.
نکته:
گزینهها
- ForbiddenSuperClassNames
- فهرستی
جداشده با
نقطهویرگول
(semicolon) از
نامهای
کلاسهای
Objective-C که از
ایجاد
زیرکلاس
پشتیبانی
نمیکنند.
پیشفرض: ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView.
objc-missing-hash
پیادهسازیهای Objective-C که متد -isEqual: را بدون پیادهسازی متناسب -hash پیادهسازی کردهاند را پیدا میکند.
مستندات Apple تأکید میکند اشیائی که با هم برابرند باید مقدار هش یکسانی داشته باشند: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418795-isequal?language=objc
توجه داشته باشید که این بررسی تنها وجود -hash را در سناریوهایی بررسی میکند که عدم وجود آن ممکن است رفتارهای نامطلوب ایجاد کند. راستیآزمایی پیادهسازی -hash بر عهدهٔ توسعهدهنده است، مثلاً از طریق افزودن آزمونهای واحد (unit tests) برای بررسی صحت پیادهسازی.
objc-nsdate-formatter
هنگامی که NSDateFormatter برای تبدیل یک نوع NSDate به نوع String استفاده میشود، کاربر میتواند یک رشتهٔ قالببندی سفارشی را تعیین کند. برخی از مشخصهسازهای فرمت با وجود معتبر بودن، نامطلوب هستند. برای مشاهدهٔ تمام الگوهای معتبر تاریخ، ببینید: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
این بررسیکننده الگوهای رشتهای زیر را در یک مشخصهساز فرمت تاریخ بهعنوان هشدار گزارش میکند:
- 1.
- yyyy + ww : مشخص کردن سال تقویمی به همراه هفته از سال هفتگی (مگر اینکه YYYY نیز مشخص شده باشد).
-
Example 1: Input Date: 29 December 2014 ; Format String: yyyy-ww; Output string: 2014-01 (Wrong because it’s not the first week of 2014)
-
Example 2: Input Date: 29 December 2014 ; Format String: dd-MM-yyyy (ww-YYYY); Output string: 29-12-2014 (01-2015) (This is correct)
- 2.
- F بدون ee/EE : روز عددی هفته در یک ماه بدون روز واقعی.
- •
-
Example: Input Date: 29 December 2014 ; Format String: F-MM; Output string: 5-12 (Wrong because it reads as 5th ___ of Dec in English)
- 3.
- F بدون MM : روز عددی هفته در یک ماه بدون ماه.
- •
-
Example: Input Date: 29 December 2014 ; Format String: F-EE Output string: 5-Mon (Wrong because it reads as 5th Mon of ___ in English)
- 4.
- WW بدون MM : هفتهٔ ماه بدون ماه.
- •
-
Example: Input Date: 29 December 2014 ; Format String: WW-yyyy Output string: 05-2014 (Wrong because it reads as 5th Week of ___ in English)
- 5.
- YYYY + QQ : سال هفتگی مشخصشده به همراه فصل سال عادی (مگر اینکه yyyy نیز مشخص شده باشد).
-
Example 1: Input Date: 29 December 2014 ; Format String: YYYY-QQ Output string: 2015-04 (Wrong because it’s not the 4th quarter of 2015)
-
Example 2: Input Date: 29 December 2014 ; Format String: ww-YYYY (QQ-yyyy) Output string: 01-2015 (04-2014) (This is correct)
- 6.
- YYYY + MM : سال هفتگی مشخصشده به همراه ماه سال تقویمی (مگر اینکه yyyy نیز مشخص شده باشد).
-
Example 1: Input Date: 29 December 2014 ; Format String: YYYY-MM Output string: 2015-12 (Wrong because it’s not the 12th month of 2015)
-
Example 2: Input Date: 29 December 2014 ; Format String: ww-YYYY (MM-yyyy) Output string: 01-2015 (12-2014) (This is correct)
- 7.
- YYYY + DD : سال هفتگی به همراه روز سال تقویمی (مگر اینکه yyyy نیز مشخص شده باشد).
-
Example 1: Input Date: 29 December 2014 ; Format String: YYYY-DD Output string: 2015-363 (Wrong because it’s not the 363rd day of 2015)
-
Example 2: Input Date: 29 December 2014 ; Format String: ww-YYYY (DD-yyyy) Output string: 01-2015 (363-2014) (This is correct)
- 8.
- YYYY + WW : سال هفتگی به همراه هفتهٔ سال تقویمی (مگر اینکه yyyy نیز مشخص شده باشد).
-
Example 1: Input Date: 29 December 2014 ; Format String: YYYY-WW Output string: 2015-05 (Wrong because it’s not the 5th week of 2015)
-
Example 2: Input Date: 29 December 2014 ; Format String: ww-YYYY (WW-MM-yyyy) Output string: 01-2015 (05-12-2014) (This is correct)
- 9.
- YYYY + F : سال مبتنی بر هفته به همراه روز هفته در یک ماه تقویمی (مگر اینکه yyyy نیز مشخص شده باشد).
-
مثال ۱: تاریخ ورودی: 29 December 2014 ؛ رشته قالب: YYYY-ww-F-EE رشته خروجی: 2015-01-5-Mon (نادرست است زیرا پنجمین دوشنبه ژانویه در سال ۲۰۱۵ نیست)
-
مثال ۲: تاریخ ورودی: 29 December 2014 ؛ رشته قالب: ww-YYYY (F-EE-MM-yyyy) رشته خروجی: 01-2015 (5-Mon-12-2014) (این مورد درست است)
objc-nsinvocation-argument-lifetime
فراخوانی متدهای NSInvocation را تحت ARC که دارای طول عمر شیء آرگومان مناسبی نیستند، پیدا میکند. هنگام ارسال اشیاء Objective-C به عنوان پارامتر به متدهای getArgument:atIndex: و getReturnValue: در NSInvocation، مقادیر بهصورت مقداری (by value) در اشارهگر آرگومان کپی میشوند؛ امری که اگر اشارهگرهای شیء بهصورت __unsafe_unretained اعلان نشده باشند، منجر به رفتار آزادسازی (release) نادرست میشود.
برای کد:
id arg; [invocation getArgument:&arg atIndex:2]; __strong id returnValue; [invocation getReturnValue:&returnValue];
اصلاح به صورت زیر خواهد بود:
__unsafe_unretained id arg; [invocation getArgument:&arg atIndex:2]; __unsafe_unretained id returnValue; [invocation getReturnValue:&returnValue];
این بررسی در صورت ارسال ارجاع متغیرهای نمونهای (instance variable) که طول عمری غیر از __unsafe_unretained دارند، هشدار میدهد، اما اصلاحیهای پیشنهاد نمیکند:
// "id _returnValue" is declaration of instance variable of class. [invocation getReturnValue:&self->_returnValue];
objc-property-declaration
اعلانهای ویژگی (property) را در فایلهای Objective-C که از الگوی نامگذاری ویژگیها در راهنمای برنامهنویسی Apple پیروی نمیکنند، پیدا میکند. نام ویژگی باید در قالب Lower Camel Case باشد.
برای کد:
@property(nonatomic, assign) int LowerCamelCase;
اصلاح به صورت زیر خواهد بود:
@property(nonatomic, assign) int lowerCamelCase;
این بررسی تنها 'CamelCase' را به 'camelCase' اصلاح میکند. در برخی موارد دیگر تنها پیامهای هشدار ارائه خواهد شد، زیرا ممکن است نام ویژگی پیچیده باشد. کاربران باید خودشان نام مناسبی پیدا کنند.
این بررسی همچنین مخففهای خاص را به عنوان پیشوند یا پسوند میپذیرد. چنین پیشوندها یا پسوندهایی طبق راهنما، بررسی Lower Camel Case را نادیده میگیرند: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingBasics.html#//apple_ref/doc/uid/20001281-1002931-BBCFHEAB
برای فهرست کاملی از مخففهای شناختهشده: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE
قاعده شیوهنامه متناظر: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-1001757
این بررسی همچنین ویژگیهای اعلانشده در category را که با پیشوندی از حروف کوچک به همراه یک '_' برای جلوگیری از تداخل نامگذاری آغاز میشوند، میپذیرد. برای نمونه:
@property(nonatomic, assign) int abc_lowerCamelCase;
قاعده شیوهنامه متناظر: https://developer.apple.com/library/content/qa/qa1908/_index.html
objc-super-self
فراخوانیهای -self را روی نمونههای super در مقداردهندههای اولیه (initializers) زیرکلاسهای NSObject پیدا کرده و پیشنهاد میکند به جای آن، مقداردهنده اولیه کلاس والد فراخوانی شود.
فراخوانی -self روی نمونههای super در مقداردهندههای اولیه یک خطای رایج برنامهنویسی است، در حالی که هدف اصلی برنامهنویس فراخوانی مقداردهنده اولیه کلاس والد بوده است. عدم فراخوانی مقداردهنده اولیه کلاس والد، زنجیره مقداردهی اولیه را مختل کرده و میتواند منجر به مقداردهی اولیه نامعتبر شیء شود.
openmp-exception-escape
بلوکهای ساختیافته OpenMP (اصطلاحاً Structured Blocks) را تحلیل کرده و بررسی میکند که هیچ استثنایی (exception) به خارج از بلوک ساختیافتهای که در آن پرتاب شده، فرار نکند.
طبق مشخصات OpenMP، یک بلوک ساختیافته یک دستور اجرایی، احتمالاً مرکب، با یک نقطه ورود در بالا و یک نقطه خروج در پایین است. به این معنی که نمیتوان از throw برای «خروج» از بلوک ساختیافته استفاده کرد. اگر یک استثنا در همان بلوک ساختیافتهای که در آن پرتاب شده گرفته نشود (catch نشود)، رفتار تعریفنشده خواهد بود.
FIXME: این بررسی رفتارهای SEH و setjmp/longjmp را مدلسازی نمیکند.
هشدار! این بررسی ممکن است روی فایلهای منبع بزرگ پرهزینه و زمانبر باشد.
گزینهها
- IgnoredExceptions
- فهرستی جداشده با کاما از نام نوعهایی که در این بررسی به عنوان استثناهای پرتابشده محسوب نمیشوند. مقدار پیشفرض یک رشته خالی است.
openmp-use-default-none
دستورهای OpenMP را که مجاز به داشتن بند default هستند، اما یا آن را مشخص نکردهاند یا بند با نوعی غیر از none مشخص شده است، پیدا کرده و پیشنهاد میکند که از بند default(none) استفاده شود.
استفاده از بند default(none) توسعهدهندگان را وادار میکند تا ویژگیهای اشتراکگذاری داده (data sharing attributes) را برای متغیرهای ارجاعشده در سازه به طور صریح مشخص کنند؛ بنابراین کاملاً مشخص میشود کدام متغیرها مورد ارجاع قرار گرفتهاند و ویژگی اشتراک داده آنها چیست. این کار خوانایی کد را افزایش داده و احتمالاً شناسایی خطاها را آسانتر میسازد.
مثال
// ``for`` directive cannot have ``default`` clause, no diagnostics.
void n0(const int a) {
#pragma omp for
for (int b = 0; b < a; b++)
;
}
// ``parallel`` directive.
// ``parallel`` directive can have ``default`` clause, but said clause is not
// specified, diagnosed.
void p0_0() {
#pragma omp parallel
;
// WARNING: OpenMP directive ``parallel`` does not specify ``default``
// clause. Consider specifying ``default(none)`` clause.
}
// ``parallel`` directive can have ``default`` clause, and said clause is
// specified, with ``none`` kind, all good.
void p0_1() {
#pragma omp parallel default(none)
;
}
// ``parallel`` directive can have ``default`` clause, and said clause is
// specified, but with ``shared`` kind, which is not ``none``, diagnose.
void p0_2() {
#pragma omp parallel default(shared)
;
// WARNING: OpenMP directive ``parallel`` specifies ``default(shared)``
// clause. Consider using ``default(none)`` clause instead.
}
// ``parallel`` directive can have ``default`` clause, and said clause is
// specified, but with ``firstprivate`` kind, which is not ``none``, diagnose.
void p0_3() {
#pragma omp parallel default(firstprivate)
;
// WARNING: OpenMP directive ``parallel`` specifies ``default(firstprivate)``
// clause. Consider using ``default(none)`` clause instead.
}
performance-avoid-endl
استفاده از std::endl روی جریانها را بررسی کرده و پیشنهاد میکند به جای آن از نویسه خط جدید '\n' استفاده شود.
دلیل منطقی: استفاده از std::endl روی جریانها میتواند کارایی کمتری نسبت به استفاده از نویسه خط جدید '\n' داشته باشد؛ زیرا std::endl دو عملیات انجام میدهد: یک نویسه خط جدید در جریان خروجی مینویسد و سپس بافر جریان را تخلیه (flush) میکند. نوشتن یک نویسه خط جدید تکی با استفاده از '\n' عملیات تخلیه بافر را آغاز نمیکند که میتواند کارایی را بهبود بخشد. علاوه بر این، تخلیه بافر جریان در هنگام کار با جریانهای دارای بافر میتواند بار اضافی (overhead) ایجاد کند.
مثال:
کد زیر را در نظر بگیرید:
#include <iostream>
int main() {
std::cout << "Hello" << std::endl;
}
که تبدیل میشود به:
#include <iostream>
int main() {
std::cout << "Hello" << '\n';
}
این کد یک نویسه خط جدید تکی را بدون تخلیه بافر در جریان std::cout مینویسد.
علاوه بر این، توجه به این نکته مهم است که جریانهای استاندارد C++ (مانند std::cerr، std::wcerr، std::clog و std::wclog) همواره پس از هر عملیات نوشتن عمل تخلیه را انجام میدهند، مگر اینکه مقدار std::ios_base::sync_with_stdio برابر با false تنظیم شده باشد؛ صرفنظر از اینکه std::endl استفاده شود یا '\n'. بنابراین، استفاده از '\n' با این جریانها هیچ بهبود کارایی به همراه نخواهد داشت، اما همچنان توصیه میشود برای حفظ یکدستی و خوانایی از '\n' استفاده شود.
اگر واقعاً نیاز به تخلیه بافر جریان دارید، میتوانید صراحتاً از std::flush به این صورت استفاده کنید:
#include <iostream>
int main() {
std::cout << "Hello\n" << std::flush;
}
performance-enum-size
بر اساس بازه مقادیر اعضا، کوچکترین نوع زیربنایی (underlying type) ممکن را برای یک enum یا enum class پیشنهاد میدهد. مقادیر اعضا در یک enum یا enum class، از جمله مقادیر علامتدار را تحلیل میکند تا کوچکترین نوع زیربنایی ممکن را که بتواند تمام مقادیر enum را نمایش دهد، توصیه کند. انواع زیربنایی پیشنهادی شامل انواع صحیح std::uint8_t، std::uint16_t و std::uint32_t برای انواع بدون علامت، و std::int8_t، std::int16_t و std::int32_t برای انواع علامتدار هستند. استفاده از انواع زیربنایی پیشنهادی میتواند به کاهش ردپای حافظه (memory footprint) برنامه و بهبود کارایی در برخی موارد کمک کند.
برای مثال:
// BEFORE
enum Color {
RED = -1,
GREEN = 0,
BLUE = 1
};
std::optional<Color> color_opt;
در اینجا، Color enum از نوع زیربنایی پیشفرض استفاده میکند که در این حالت int است و اعضای آن مقادیر -1، 0 و 1 دارند. علاوه بر این، شیء std::optional<Color> به دلیل پرسازی یا پدینگ (بسته به بستر/پلتفرم) ۸ بایت فضا اشغال میکند.
// AFTER
enum Color : std::int8_t {
RED = -1,
GREEN = 0,
BLUE = 1
}
std::optional<Color> color_opt;
در نسخه اصلاحشده Color enum، نوع زیربنایی به std::int8_t تغییر یافته است. مقدار عضو RED برابر با -1 است که میتواند توسط یک عدد صحیح ۸ بیتی علامتدار نمایش داده شود.
با استفاده از نوع زیربنایی کوچکتر، ردپای حافظه Color enum از ۴ بایت به ۱ بایت کاهش مییابد. نسخه اصلاحشده شیء std::optional<Color> تنها به ۲ بایت نیاز خواهد داشت (به دلیل حذف پدینگ)، زیرا حاوی یک تک بایت برای Color enum و یک تک بایت برای پرچم bool است که مشخص میکند آیا مقدار اختیاری تنظیم شده است یا خیر.
کاهش ردپای حافظه یک enum میتواند از نظر مصرف حافظه و کارایی حافظه موقت (cache) مزایای چشمگیری داشته باشد. با این حال، در نظر گرفتن معایب و مصالحهها و تأثیر بالقوه آن بر خوانایی و نگهداریپذیری کد اهمیت دارد.
انواع Enum بدون عضو (خالی) از تحلیل مستثنی میشوند.
به C++11 یا بالاتر نیاز دارد. اصلاحات خودکار (auto-fixes) ارائه نمیدهد.
گزینهها
- EnumIgnoreList
- این گزینه برای نادیده گرفتن انواع enum مشخصی استفاده میشود. این گزینه فهرستی جداشده با نقطه-ویرگول (semicolon) از نامهای کامل (fully qualified) انواع enum یا عبارات با قاعده (regular expressions) که با نامهای انواع enum مطابقت دارند را میپذیرد. مقدار پیشفرض یک رشته خالی است، به این معنی که هیچ نوع enum نادیده گرفته نخواهد شد.
performance-faster-string-find
بهینهسازی فراخوانیهای std::string::find() و موارد مشابه آن هنگامی که رشتهٔ مورد جستجو (needle) پاسدادهشده یک لیترال رشتهای تکنویسهای باشد. اورلود لیترال نویسهای کارآمدتر است.
مثالها:
str.find("A");
// becomes
str.find('A');
گزینهها
- StringLikeClasses
- فهرستی جداشده با نقطه-ویرگول از نام کلاسهای شبهرشته. بهطور پیشفرض تنها ::std::basic_string و ::std::basic_string_view در نظر گرفته میشوند. این بررسی تنها توابع عضوی با نامهای find، rfind، find_first_of، find_first_not_of، find_last_of، یا find_last_not_of را درون این کلاسها بررسی میکند.
performance-for-range-copy
حلقههای مبتنی بر بازه (range-based for) در C++11 را پیدا میکند که متغیر حلقه در هر تکرار کپی میشود، در حالی که دریافت آن از طریق ارجاع ثابت (const reference) کافی خواهد بود.
این بررسی تنها بر روی متغیرهای حلقهای اعمال میشود که کپی کردن نوع آنها هزینهبر (expensive) است؛ یعنی بهسادگی قابل کپی (trivially copyable) نیستند یا دارای سازنده کپی (copy constructor) یا مخرب (destructor) غیربدیهی هستند.
برای اطمینان از اینکه جایگزینی کپی با یک const reference ایمن است، روش ابتکاری (heuristic) زیر بهکار گرفته میشود:
- 1.
- متغیر حلقه مقید به const باشد.
- 2.
- متغیر حلقه const نباشد، اما تنها متدها یا عملگرهای const روی آن فراخوانی شده باشند، یا بهعنوان آرگومان مقداری یا ارجاع ثابت در سازندهها یا فراخوانیهای توابع استفاده شده باشد.
گزینهها
- WarnOnAllAutoCopies
- هنگامی که true باشد، در مورد هرگونه استفاده از auto بهعنوان نوع متغیر حلقهٔ range-based for هشدار میدهد. پیشفرض false است.
- AllowedTypes
- فهرستی جداشده با نقطه-ویرگول از نام انواع مجاز برای کپی شدن در هر تکرار. عبارات با قاعده نیز پذیرفته میشوند؛ بهعنوان مثال [Rr]ef(erence)?$ با هر نوعی که پسوند Ref، ref، Reference و reference داشته باشد مطابقت دارد. مقدار پیشفرض خالی است. اگر نامی در فهرست شامل دنبالهٔ :: باشد، با نام کامل نوع (یعنی namespace::Type) مطابقت داده میشود، در غیر این صورت تنها با نام نوع (یعنی Type) مطابقت داده خواهد شد.
performance-implicit-cast-in-loop
نام این بررسی به performance-implicit-conversion-in-loop <> تغییر یافته است.
performance-implicit-conversion-in-loop
این هشدار در یک حلقهٔ مبتنی بر بازه با یک متغیر حلقه از نوع ارجاع ثابت رخ میدهد که در آن نوع متغیر با نوع بازگرداندهشده توسط تکرارکننده (iterator) مطابقت ندارد. این بدان معناست که یک تبدیل ضمنی (implicit conversion) رخ میدهد که میتواند بهعنوان مثال منجر به کپیهای عمیق (deep copies) پرهزینه شود.
مثال:
map<int, vector<string>> my_map;
for (const pair<int, vector<string>>& p : my_map) {}
// The iterator type is in fact pair<const int, vector<string>>, which means
// that the compiler added a conversion, resulting in a copy of the vectors.
سادهترین راهحل معمولاً استفاده از const auto& بهجای نوشتن دستی نوع است.
performance-inefficient-algorithm
در مورد استفادهٔ ناکارآمد از الگوریتمهای STL بر روی ظروف انجمنی (associative containers) هشدار میدهد.
ظروف انجمنی برخی از الگوریتمها را بهصورت متد پیادهسازی کردهاند که باید نسبت به الگوریتمهای موجود در هدر algorithm ترجیح داده شوند. این متدها میتوانند از ترتیب عناصر بهره ببرند.
std::set<int> s; auto it = std::find(s.begin(), s.end(), 43); // becomes auto it = s.find(43);
std::set<int> s; auto c = std::count(s.begin(), s.end(), 43); // becomes auto c = s.count(43);
performance-inefficient-string-concatenation
این بررسی در مورد سربار عملکردی ناشی از الحاق رشتهها با استفاده از operator+ هشدار میدهد، بهعنوان مثال:
std::string a("Foo"), b("Bar");
a = a + b;
بهجای این ساختار، باید از operator+= یا تابع عضو append() در کلاس std::string (std::basic_string) استفاده کنید. برای مثال:
std::string a("Foo"), b("Baz");
for (int i = 0; i < 20000; ++i) {
a = a + "Bar" + b;
}
میتواند به روشی بسیار کارآمدتر بازنویسی شود، مانند:
std::string a("Foo"), b("Baz");
for (int i = 0; i < 20000; ++i) {
a.append("Bar").append(b);
}
و این مورد نیز میتواند بازنویسی شود:
void f(const std::string&) {}
std::string a("Foo"), b("Baz");
void g() {
f(a + "Bar" + b);
}
به شیوهای کمی کارآمدتر، مانند:
void f(const std::string&) {}
std::string a("Foo"), b("Baz");
void g() {
f(std::string(a).append("Bar").append(b));
}
گزینهها
- StrictMode
- هنگامی که false باشد، بررسی تنها استفاده از رشتهها را در دستورهای while، for و for-range بررسی میکند. مقدار پیشفرض false است.
performance-inefficient-vector-operation
عملیات ناکارآمد بالقوه در std::vector (مانند push_back، emplace_back) را که ممکن است باعث تخصیص مجدد غیرضروری حافظه شوند، پیدا میکند.
همچنین میتواند فراخوانیهایی را که در یک حلقه بدون فراخوانی ()Reserve پیش از حلقه، عنصری را به یک فیلد repeated در protobuf اضافه میکنند، شناسایی کند. فراخوانی اولیه ()Reserve میتواند از تخصیص مجدد غیرضروری حافظه جلوگیری کند.
در حال حاضر، این بررسی تنها انواع حلقههای زیر را که بدنهای با یک عبارت تکی دارند تشخیص میدهد:
- •
- حلقههای for مبتنی بر شمارنده که از ۰ شروع میشوند:
std::vector<int> v;
for (int i = 0; i < n; ++i) {
v.push_back(n);
// This will trigger the warning since the push_back may cause multiple
// memory reallocations in v. This can be avoid by inserting a 'reserve(n)'
// statement before the for statement.
}
SomeProto p;
for (int i = 0; i < n; ++i) {
p.add_xxx(n);
// This will trigger the warning since the add_xxx may cause multiple memory
// reallocations. This can be avoid by inserting a
// 'p.mutable_xxx().Reserve(n)' statement before the for statement.
}
- •
- حلقههای For-range مانند for (range-declaration : range_expression)؛ نوع range_expression میتواند std::vector، std::array، std::deque، std::set، std::unordered_set، std::map یا std::unordered_set باشد:
std::vector<int> data;
std::vector<int> v;
for (auto element : data) {
v.push_back(element);
// This will trigger the warning since the 'push_back' may cause multiple
// memory reallocations in v. This can be avoid by inserting a
// 'reserve(data.size())' statement before the for statement.
}
گزینهها
- VectorLikeClasses
- فهرستی جداشده با نقطه-ویرگول از نامهای کلاسهای شبه-وکتور. بهطور پیشفرض تنها ::std::vector در نظر گرفته میشود.
- EnableProto
- هنگامی که true باشد، بررسی برای عملیات ناکارآمد روی فیلدهای repeated در proto نیز هشدار میدهد. در غیر این صورت، این بررسی تنها برای عملیات ناکارآمد وکتور هشدار صادر میکند. مقدار پیشفرض false است.
performance-move-const-arg
این بررسی در موارد زیر هشدار میدهد:
- اگر std::move() با یک آرگومان ثابت (const) فراخوانی شود،
- اگر std::move() با آرگومانی از یک نوع با قابلیت کپی ساده (trivially-copyable) فراخوانی شود،
- اگر نتیجهٔ std::move() به عنوان یک آرگومان ارجاع ثابت (const reference) ارسال شود.
در هر سه حالت، بررسی یک راهکار اصلاحی پیشنهاد میدهد که std::move() را حذف میکند.
در اینجا نمونههایی برای هر یک از این سه حالت آورده شده است:
const string s; return std::move(s); // Warning: std::move of the const variable has no effect int x; return std::move(x); // Warning: std::move of the variable of a trivially-copyable type has no effect void f(const string &s); string s; f(std::move(s)); // Warning: passing result of std::move as a const reference argument; no move will actually happen
گزینهها
- CheckTriviallyCopyableMove
- در صورت تنظیم روی true، تشخیص انواع با قابلیت کپی ساده که فاقد سازندهٔ انتقال (move constructor) هستند را فعال میکند. مقدار پیشفرض true است.
- CheckMoveToConstRef
- در صورت تنظیم روی true، تشخیص ارسال std::move() به عنوان یک آرگومان ارجاع ثابت (const reference) را فعال میکند. مقدار پیشفرض true است.
performance-move-constructor-init
عبارت "cert-oop11-cpp" به عنوان نام مستعار برای این بررسی به اینجا هدایت میشود.
این بررسی سازندههای انتقال تعریفشده توسط کاربر را که دارای ctor-initializer برای مقداردهی اولیه به یک عضو یا کلاس پایه از طریق یک سازندهٔ کپی بهجای سازندهٔ انتقال هستند، علامتگذاری میکند.
performance-no-automatic-move
متغیرهای محلی را که به دلیل const بودن نمیتوانند بهطور خودکار منتقل شوند (move)، پیدا میکند.
تحت شرایط خاص https://en.cppreference.com/w/cpp/language/return#automatic_move_from_local_variables_and_parameters، مقادیر محلی هنگام بازگشت از یک تابع بهطور خودکار به بیرون منتقل میشوند. یک اشتباه رایج، تعریف متغیرهای محلی lvalue به صورت const است که مانع از این انتقال میشود.
مثال [1] https://godbolt.org/z/x7SYYA:
StatusOr<std::vector<int>> Cool() {
std::vector<int> obj = ...;
return obj; // calls StatusOr::StatusOr(std::vector<int>&&)
}
StatusOr<std::vector<int>> NotCool() {
const std::vector<int> obj = ...;
return obj; // calls `StatusOr::StatusOr(const std::vector<int>&)`
}
نسخه اول (Cool) باید بر نسخه دوم (NotCool) ترجیح داده شود، چرا که از تخصیصها و کپیهای بالقوه بزرگ در حافظه جلوگیری میکند.
معناشناسی (Semantics)
در مثال بالا، StatusOr::StatusOr(T&&) معناشناسی یکسانی دارد تا زمانی که سازندههای رونوشت (copy) و انتقال (move) برای T معناشناسی یکسانی داشته باشند. توجه داشته باشید که هیچ تضمینی وجود ندارد که S::S(T&&) و S::S(const T&) برای هر تک S معناشناسی یکسانی داشته باشند، بنابراین ما اصلاحات خودکاری برای این بررسی ارائه نمیدهیم و هنگام اعمال تغییرات پیشنهادی باید قضاوت و دقت لازم به کار گرفته شود.
-Wreturn-std-move
حالت دیگری که در آن عمل انتقال نمیتواند رخ دهد به شرح زیر است:
StatusOr<std::vector<int>> Uncool() {
std::vector<int>&& obj = ...;
return obj; // calls `StatusOr::StatusOr(const std::vector<int>&)`
}
در این حالت، راهحل مورد توافقتر است: فقط عبارت return std::move(obj). این مورد توسط هشدار -Wreturn-std-move مدیریت میشود.
performance-no-int-to-ptr
هرگونه تبدیل نوع (cast) عدد صحیح به اشارهگر را عیبیابی و گزارش میکند.
در حالی که تبدیل یک اشارهگر (صحیح) به یک عدد صحیح واضح است - شما صرفاً مقدار عددی صحیح اشارهگر را دریافت میکنید، تبدیل یک عدد صحیح به یک اشارهگر (صحیح) بهطرز فریبندهای متفاوت است. با اینکه شما اشارهگری با همان مقدار عددی صحیح دریافت خواهید کرد، اما اگر آن مقدار صحیح را در اصل از طریق تبدیل اشارهگر به عدد صحیح به دست آورده باشید، اشارهگر جدید فاقد اطلاعات منشأ و پیشینه (provenance) اشارهگر اصلی خواهد بود.
بنابراین در حالی که تبدیل نوع اشارهگر (صحیح) به عدد صحیح عملاً no-op (بدون عملیات) بوده و برای بهینهساز شفاف (transparent) هستند، تبدیلهای عدد صحیح به اشارهگر (صحیح) شفاف نیستند، و ممکن است اطلاعات را از بهینهساز پنهان کنند.
با اینکه ممکن است این کار عمدی باشد، اما همیشه اینطور نیست. به عنوان مثال، بیایید نگاهی به روالی برای تراز کردن (align) اشارهگر به مضرب ۱۶ بیندازیم: پیادهسازی بدیهی و خام آن به این صورت است:
char* src(char* maybe_underbiased_ptr) {
uintptr_t maybe_underbiased_intptr = (uintptr_t)maybe_underbiased_ptr;
uintptr_t aligned_biased_intptr = maybe_underbiased_intptr + 15;
uintptr_t aligned_intptr = aligned_biased_intptr & (~15);
return (char*)aligned_intptr; // warning: avoid integer to pointer casts [performance-no-int-to-ptr]
}
این بررسی بهدرستی آن تبدیل نوع را عیبیابی و مشخص میکند.
اما هنگامی که پنهانسازی پیشینه هدف کد نباشد، بلکه تصادفی رخ داده باشد، میتوان این مثال را به شکل زیر و بدون استفاده از تبدیل عدد صحیح به اشارهگر بازنویسی کرد:
char*
tgt(char* maybe_underbiased_ptr) {
uintptr_t maybe_underbiased_intptr = (uintptr_t)maybe_underbiased_ptr;
uintptr_t aligned_biased_intptr = maybe_underbiased_intptr + 15;
uintptr_t aligned_intptr = aligned_biased_intptr & (~15);
uintptr_t bias = aligned_intptr - maybe_underbiased_intptr;
return maybe_underbiased_ptr + bias;
}
performance-noexcept-destructor
این بررسی مخربهای تعریفشده توسط کاربر را که با noexcept(expr) علامتگذاری شدهاند و در آنها expr به false ارزیابی میشود (اما خود یک لیترال false نیست) علامتگذاری میکند.
هنگامی که یک مخرب به عنوان noexcept علامتگذاری میشود، به کامپایلر اطمینان میدهد که در طول تخریب شیء هیچ استثنایی (exception) پرتاب نخواهد شد، که این امر به کامپایلر اجازه میدهد بهینهسازیهای خاصی مانند حذف کدهای مدیریت استثنا را انجام دهد.
performance-noexcept-move-constructor
این بررسی سازندههای انتقال و عملگرهای انتساب تعریفشده توسط کاربر را که با noexcept علامتگذاری نشدهاند یا با noexcept(expr) علامتگذاری شدهاند و در آنها expr به false ارزیابی میشود (اما خود یک لیترال false نیست)، پرچمگذاری میکند.
برای مثال، سازندههای انتقال تمامی انواع استفادهشده با کانتینرهای STL باید noexcept اعلام شوند. در غیر این صورت ممکن است STL به جای آن از سازندههای رونوشت استفاده کند. همین موضوع برای عملیات انتساب انتقال نیز صادق است.
performance-noexcept-swap
این بررسی توابع swap و iter_swap تعریفشده توسط کاربر را که با noexcept علامتگذاری نشدهاند یا با noexcept(expr) علامتگذاری شدهاند و در آنها expr به false ارزیابی میشود (اما خود یک لیترال false نیست)، پرچمگذاری میکند.
هنگامی که یک تابع swap یا iter_swap به عنوان noexcept علامتگذاری میشود، به کامپایلر اطمینان میدهد که در طول تعویض دو شیء هیچ استثنایی رخ نخواهد داد، که این امر به کامپایلر امکان میدهد بهینهسازیهای خاصی از جمله حذف کدهای مدیریت استثنا را اعمال کند.
performance-trivially-destructible
انواعی را مییابد که میتوانند با حذف اعلانهای خارج از بدنه (out-of-line) مخربهای پیشفرضشده (defaulted)، به صورت trivially-destructible درآیند.
struct A: TrivialType {
~A(); // Makes A non-trivially-destructible.
TrivialType trivial_fields;
};
A::~A() = default;
performance-type-promotion-in-math-fn
فراخوانی توابع کتابخانه ریاضی C (از math.h یا در C++، cmath) را با ارتقای ضمنی (implicit promotion) نوع float به double پیدا میکند.
برای مثال، در مورد ::sin(0.f) هشدار میدهد، زیرا پارامتر این تابع یک double است. احتمالاً منظور شما فراخوانی std::sin(0.f) (در C++) یا sinf(0.f) (در C) بوده است.
float a; asin(a); // becomes float a; std::asin(a);
performance-unnecessary-copy-initialization
اعلان متغیرهای محلی را پیدا میکند که با استفاده از سازنده رونوشت یک نوع non-trivially-copyable مقداردهی اولیه شدهاند، در حالی که دریافت یک ارجاع ثابت (const reference) کفایت میکرد.
این بررسی تنها زمانی اعمال میشود که جایگزینی رونوشت با یک ارجاع ثابت ایمن باشد. این حالت زمانی است که متغیر دارای مشخصه const باشد یا فقط به عنوان const استفاده شود، یعنی فقط متدها یا عملگرهای const روی آن فراخوانی شوند، یا به عنوان آرگومان مقداری یا ارجاع ثابت در سازندهها یا فراخوانیهای توابع به کار رود.
مثال:
const string& constReference();
void Function() {
// The warning will suggest making this a const reference.
const string UnnecessaryCopy = constReference();
}
struct Foo {
const string& name() const;
};
void Function(const Foo& foo) {
// The warning will suggest making this a const reference.
string UnnecessaryCopy1 = foo.name();
UnnecessaryCopy1.find("bar");
// The warning will suggest making this a const reference.
string UnnecessaryCopy2 = UnnecessaryCopy1;
UnnecessaryCopy2.find("bar");
}
گزینهها
- AllowedTypes
- یک فهرست جداشده با نقطه-ویرگول (semicolon) از نام انواع مجاز برای مقداردهی اولیه از طریق کپی کردن. عبارات باقاعده (regular expressions) پذیرفته میشوند؛ برای نمونه [Rr]ef(erence)?$ با تمام انواع دارای پسوند Ref، ref، Reference و reference تطابق مییابد. مقدار پیشفرض خالی است. اگر نامی در فهرست شامل توالی :: باشد، با نام نوع واجد شرایط (یعنی namespace::Type) مطابقت داده میشود؛ در غیر این صورت تنها با نام نوع (یعنی Type) تطبیق مییابد.
- ExcludedContainerTypes
- یک فهرست جداشده با نقطه-ویرگول از نام انواعی که متدهای آنها مجاز به بازگرداندن ارجاع const هستند که متغیر از روی آن کپی میشود. هنگامی که متغیری با کپی پرهزینه توسط مقدار بازگشتی از نوعی در این فهرست مقداردهی اولیه کپی میشود، این بررسی فعال نمیگردد. این گزینه میتواند برای مستثنی کردن انواعی استفاده شود که به عنوان const incorrect شناخته میشوند یا جاهایی که طول عمر یا تغییرناپذیری ارجاعهای بازگرداندهشده به تغییرات کانتینر وابسته نیست. یک نمونه، انواع view هستند که مالک دادههای زیربنایی نیستند. همانند AllowedTypes در بالا، عبارات باقاعده پذیرفته میشوند و وجود :: تعیین میکند که آیا نام نوعِ واجد شرایط مطابقت داده شود یا خیر.
محدودیتها
این بررسی تحلیل طول عمر (lifetime analysis) انجام نمیدهد و ممکن است جایگزینی کپیها را با ارجاعهای const پیشنهاد دهد که ممکن است معلق (dangling) شوند. در مواردی که ممکن است شیء ارجاعشده با عملیات بعدی نامعتبر شود، احتیاط کنید.
void consume(const S&);
void func(std::vector<S> &Vec) {
const auto It = Vec.begin();
const S Value(*It); // The warning will suggest making this a const reference.
Vec.erase(It); // Container modifications could invalidate references.
consume(Value); // Safe with copy, dangling reference otherwise.
}
performance-unnecessary-value-param
تعریف پارامترهای مقداری (value parameter) از انواع دارای کپی پرهزینه را که برای هر فراخوانی کپی میشوند اما ارسال آنها از طریق ارجاع const کفایت میکند، نشانهگذاری میکند.
این بررسی تنها روی پارامترهایی از انواع دارای کپی پرهزینه اعمال میشود؛ به این معنی که آنها بهطور ساده قابل کپی (trivially copyable) نبوده یا دارای سازنده کپی یا ویرانگر غیربدیهی (non-trivial) هستند.
برای اطمینان از اینکه جایگزینی پارامتر مقداری با ارجاع const ایمن است، ابتکار (heuristic) زیر به کار گرفته میشود:
- 1.
- پارامتر دارای توصیفکننده const باشد؛
- 2.
- پارامتر const نباشد، اما تنها متدها یا عملگرهای const روی آن فراخوانی شوند، یا به عنوان آرگومان مقداری یا ارجاع const در سازندهها یا فراخوانیهای تابع استفاده شود.
مثال:
void f(const string Value) {
// The warning will suggest making Value a reference.
}
void g(ExpensiveToCopy Value) {
// The warning will suggest making Value a const reference.
Value.ConstMethd();
ExpensiveToCopy Copy(Value);
}
اگر پارامتر const نباشد، تنها یک بار کپی یا تخصیص داده شود و به ترتیب دارای سازنده انتقال (move-constructor) یا عملگر انتساب انتقال (move-assignment) غیربدیهی باشد، بررسی پیشنهاد میدهد که آن را move کنید.
مثال:
void setValue(string Value) {
Field = Value;
}
تبدیل خواهد شد به:
#include <utility>
void setValue(string Value) {
Field = std::move(Value);
}
از آنجا که اصلاحیه خودکار (fix-it) نیاز به تغییر امضای تابع دارد، در صورتی که تابع در چندین واحد ترجمه (translation units) استفاده شده باشد یا برخی کدها به امضاهای تابع وابسته باشند، ممکن است ساخت برنامهها (builds) دچار شکست شود.
گزینهها
- IncludeStyle
- یک رشته که مشخص میکند کدام سبک include استفاده میشود: llvm یا google. پیشفرض llvm است.
- AllowedTypes
- یک فهرست جداشده با نقطه-ویرگول از نام انواع مجاز برای ارسال با مقدار (pass by value). عبارات باقاعده پذیرفته میشوند؛ برای نمونه [Rr]ef(erence)?$ با تمام انواع دارای پسوند Ref، ref، Reference و reference تطابق مییابد. مقدار پیشفرض خالی است. اگر نامی در فهرست شامل توالی :: باشد، با نام نوع واجد شرایط (یعنی namespace::Type) مطابقت داده میشود؛ در غیر این صورت تنها با نام نوع (یعنی Type) تطبیق مییابد.
- IgnoreCoroutines
- یک مقدار بولی که مشخص میکند آیا بررسی باید ارسال پارامترها با ارجاع را در کوروتینها پیشنهاد دهد یا خیر. ارسال پارامترها با ارجاع در کوروتینها ممکن است ایمن نباشد؛ لطفاً برای اطلاعات بیشتر cppcoreguidelines-avoid-reference-coroutine-parameters <> را ببینید. پیشفرض true است.
portability-avoid-pragma-once
موارد استفاده از #pragma once را یافته و جایگزینی آنها با گاردهای استاندارد include (شامل #ifndef/#define/#endif) را برای بهبود قابلیت حمل (portability) پیشنهاد میدهد.
دستور #pragma once علیرغم پشتیبانی گسترده توسط کامپایلرهای مدرن، یک افزونه غیر استاندارد است. تکیه بر آن میتواند در برخی محیطها به مشکلات سازگاری و انتقالپذیری منجر شود.
برخی کامپایلرهای قدیمیتر یا اختصاصی C/C++، بهویژه در سیستمهای تعبیهشده (embedded)، ممکن است بهطور کامل از #pragma once پشتیبانی نکنند.
همچنین ممکن است در پیکربندیهای خاص فایلسیستم، مانند درایوهای شبکه یا پیوندهای نمادین پیچیده با شکست مواجه شود و به مشکلات احتمالی در کامپایل بیانجامد.
فایل هدر زیر را در نظر بگیرید:
// my_header.h #pragma once // warning: avoid 'pragma once' directive; use include guards instead
هشدار پیشنهاد میدهد که از گاردهای include استفاده شود:
// my_header.h #ifndef PATH_TO_MY_HEADER_H // Good: use include guards. #define PATH_TO_MY_HEADER_H #endif // PATH_TO_MY_HEADER_H
portability-restrict-system-includes
بررسیهایی برای مجاز یا غیرمجاز کردن انتخابیِ یک فهرست قابلپیکربندی از هدرهای سیستم انجام میدهد.
به عنوان مثال:
برای اینکه تنها هدر zlib.h از سیستم مجاز باشد، گزینهها را روی -*,zlib.h تنظیم میکنید.
#include <curses.h> // Bad: disallowed system header. #include <openssl/ssl.h> // Bad: disallowed system header. #include <zlib.h> // Good: allowed system header. #include "src/myfile.h" // Good: non-system header always allowed.
برای اینکه همهچیز به جز zlib.h از سیستم مجاز باشد، گزینهها را روی *,-zlib.h تنظیم میکنید.
#include <curses.h> // Good: allowed system header. #include <openssl/ssl.h> // Good: allowed system header. #include <zlib.h> // Bad: disallowed system header. #include "src/myfile.h" // Good: non-system header always allowed.
از آنجا که گزینهها از الگویابی (globbing) پشتیبانی میکنند، میتوانید از نویسههای عام (wildcard) برای مجاز کردن گروههایی از هدرها استفاده کنید.
الگوی -*,openssl/*.h تمام هدرهای openssl را مجاز کرده اما بقیه هدرها را غیرمجاز میکند.
#include <curses.h> // Bad: disallowed system header. #include <openssl/ssl.h> // Good: allowed system header. #include <openssl/rsa.h> // Good: allowed system header. #include <zlib.h> // Bad: disallowed system header. #include "src/myfile.h" // Good: non-system header always allowed.
گزینهها
- Includes
- رشتهای شامل یک فهرست glob جداشده با کاما از نامفایلهای include مجاز. مشابه فهرست glob گزینه -checks برای اجرای خود clang-tidy، دو نویسه عام شامل * و - هستند که به ترتیب برای شامل کردن و مستثنی کردن الگوها به کار میروند. مقدار پیشفرض * است که تمام includeها را مجاز میکند.
portability-simd-intrinsics
فراخوانیهای ذاتی (intrinsics) مربوط به SIMD را یافته و جایگزینهای std::experimental::simd (P0214 https://wg21.link/p0214) را پیشنهاد میدهد.
اگر گزینه Suggest بر روی true تنظیم شده باشد، برای
_mm_add_epi32(a, b); // x86 vec_add(a, b); // Power
این بررسی یک جایگزین پیشنهاد میدهد: operator+ روی اشیاء std::experimental::simd.
در غیر این صورت، صرفاً هشدار میدهد که توابع ذاتی غیرقابلحمل (non-portable) هستند (و جایگزینهای P0214 https://wg21.link/p0214 وجود دارند).
معماریهای متعددی عملیاتهای SIMD را ارائه میدهند (مانند x86 SSE/AVX، Power AltiVec/VSX، ARM NEON). معمول است که کدهای SIMD پیادهسازیکننده یک الگوریتم یکسان، برای بهینهسازی در معماریها یا ریزمعماریهای مختلف، در چندین قطعه مبتنی بر معماری هدف (target-dispatching) نوشته شوند.
پیشنهاد استاندارد C++ با شناسه P0214 https://wg21.link/p0214 و گسترشهای آن بسیاری از عملیاتهای متداول SIMD را پوشش میدهند. با مهاجرت از توابع ذاتی وابسته به هدف به عملیاتهای P0214 https://wg21.link/p0214، میتوان کدهای SIMD را سادهسازی کرد و قطعات مختلف مربوط به معماریهای هدف گوناگون را یکپارچه ساخت.
برای آشنایی و چرایی استفاده از کتابخانه استاندارد دادهموازی، به P0214 https://wg21.link/p0214 مراجعه کنید.
گزینهها
- Suggest
- اگر این گزینه روی true تنظیم شود (پیشفرض false است)، این بررسی جایگزینهای P0214 https://wg21.link/p0214 را پیشنهاد میدهد؛ در غیر این صورت، تنها اشاره میکند که تابع ذاتی غیرقابلحمل است.
- Std
- فضای نامی (namespace) مورد استفاده برای پیشنهاد جایگزینهای P0214 https://wg21.link/p0214. در صورت عدم تعیین، برای -std=c++20 مقدار std:: و برای -std=c++11 مقدار std::experimental:: استفاده میشود.
portability-std-allocator-const
استفاده از std::vector<const T> (و کانتینرهای مشابه با عناصر const) را گزارش میدهد. این موارد در C++ استاندارد مجاز نیستند و معمولاً باید بهجای آن از std::vector<T> استفاده شود.
مطابق بخش [allocator.requirements.general] در استاندارد C++: عبارت «T هر نوع شیء بدون اصلاحکننده cv است»، بنابراین std::allocator<const T> تعریفنشده (undefined) است. بسیاری از کانتینرهای استاندارد بهطور پیشفرض از std::allocator استفاده میکنند و از این رو نمونهسازیهای const T آنها تعریفنشده است.
کتابخانه libc++ گزینه std::allocator<const T> را به عنوان یک افزونه تعریف میکند که در آینده حذف خواهد شد.
کتابخانههای libstdc++ و MSVC از std::allocator<const T> پشتیبانی نمیکنند:
// libstdc++ has a better diagnostic since https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48101 std::deque<const int> deque; // error: static assertion failed: std::deque must have a non-const, non-volatile value_type std::set<const int> set; // error: static assertion failed: std::set must have a non-const, non-volatile value_type std::vector<int* const> vector; // error: static assertion failed: std::vector must have a non-const, non-volatile value_type // MSVC // error C2338: static_assert failed: 'The C++ Standard forbids containers of const elements because allocator<const T> is ill-formed.'
پایگاههای کدی که فقط با libc++ کامپایل میشوند ممکن است با گذشت زمان چنین استفادههای تعریفنشدهای را انباشته کنند. این بررسی چنین کدهایی را پیدا میکند و از پسرفت کد در حین پاکسازی مداوم جلوگیری مینماید.
portability-template-virtual-member-function
مواردی را مییابد که در آنها یک تابع عضو مجازی نمونهسازینشده در یک کلاس الگو (template class) باعث ناسازگاری میان کامپایلرها میشود.
هنگام نمونهسازی از یک کلاس الگو، توابع عضو غیرمجازی نیازی به نمونهسازی ندارند مگر اینکه استفاده شوند. از طرف دیگر، نمونهسازی توابع عضو مجازی نامشخص (unspecified) است و به پیادهسازی کامپایلر بستگی دارد.
در قطعه کدهای زیر، تابع عضو مجازی توسط GCC و Clang نمونهسازی نمیشود، اما توسط MSVC نمونهسازی میگردد؛ بنابراین در حالی که قطعه کد توسط کامپایلرهای اول پذیرفته میشود، توسط کامپایلر دوم رد خواهد شد.
template<typename T>
struct CrossPlatformError {
virtual ~CrossPlatformError() = default;
static void used() {}
virtual void unused() {
T MSVCError = this;
};
};
int main() {
CrossPlatformError<int>::used();
return 0;
}
پروژههای چندسکویی (cross-platform) که نیاز به پشتیبانی از MSVC روی ویندوز دارند، ممکن است با خطاهای کامپایلر مواجه شوند زیرا برخی توابع عضو مجازی نمونهسازی میشوند که توسط کامپایلرهای دیگر در سایر پلتفرمها نمونهسازی نمیشوند. این بررسی چنین توابع عضو مجازی را مشخص میکند.
readability-ambiguous-smartptr-reset-call
فراخوانیهای بالقوه نادرست متد reset روی اشارهگرهای هوشمند را زمانی که نوع اشارهشده نیز دارای یک متد reset است، پیدا میکند. وجود متد reset در هر دو کلاس باعث میشود که هنگام قصد بازنشانی شیء زیرین، به اشتباه اشارهگر تهی (null) شود.
struct Resettable {
void reset() { /* Own reset logic */ }
};
auto ptr = std::make_unique<Resettable>();
ptr->reset(); // Calls underlying reset method
ptr.reset(); // Makes the pointer null
هر دو فراخوانی کدهای معتبر C++ هستند، اما مورد دوم ممکن است آن چیزی نباشد که برنامهنویس قصد داشته است، زیرا به جای بازنشانی وضعیت شیء اشارهشده، آن را از بین میبرد. رخ دادن چنین غلط تایپی بسیار ساده است زیرا تفاوت میان . و -> بسیار کوچک است.
رویکرد پیشنهادی این است که با استفاده از دسترسی به عضو یا انتساب مستقیم، منظور به طور واضح مشخص شود:
std::unique_ptr<Resettable> ptr = std::make_unique<Resettable>(); (*ptr).reset(); // Clearly calls underlying reset method ptr = nullptr; // Clearly makes the pointer null
اشارهگرهای هوشمند و کلاسهای پیشفرضی که در نظر گرفته میشوند عبارتند از std::unique_ptr، std::shared_ptr و boost::shared_ptr. برای مشخص کردن سایر اشارهگرهای هوشمند یا کلاسهای دیگر از گزینهٔ SmartPointers <#cmdoption-arg-SmartPointers> استفاده کنید.
نکته:
نکته:
گزینهها
- SmartPointers
- فهرستی جداشده با نقطه ویرگول (semicolon) از نامهای واجد شرایط کامل (fully qualified) کلاسهای اشارهگرهای هوشمند سفارشی. مقدار پیشفرض برابر است با ::std::unique_ptr;::std::shared_ptr; ::boost::shared_ptr.
readability-avoid-const-params-in-decls
بررسی میکند که آیا در اعلان یک تابع، پارامترهایی با const در سطح بالا (top level) وجود دارد یا خیر.
مقادیر const در اعلانها بر امضای (signature) تابع تأثیری ندارند، بنابراین نباید در آنجا قرار گیرند.
مثالها:
void f(const string); // Bad: const is top level. void f(const string&); // Good: const is not top level.
گزینهها
- IgnoreMacros
- اگر روی true تنظیم شود، این بررسی در داخل ماکروها هشدار نخواهد داد. پیشفرض برابر true است.
readability-avoid-nested-conditional-operator
موارد استفاده از عملگرهای شرطی تو در تو را در کد شناسایی میکند.
عملگرهای شرطی تو در تو، که با نام عملگرهای سهتایی (ternary) نیز شناخته میشوند، میتوانند موجب کاهش خوانایی و درک کد شوند. بنابراین باید به چند عبارت مجزا تقسیم شده و نتایج میانی در متغیرهای موقت ذخیره گردند.
مثالها:
int NestInConditional = (condition1 ? true1 : false1) ? true2 : false2; int NestInTrue = condition1 ? (condition2 ? true1 : false1) : false2; int NestInFalse = condition1 ? true1 : condition2 ? true2 : false1;
readability-avoid-return-with-void-value
دستورهای return با مقادیر void را که در توابعی با نوع نتیجهٔ void استفاده شدهاند پیدا میکند.
یک تابع با نوع بازگشتی void برای انجام یک وظیفه بدون تولید مقدار بازگشتی در نظر گرفته شده است. دستورهای return همراه با عبارتها میتوانند باعث سردرگمی شوند و ممکن است رفتار مورد نظر تابع را به اشتباه منتقل کنند.
مثال:
void g();
void f() {
// ...
return g();
}
در یک بدنهٔ طولانی تابع، عبارت return این تصور را ایجاد میکند که تابع مقداری را بازمیگرداند. با این حال، return g(); ترکیبی از دو دستور است که باید به این صورت نوشته شود:
g(); return;
تا مشخص شود که ابتدا g() فراخوانی شده و بلافاصله پس از آن، تابع (بدون بازگرداندن هیچ چیزی) بازمیگردد.
در زبان C، در صورت فعال بودن حالت -Wpedantic، همین موضوع توسط کامپایلر تشخیص داده میشود.
گزینهها
- IgnoreMacros
- مقدار false مشخص میکند که دستورات return بسطیافته از ماکروها بررسی نشوند. مقدار پیشفرض true است.
- StrictMode
- مقدار false مشخص میکند که یک دستور return مستقیم، در صورتی که تنها دستور خارج از یک بلوک باشد (مانند if (cond) return g();)، از تحلیل مستثنی شود. مقدار پیشفرض true است.
readability-avoid-unconditional-preprocessor-if
بلوکهای کدی را که با تحلیل شرایط #if (مانند #if 0 و #if 1 و غیره) در دستورات پیشپردازنده بهطور دائم فعال یا غیرفعال شدهاند، پیدا میکند.
#if 0
// some disabled code
#endif
#if 1
// some enabled code that can be disabled manually
#endif
دستورات پیشپردازنده بدون شرط، مانند #if 0 برای کدهای غیرفعال و #if 1 برای کدهای فعال، میتوانند به ترتیب منجر به کد مرده (dead code) و کد همیشه فعال شوند. کد مرده میتواند درک پایهکد را دشوارتر کند، به خوانایی آسیب بزند، و ممکن است نشانهای از قابلیتهای ناتمام یا ویژگیهای رهاشده باشد. این امر میتواند موجب مشکلات نگهداری، سردرگمی برای توسعهدهندگان آینده، و مشکلات احتمالی در کامپایل شود.
به عنوان یک راهحل برای هر دو مورد، استفاده از ماکروها یا تعاریف پیشپردازنده مانند #ifdef DEBUGGING_ENABLED را جهت کنترل فعال یا غیرفعال بودن کد مد نظر قرار دهید. این رویکرد هنگام کار با بخشهای مختلف پایهکد، هماهنگی و انعطافپذیری بهتری فراهم میکند. متناوباً، میتوانید کل کد را با استفاده از کامنتهای بلوکی /* */ کامنت کرده و یک یادداشت مانند @todo برای مشخص کردن اقدامات آینده اضافه کنید.
readability-braces-around-statements
بررسی google-readability-braces-around-statements به عنوان یک نام مستعار برای این بررسی به اینجا هدایت میشود.
بررسی میکند که بدنه دستورات if و حلقهها (for، do while و while) داخل آکولاد قرار گرفته باشند.
قبل:
if (condition) statement;
بعد:
if (condition) {
statement;
}
گزینهها
- ShortStatementLines
- حداقل
تعداد
خطوطی را که
دستور باید
داشته باشد
تا این
بررسی را
فعال کند،
تعریف
میکند.
تعداد خطوط از پایان شرط یا کلمه کلیدی اولیه (do/else) تا آخرین خط دستور داخلی شمارش میشود. مقدار پیشفرض 0 به این معنی است که آکولاد به تمام دستوراتی که در حال حاضر فاقد آن هستند، اضافه خواهد شد.
readability-const-return-type
توابعی را که نوع بازگشتی آنها دارای توصیفکننده const است بررسی کرده و حذف کلمه کلیدی const را توصیه میکند. چنین استفادهای از const معمولاً زائد است و میتواند مانع از بهینهسازیهای ارزشمند کامپایلر شود. (هنوز) trailing return typeها را اصلاح نمیکند.
مثالها:
const int foo(); const Clazz foo(); Clazz *const foo();
توجه داشته باشید که این موضوع صرفاً برای توصیفکنندههای سطح بالا (top-level) اعمال میشود و اشارهگرها یا ارجاعها به مقادیر const را شامل نمیشود. به عنوان مثال، این موارد بلامانع هستند:
const int* foo(); const int& foo(); const Clazz* foo();
گزینهها
- IgnoreMacros
- در صورت تنظیم روی true، این بررسی هشداری درون ماکروها صادر نخواهد کرد. پیشفرض true است.
readability-container-contains
موارد استفاده از container.count() و container.find() == container.end() را که باید با فراخوانی متد container.contains() جایگزین شوند، پیدا میکند.
اینکه آیا یک عنصر داخل کانتینر وجود دارد یا خیر باید به جای count/find با contains بررسی شود، زیرا contains هدف را با وضوح بیشتری منتقل میکند. علاوه بر این، برای کانتینرهایی که اجازه ورودیهای چندگانه برای هر کلید را میدهند (multimap، multiset و...)، contains از count بهینهتر است، زیرا count باید عملیات اضافی غیرضروری انجام دهد.
مثالها:
| عبارت اولیه | نتیجه |
| myMap.find(x) == myMap.end() | !myMap.contains(x) |
| myMap.find(x) != myMap.end() | myMap.contains(x) |
| myStr.find(x) != std::string::npos | myStr.contains(x) |
| if (myMap.count(x)) | if (myMap.contains(x)) |
| bool exists = myMap.count(x) | bool exists = myMap.contains(x) |
| bool exists = myMap.count(x) > 0 | bool exists = myMap.contains(x) |
| bool exists = myMap.count(x) >= 1 | bool exists = myMap.contains(x) |
| bool missing = myMap.count(x) == 0 | bool missing = !myMap.contains(x) |
این بررسی برای هر کلاسی که دارای متد contains باشد اعمال خواهد شد، که بهویژه شامل std::set، std::unordered_set، std::map و std::unordered_map از C++20 به بعد، و std::string و std::string_view از C++23 به بعد میشود.
readability-container-data-pointer
مواردی را مییابد که کد میتواند به جای آدرس عنصر در اندیس 0 یک کانتینر، از data() استفاده کند. این الگو معمولاً برای دستیابی به اشارهگری به دادههای پشتیبان یک کانتینر به کار میرود. کانتینرهای std::vector و std::string یک متد دسترسی data() را برای بازیابی اشارهگر داده فراهم میکنند که استفاده از آن ارجحیت دارد.
این امر همچنین اطمینان حاصل میکند که در صورت خالی بودن کانتینر، دسترسی به اشارهگر داده موجب دسترسی نادرست به حافظه نشود.
گزینهها
- IgnoredContainers
- فهرستی از عبارات باقاعده (regexp) برای کانتینرهایی که با نقطه-ویرگول (semicolon) جدا شدهاند و این بررسی بر روی آنها اعمال نخواهد شد. مقدار پیشفرض یک رشته خالی است.
readability-container-size-empty
بررسی میکند که آیا میتوان فراخوانی متد size()/length() را با فراخوانی empty() جایگزین کرد یا خیر.
خالی بودن یک کانتینر باید به جای استفاده از متد size()/length() با متد empty() بررسی شود. استفاده از empty() قصد و نیت کد را به شکل واضحتری نشان میدهد. علاوه بر این، برخی از کانتینرها (برای مثال، std::forward_list) ممکن است متد empty() را پیادهسازی کرده باشند اما متد size() یا length() را نداشته باشند. استفاده از empty() در هر جا که ممکن است، تغییر کانتینر به نوعی دیگر را در آینده آسانتر میسازد.
اگر یک کانتینر دارای متدهای empty() و size() یا length() مطابق با امضاهای زیر باشد، این بررسی هشدار صادر میکند:
size_type size() const; size_type length() const; bool empty() const;
نوع size_type میتواند هر نوع عدد صحیحی باشد.
گزینهها
- ExcludedComparisonTypes
- فهرستی از عبارات باقاعده (regexp) جداشده با نقطه-ویرگول مطابق با نام کلاسهایی که بررسی برای آنها، مقایسه اشیاء با اشیاء پیشفرض ساختهشده از همان نوع را نادیده میگیرد. اگر کلاسی در اینجا فهرست شود، بررسی پیشنهاد استفاده از empty() را به جای چنین مقایسههایی برای اشیاء آن کلاس ارائه نخواهد داد. مقدار پیشفرض عبارت است از: ::std::array.
readability-convert-member-functions-to-static
توابع عضو غیر ایستا (non-static) را که میتوانند static شوند، به این دلیل که از this استفاده نمیکنند، پیدا میکند.
پس از اعمال تغییرات پیشنهادی توسط بررسی، اجرای مجدد آن ممکن است فرصتهای بیشتری را برای علامتگذاری توابع عضو به عنوان static بیابد.
پس از تبدیل یک تابع عضو به static، ممکن است بخواهید بررسی readability-static-accessed-through-instance <> را اجرا کنید تا فراخوانیهایی مانند Instance.method() با Class::method() جایگزین شوند.
readability-delete-null-pointer
دستورات if را که در آنها وجود یک اشارهگر بررسی شده و سپس آن اشارهگر حذف میشود، بررسی میکند. این بررسی غیرضروری است زیرا حذف یک اشارهگر تهی (null pointer) هیچ اثری ندارد.
int *p; if (p) delete p;
readability-duplicate-include
موارد include تکراری را جستجو کرده و آنها را حذف میکند. این بررسی فهرستی از فایلهای include شده را نگه میدارد و به دنبال موارد تکراری میگردد. در صورت تعریف یا لغو تعریف یک ماکرو، فهرست فایلهای include شده پاک میشود.
مثالها:
#include <memory> #include <vector> #include <memory>
تبدیل میشود به:
#include <memory> #include <vector>
به دلیل وجود تعاریف ماکرو در میان دستورات، این کد بدون تغییر باقی میماند:
#undef NDEBUG #include "assertion.h" // ...code with assertions enabled #define NDEBUG #include "assertion.h" // ...code with assertions disabled
گزینهها
- IgnoredFilesList
- فهرستی از عبارات باقاعده یا نام فایلها، جداشده با نقطه-ویرگول، که مجاز هستند چندین بار بدون ایجاد عیبیابی (diagnostics) گنجانده (include) شوند. تطبیق بر روی نام متنی include انجام میشود. مقدار پیشفرض یک رشته خالی است.
readability-else-after-return
استانداردهای کدنویسی LLVM در https://llvm.org/docs/CodingStandards.html توصیه میکند تا حد امکان و در جاهایی که درک کد را آسانتر میکند، تورفتگی (indentation) کاهش یابد. خروج زودهنگام (Early exit) یکی از راهحلهای پیشنهادی برای این موضوع است. لطفاً پس از مواردی که جریان کنترل را قطع میکنند - مانند return، break، continue، throw - از else یا else if استفاده نکنید.
قطعه کد زیر نحوه عملکرد این بررسی را نشان میدهد. این قطعه کد:
void foo(int Value) {
int Local = 0;
for (int i = 0; i < 42; i++) {
if (Value == 1) {
return;
} else {
Local++;
}
if (Value == 2)
continue;
else
Local++;
if (Value == 3) {
throw 42;
} else {
Local++;
}
}
}
تبدیل خواهد شد به:
void foo(int Value) {
int Local = 0;
for (int i = 0; i < 42; i++) {
if (Value == 1) {
return;
}
Local++;
if (Value == 2)
continue;
Local++;
if (Value == 3) {
throw 42;
}
Local++;
}
}
گزینهها
- WarnOnUnfixable
- هنگامی که true باشد، برای مواردی که بررسی قادر به ارائه یک Fix-It نیست، یک هشدار صادر میکند. این حالت میتواند با اعلانهای درون شاخهٔ else رخ دهد که در صورت حذف شاخهٔ else طول عمر طولانیتری پیدا میکردند. مقدار پیشفرض true است.
- WarnOnConditionVariables
- هنگامی که true باشد، بررسی تلاش خواهد کرد متغیری را که درون شرط دستور if تعریف شده و در شاخهٔ else استفاده میشود، با بازتعریف آن درست پیش از دستور if بازنویسی کند. این کار تنها در صورتی قابل انجام است که دستور if آخرین دستور در حوزهٔ (scope) والد خود باشد. مقدار پیشفرض true است.
نام مستعار LLVM
یک نام مستعار برای این بررسی با نام llvm-else-after-return وجود دارد. در آن نسخه گزینههای WarnOnUnfixable و WarnOnConditionVariables هر دو به طور پیشفرض روی false تنظیم شدهاند.
این بررسی به اجرای این توصیه از استانداردهای کدنویسی LLVM کمک میکند: https://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return.
readability-enum-initial-value
سبک سازگاری را برای مقداردهی اولیه شمارندهها (enumerators) اعمال میکند، که شامل سه سبک است: هیچکدام، فقط اولی، یا مقداردهی اولیه صریح همه موارد.
سبک متناقض و سختگیری در تعریف مقدار اولیه شمارندهها، در صورتی که شمارش (enumeration) با شمارندههای جدیدی که نمایش عدد صحیح خود را به صورت ضمنی دریافت میکنند گسترش یابد، ممکن است مشکلاتی ایجاد کند.
سه حالت زیر پذیرفته میشوند:
- 1.
- هیچیک از شمارندهها به صورت صریح مقداردهی اولیه نشده باشند.
- 2.
- دقیقاً اولین شمارنده به صورت صریح مقداردهی اولیه شده باشد.
- 3.
- تمام شمارندهها به صورت صریح مقداردهی اولیه شده باشند.
enum A { // (1) Valid, none of enumerators are initialized.
a0,
a1,
a2,
};
enum B { // (2) Valid, the first enumerator is initialized.
b0 = 0,
b1,
b2,
};
enum C { // (3) Valid, all of enumerators are initialized.
c0 = 0,
c1 = 1,
c2 = 2,
};
enum D { // Invalid, d1 is not explicitly initialized!
d0 = 0,
d1,
d2 = 2,
};
enum E { // Invalid, e1, e3, and e5 are not explicitly initialized.
e0 = 0,
e1,
e2 = 2,
e3, // Dangerous, as the numeric values of e3 and e5 are both 3, and this is not explicitly visible in the code!
e4 = 2,
e5,
};
این بررسی با توصیه استانداردهای کدنویسی CERT C تحت عنوان INT09-C. Ensure enumeration constants map to unique values مطابقت دارد: https://wiki.sei.cmu.edu/confluence/display/c/INT09-C.+Ensure+enumeration+constants+map+to+unique+values.
بررسی cert-int09-c به عنوان یک نام مستعار از این بررسی به اینجا هدایت میشود.
گزینهها
- AllowExplicitZeroFirstInitialValue
- در صورت تنظیم روی false، اولین شمارنده نباید به صورت صریح با مقدار لیتال 0 مقداردهی شود. پیشفرض true است.
enum F {
f0 = 0, // Not allowed if AllowExplicitZeroFirstInitialValue is false.
f1,
f2,
};
- AllowExplicitSequentialInitialValues
- در صورت تنظیم روی false، مقداردهی اولیه صریح با مقادیر متوالی مجاز نیست. پیشفرض true است.
enum G {
g0 = 1, // Not allowed if AllowExplicitSequentialInitialValues is false.
g1 = 2,
g2 = 3,
readability-function-cognitive-complexity
معیار پیچیدگی شناختی (Cognitive Complexity) توابع را بررسی میکند.
این معیار بر اساس مشخصات COGNITIVE COMPLEXITY ارائه شده توسط SonarSource به نشانی https://www.sonarsource.com/docs/CognitiveComplexity.pdf نسخه ۱.۲ (۱۹ آوریل ۲۰۱۷) پیادهسازی شده است.
گزینهها
- Threshold
- توابعی را که پیچیدگی شناختی آنها از این عدد فراتر میرود پرچمگذاری میکند. پیشفرض 25 است.
- DescribeBasicIncrements
- در صورت تنظیم روی true، برای هر تابعی که از آستانه پیچیدگی فراتر رود، بررسی برای هر قطعه کد (حلقه، دستور if، و غیره) که در این پیچیدگی نقش دارد، گزارشهای عیبیابی تکمیلی صادر میکند. همچنین به مثالهای زیر مراجعه کنید. پیشفرض true است.
- IgnoreMacros
- در صورت تنظیم روی true، بررسی از کدهای درون ماکروها صرفنظر میکند. توجه داشته باشید که هرگونه آرگومان ماکرو نیز نادیده گرفته میشود، حتی اگر باید در شمارش پیچیدگی لحاظ شوند. از آنجا که ممکن است این رفتار در آینده تغییر کند، سازگاری رو به جلوی این گزینه تضمین نمیشود. پیشفرض false است.
مؤلفههای اصلی
سه مؤلفه اصلی برای معیار Cognitive Complexity وجود دارد:
افزایش (Increment)
ساختارهای زیر معیار Cognitive Complexity تابع را افزایش میدهند (به میزان 1 واحد):
- •
- عملگرهای شرطی:
- if()
- else if()
- else
- cond ? true : false
- switch()
- حلقهها:
- for()
- دستور for() مبتنی بر بازه در C++11
- while()
- do while()
- catch ()
- goto LABEL، goto *(&&LABEL))،
- توالیهای عملگرهای منطقی دودویی:
- boolean1 || boolean2
- boolean1 && boolean2
سطح تو در تو بودن (Nesting level)
اگرچه سطح تو در تو بودن به خودی خود معیار Cognitive Complexity تابع را تغییر نمیدهد، اما پیگیری شده و توسط مؤلفه اصلی بعدی (سوم) استفاده میشود. ساختارهای زیر سطح تو در تو بودن را افزایش میدهند (به میزان 1 واحد):
- •
- عملگرهای شرطی:
- if()
- else if()
- else
- cond ? true : false
- switch()
- حلقهها:
- for()
- دستور for() مبتنی بر بازه در C++11
- while()
- do while()
- catch ()
- توابع تو در تو:
- لامبدا در C++11
- یک class تو در تو
- یک struct تو در تو
- عبارت دستوری GNU (GNU statement expression)
- اعلان بلوک در Apple (Apple Block Declaration)
افزایش تو در تو (Nesting increment)
اینجا جایی است که مؤلفه اصلی قبلی، یعنی سطح تو در تو بودن، اهمیت پیدا میکند. ساختارهای زیر معیار Cognitive Complexity تابع را به اندازه سطح فعلی تو در تو بودن افزایش میدهند:
- •
- عملگرهای شرطی:
- if()
- cond ? true : false
- switch()
- حلقهها:
- for()
- دستور for() مبتنی بر بازه در C++11
- while()
- do while()
- •
- catch ()
مثالها
سادهترین حالت. این تابع دارای Cognitive Complexity برابر با 0 است.
void function0() {}
مثالی کمی بهتر. این تابع دارای Cognitive Complexity برابر با 1 است.
int function1(bool var) {
if(var) // +1, nesting level +1
return 42;
return 0;
}
مثال کامل. این تابع دارای Cognitive Complexity برابر با 3 است.
int function3(bool var1, bool var2) {
if(var1) { // +1, nesting level +1
if(var2) // +2 (1 + current nesting level of 1), nesting level +1
return 42;
}
return 0;
}
در مثال آخر، اگر گزینه Threshold روی 2 یا کمتر تنظیم شده باشد، این بررسی به function3 اخطار میدهد. اگر گزینه DescribeBasicIncrements روی true تنظیم شده باشد، علاوه بر این به دو عبارت if به همراه مقداری که به پیچیدگی تابع و سطح تودرتویی (nesting level) فعلی اضافه میکنند، اشاره و اخطار خواهد داد.
محدودیتها
- این معیار با دو استثنای قابل توجه پیادهسازی شده است:
- شرطهای پیشپردازنده (#ifdef، #if، #elif، #else، #endif) محاسبه نمیشوند.
- هر متد در یک چرخه بازگشتی (recursion cycle) به حساب نمیآید. این مورد نمیتواند بهطور کامل پیادهسازی شود، زیرا به تحلیل بینواحدی ترجمه (cross-translational-unit analysis) نیاز است، که در حال حاضر در clang-tidy امکانپذیر نیست.
readability-function-size
گزینه google-readability-function-size به عنوان یک نام مستعار برای این بررسی به اینجا هدایت میشود.
توابع بزرگ را بر اساس معیارهای مختلف بررسی میکند.
گزینهها
- LineThreshold
- توابعی را که تعداد خطوط آنها از این مقدار فراتر رود، نشانهگذاری میکند. مقدار پیشفرض none است (نادیده گرفتن تعداد خطوط).
- StatementThreshold
- توابعی را که تعداد دستورات (statements) آنها از این مقدار بیشتر باشد، نشانهگذاری میکند. این مقدار ممکن است برای کدهایی که شدیداً به ماکروها وابستهاند، تفاوت چشمگیری با تعداد خطوط داشته باشد. مقدار پیشفرض 800 است.
- BranchThreshold
- توابعی را که تعداد دستورات کنترلی آنها از این مقدار فراتر رود، نشانهگذاری میکند. مقدار پیشفرض none است (نادیده گرفتن تعداد شاخهها).
- ParameterThreshold
- توابعی را که تعداد پارامترهای آنها از یک مقدار مشخص فراتر رود، نشانهگذاری میکند. مقدار پیشفرض none است (نادیده گرفتن تعداد پارامترها).
- NestingThreshold
- دستورات مرکبی را که پس از NestingThreshold سطح تودرتویی بعدی را ایجاد میکنند، نشانهگذاری میکند. این مقدار ممکن است برای کدهای وابسته به ماکرو، تفاوت چشمگیری با مقدار مورد انتظار داشته باشد. مقدار پیشفرض none است (نادیده گرفتن سطح تودرتویی).
- VariableThreshold
- توابعی را که تعداد متغیرهای تعریفشده در بدنه آنها از این مقدار فراتر رود، نشانهگذاری میکند. مقدار پیشفرض none است (نادیده گرفتن تعداد متغیرها). لطفاً توجه داشته باشید که پارامترهای تابع و متغیرهای تعریفشده در عبارات لامبدا، GNU Statement Expressions، و توابع درونخطی کلاسهای تودرتو شمرده نمیشوند.
- CountMemberInitAsStmt
- در صورت تنظیم روی true، مقداردهندههای اولیه اعضای کلاس در سازندهها به عنوان دستور (statement) شمرده میشوند. پیشفرض true است.
readability-identifier-length
این بررسی متغیرها و پارامترهای تابعی را که طول نام آنها بسیار کوتاه است، پیدا میکند. طول نام مورد نظر قابل پیکربندی است.
موارد خاصی برای شمارندههای حلقه و متغیرهای مربوط به استثناها پشتیبانی میشوند.
گزینهها
گزینههای زیر در ادامه شرح داده شدهاند:
- MinimumVariableNameLength، IgnoredVariableNames
- MinimumParameterNameLength، IgnoredParameterNames <#cmdoption-arg-IgnoredParameterNames>
- MinimumLoopCounterNameLength، IgnoredLoopCounterNames
- MinimumExceptionNameLength، IgnoredExceptionVariableNames
- MinimumVariableNameLength
- انتظار میرود تمام متغیرها (بهجز شمارنده حلقه، نامهای استثنا و پارامترهای تابع) حداقل طولی برابر با MinimumVariableNameLength داشته باشند (پیشفرض 3 است). تنظیم آن روی 0 یا 1 این بررسی را به کلی غیرفعال میکند.
int i = 42; // warns that 'i' is too short
این بررسی در حالت کلی هیچ پیشنهادی برای اصلاح ارائه نمیدهد زیرا نام متغیرها دارای بار معنایی هستند.
- IgnoredVariableNames
- یک عبارت باقاعده (regular expression) برای نامهای متغیری که باید نادیده گرفته شوند مشخص میکند. مقدار پیشفرض خالی است، بنابراین هیچ نامی نادیده گرفته نمیشود.
- MinimumParameterNameLength
- انتظار میرود تمام نامهای پارامترهای تابع حداقل طولی برابر با MinimumParameterNameLength داشته باشند (پیشفرض 3 است). تنظیم آن روی 0 یا 1 این بررسی را به کلی غیرفعال میکند.
int doubler(int x) // warns that x is too short
{
return 2 * x;
}
این بررسی در حالت کلی هیچ پیشنهادی برای اصلاح ارائه نمیدهد زیرا نام متغیرها دارای بار معنایی هستند.
- IgnoredParameterNames
- یک عبارت باقاعده برای پارامترهایی که باید نادیده گرفته شوند مشخص میکند. مقدار پیشفرض بنا به دلایل تاریخی ^[n]$ است.
- MinimumLoopCounterNameLength
- انتظار میرود متغیرهای شمارنده حلقه حداقل طولی برابر با MinimumLoopCounterNameLength نویسه داشته باشند (پیشفرض 2 است). تنظیم آن روی 0 یا 1 بررسی را بهطور کامل غیرفعال میکند.
// This warns that 'q' is too short.
for (int q = 0; q < size; ++ q) {
// ...
}
- IgnoredLoopCounterNames
- یک عبارت باقاعده برای نامهای شمارندهای که باید نادیده گرفته شوند مشخص میکند. مقدار پیشفرض ^[ijk_]$ است؛ سه نماد اول بنا به دلایل تاریخی و نماد آخر به دلیل کاربرد مکرر آن به عنوان مقدار "مهم نیست" (don't care)، بهویژه در ابزارهایی مانند Google Benchmark.
// This does not warn by default, for historical reasons.
for (int i = 0; i < size; ++ i) {
// ...
}
- MinimumExceptionNameLength
- متغیرهای بند استثنا (exception clause) باید حداقل طولی برابر با MinimumExceptionNameLength داشته باشند (مقدار پیشفرض 2 است). تنظیم آن روی 0 یا 1 این بررسی را به طور کامل غیرفعال میکند.
try {
// ...
}
// This warns that 'e' is too short.
catch (const std::exception& x) {
// ...
}
- IgnoredExceptionVariableNames
- یک عبارت باقاعده (regular expression) برای نام متغیرهای استثنایی که باید نادیده گرفته شوند مشخص میکند. مقدار پیشفرض به دلایل تاریخی عمدتاً ^[e]$ است.
try {
// ...
}
// This does not warn by default, for historical reasons.
catch (const std::exception& e) {
// ...
}
عدم تطابق شیوهی نامگذاری شناسهها (identifiers) را بررسی میکند.
این بررسی تلاش میکند تا راهنماهای کدنویسی را روی نامگذاری شناسهها اعمال کند. این بررسی از یکی از انواع حالتهای حروف (casing) زیر پشتیبانی کرده و در صورت تشخیص عدم تطابق، سعی میکند آن را از یک حالت به حالت دیگر تبدیل کند.
انواع حالتهای حروف عبارتند از:
- lower_case
- UPPER_CASE
- camelBack
- CamelCase
- camel_Snake_Back
- Camel_Snake_Case
- aNy_CasE
- Leading_upper_snake_case
همچنین از یک پیشوند و پسوند ثابت پشتیبانی میکند که صرفنظر از وضعیت حروف، به ابتدا یا انتهای شناسهها اضافه خواهند شد.
گزینههای پیکربندی متعددی در دسترس هستند تا بتوان قواعد مختلفی را برای انواع مختلف شناسهها تعریف کرد. به طور کلی، اگر مورد خاصی پیکربندی نشده باشد، قواعد به یک قاعده عمومیتر بازمیگردند (fallback).
نامگذاری توابع مجازی (virtual methods) در جایی که در کلاس پایه تعریف شدهاند گزارش میشود، اما در جایی که بازنویسی (override) شدهاند گزارش نمیشود، چرا که نمیتوان آن را به صورت محلی در آنجا اصلاح کرد. این موضوع برای الگوهای شبهبازنویسی مانند CRTP نیز صدق میکند.
قاعده Leading_upper_snake_case یک شیوه نامگذاری است که در آن کلمه اول با حروف بزرگ شروع شده و سپس کلمه یا کلمات با حروف کوچک به وسیله خط زیرین '_' جدا میشوند. به عنوان مثال: Cap_snake_case، Cobra_case، Foo_bar_baz و Master_copy_8gb.
نشانهگذاری مجارستانی (Hungarian notation) را میتوان با استفاده از تنظیمات مختلف HungarianPrefix سفارشیسازی کرد. گزینهها و مقادیر متناظر آنها عبارتند از:
- Off - تنظیم پیشفرض
- On - مثال: int iVariable
- LowerCase - مثال: int i_Variable
- CamelCase - مثال: int IVariable
این بررسی تنها شیوهی نامگذاری را روی آن دسته از شناسههایی اعمال میکند که پیکربندی شده باشند؛ بنابراین، یک پیکربندی خالی عملاً آن را غیرفعال میکند. از گزینه DefaultCase میتوان برای اعمال شیوه نامگذاری بر روی تمام انواع شناسهها استفاده کرد، و سپس در صورت تمایل، آن را برای انواع خاصی که حالت دیگری برایشان مد نظر است، بازنویسی نمود.
به عنوان مثال با استفاده از مقادیر:
- مقدار DefaultCase برابر با lower_case
- مقدار MacroDefinitionCase برابر با UPPER_CASE
- مقدار TemplateParameterCase برابر با CamelCase
نامها را به صورت زیر شناسایی کرده و تغییر میدهد:
قبل:
#define macroDefinition template <typename typenameParameter> int functionDeclaration(typenameParameter paramVal, int paramCount);
بعد:
#define MACRO_DEFINITION template <typename TypenameParameter> int function_declarations(TypenameParameter param_val, int param_count);
خلاصه گزینهها
گزینههای موجود در زیر خلاصه شدهاند:
گزینههای عمومی
- AggressiveDependentMemberLookup
- CheckAnonFieldInParent
- GetConfigPerFile
- IgnoreMainLikeFunctions
گزینههای اختصاصی
- DefaultCase, DefaultPrefix, DefaultSuffix, DefaultIgnoredRegexp, DefaultHungarianPrefix
- AbstractClassCase, AbstractClassPrefix, AbstractClassSuffix, AbstractClassIgnoredRegexp, AbstractClassHungarianPrefix
- ClassCase, ClassPrefix, ClassSuffix, ClassIgnoredRegexp, ClassHungarianPrefix
- ClassConstexprCase, ClassConstexprPrefix, ClassConstexprSuffix, ClassConstexprIgnoredRegexp, ClassConstexprHungarianPrefix
- ClassConstantCase, ClassConstantPrefix, ClassConstantSuffix, ClassConstantIgnoredRegexp, ClassConstantHungarianPrefix
- ClassMemberCase, ClassMemberPrefix, ClassMemberSuffix, ClassMemberIgnoredRegexp, ClassMemberHungarianPrefix
- ClassMethodCase, ClassMethodPrefix, ClassMethodSuffix, ClassMethodIgnoredRegexp
- ConceptCase, ConceptPrefix, ConceptSuffix, ConceptIgnoredRegexp
- ConstantCase, ConstantPrefix, ConstantSuffix, ConstantIgnoredRegexp, ConstantHungarianPrefix
- ConstantMemberCase, ConstantMemberPrefix, ConstantMemberSuffix, ConstantMemberIgnoredRegexp, ConstantMemberHungarianPrefix
- ConstantParameterCase, ConstantParameterPrefix, ConstantParameterSuffix, ConstantParameterIgnoredRegexp, ConstantParameterHungarianPrefix
- ConstantPointerParameterCase, ConstantPointerParameterPrefix, ConstantPointerParameterSuffix, ConstantPointerParameterIgnoredRegexp, ConstantPointerParameterHungarianPrefix
- ConstexprFunctionCase, ConstexprFunctionPrefix, ConstexprFunctionSuffix, ConstexprFunctionIgnoredRegexp
- ConstexprMethodCase, ConstexprMethodPrefix, ConstexprMethodSuffix, ConstexprMethodIgnoredRegexp
- ConstexprVariableCase, ConstexprVariablePrefix, ConstexprVariableSuffix, ConstexprVariableIgnoredRegexp, ConstexprVariableHungarianPrefix
- EnumCase, EnumPrefix, EnumSuffix, EnumIgnoredRegexp
- EnumConstantCase, EnumConstantPrefix, EnumConstantSuffix, EnumConstantIgnoredRegexp, EnumConstantHungarianPrefix
- FunctionCase, FunctionPrefix, FunctionSuffix, FunctionIgnoredRegexp
- GlobalConstexprVariableCase, GlobalConstexprVariablePrefix, GlobalConstexprVariableSuffix, GlobalConstexprVariableIgnoredRegexp, GlobalConstexprVariableHungarianPrefix
- GlobalConstantCase, GlobalConstantPrefix, GlobalConstantSuffix, GlobalConstantIgnoredRegexp, GlobalConstantHungarianPrefix
- GlobalConstantPointerCase, GlobalConstantPointerPrefix, GlobalConstantPointerSuffix, GlobalConstantPointerIgnoredRegexp, GlobalConstantPointerHungarianPrefix
- GlobalFunctionCase, GlobalFunctionPrefix, GlobalFunctionSuffix, GlobalFunctionIgnoredRegexp
- GlobalPointerCase, GlobalPointerPrefix, GlobalPointerSuffix, GlobalPointerIgnoredRegexp, GlobalPointerHungarianPrefix
- GlobalVariableCase, GlobalVariablePrefix, GlobalVariableSuffix, GlobalVariableIgnoredRegexp, GlobalVariableHungarianPrefix
- InlineNamespaceCase, InlineNamespacePrefix, InlineNamespaceSuffix, InlineNamespaceIgnoredRegexp
- LocalConstexprVariableCase, LocalConstexprVariablePrefix, LocalConstexprVariableSuffix, LocalConstexprVariableIgnoredRegexp, LocalConstexprVariableHungarianPrefix
- LocalConstantCase, LocalConstantPrefix, LocalConstantSuffix, LocalConstantIgnoredRegexp, LocalConstantHungarianPrefix
- LocalConstantPointerCase, LocalConstantPointerPrefix, LocalConstantPointerSuffix, LocalConstantPointerIgnoredRegexp, LocalConstantPointerHungarianPrefix
- LocalPointerCase, LocalPointerPrefix, LocalPointerSuffix, LocalPointerIgnoredRegexp, LocalPointerHungarianPrefix
- LocalVariableCase, LocalVariablePrefix, LocalVariableSuffix, LocalVariableIgnoredRegexp, LocalVariableHungarianPrefix
- MacroDefinitionCase, MacroDefinitionPrefix, MacroDefinitionSuffix, MacroDefinitionIgnoredRegexp
- MemberCase, MemberPrefix, MemberSuffix, MemberIgnoredRegexp, MemberHungarianPrefix
- MethodCase, MethodPrefix, MethodSuffix, MethodIgnoredRegexp
- NamespaceCase, NamespacePrefix, NamespaceSuffix, NamespaceIgnoredRegexp
- ParameterCase, ParameterPrefix, ParameterSuffix, ParameterIgnoredRegexp, ParameterHungarianPrefix
- ParameterPackCase, ParameterPackPrefix, ParameterPackSuffix, ParameterPackIgnoredRegexp
- PointerParameterCase, PointerParameterPrefix, PointerParameterSuffix, PointerParameterIgnoredRegexp, PointerParameterHungarianPrefix
- PrivateMemberCase, PrivateMemberPrefix, PrivateMemberSuffix, PrivateMemberIgnoredRegexp, PrivateMemberHungarianPrefix
- PrivateMethodCase, PrivateMethodPrefix, PrivateMethodSuffix, PrivateMethodIgnoredRegexp
- ProtectedMemberCase, ProtectedMemberPrefix, ProtectedMemberSuffix, ProtectedMemberIgnoredRegexp, ProtectedMemberHungarianPrefix
- ProtectedMethodCase, ProtectedMethodPrefix, ProtectedMethodSuffix, ProtectedMethodIgnoredRegexp
- PublicMemberCase, PublicMemberPrefix, PublicMemberSuffix, PublicMemberIgnoredRegexp, PublicMemberHungarianPrefix
- PublicMethodCase, PublicMethodPrefix, PublicMethodSuffix, PublicMethodIgnoredRegexp
- ScopedEnumConstantCase, ScopedEnumConstantPrefix, ScopedEnumConstantSuffix, ScopedEnumConstantIgnoredRegexp
- StaticConstexprVariableCase, StaticConstexprVariablePrefix, StaticConstexprVariableSuffix, StaticConstexprVariableIgnoredRegexp, StaticConstexprVariableHungarianPrefix
- StaticConstantCase, StaticConstantPrefix, StaticConstantSuffix, StaticConstantIgnoredRegexp, StaticConstantHungarianPrefix
- StaticVariableCase, StaticVariablePrefix, StaticVariableSuffix, StaticVariableIgnoredRegexp, StaticVariableHungarianPrefix
- StructCase, StructPrefix, StructSuffix, StructIgnoredRegexp
- TemplateParameterCase, TemplateParameterPrefix, TemplateParameterSuffix, TemplateParameterIgnoredRegexp
- TemplateTemplateParameterCase, TemplateTemplateParameterPrefix, TemplateTemplateParameterSuffix, TemplateTemplateParameterIgnoredRegexp
- TypeAliasCase, TypeAliasPrefix, TypeAliasSuffix, TypeAliasIgnoredRegexp
- TypedefCase, TypedefPrefix, TypedefSuffix, TypedefIgnoredRegexp
- TypeTemplateParameterCase, TypeTemplateParameterPrefix, TypeTemplateParameterSuffix, TypeTemplateParameterIgnoredRegexp
- UnionCase, UnionPrefix, UnionSuffix, UnionIgnoredRegexp
- ValueTemplateParameterCase, ValueTemplateParameterPrefix, ValueTemplateParameterSuffix, ValueTemplateParameterIgnoredRegexp
- VariableCase, VariablePrefix, VariableSuffix, VariableIgnoredRegexp, VariableHungarianPrefix
- VirtualMethodCase, VirtualMethodPrefix, VirtualMethodSuffix, VirtualMethodIgnoredRegexp
شرح گزینهها
توضیحات دقیق هر یک از گزینهها در زیر ارائه شده است:
- DefaultCase
- در صورت تعیین، این بررسی اطمینان حاصل میکند که تمام نامها بهطور پیشفرض از شیوهی نگارش حروف انتخابشده پیروی میکنند.
- DefaultPrefix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که به تمام نامها بهطور پیشفرض پیشوندی با مقدار دادهشده اضافه شود (صرفنظر از شیوهی نگارش حروف).
- DefaultIgnoredRegexp
- بررسیهای نامگذاری شناسهها بهطور پیشفرض برای تمامی نامهایی که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- DefaultSuffix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که به تمام نامها بهطور پیشفرض پسوندی با مقدار دادهشده اضافه شود (صرفنظر از شیوهی نگارش حروف).
- DefaultHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نشانهگذاری مجارستانی (Hungarian notation) باشد.
- AbstractClassCase
- در صورت تعیین، این بررسی اطمینان حاصل میکند که نام کلاسهای انتزاعی از شیوهی نگارش حروف انتخابشده پیروی میکنند.
- AbstractClassPrefix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که به نام کلاسهای انتزاعی پیشوندی با مقدار دادهشده اضافه شود (صرفنظر از شیوهی نگارش حروف).
- AbstractClassIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام کلاسهای انتزاعی که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- AbstractClassSuffix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که به نام کلاسهای انتزاعی پسوندی با مقدار دادهشده اضافه شود (صرفنظر از شیوهی نگارش حروف).
- AbstractClassHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نشانهگذاری مجارستانی باشد.
به عنوان مثال با استفاده از مقادیر:
- AbstractClassCase با مقدار lower_case
- AbstractClassPrefix با مقدار pre_
- AbstractClassSuffix با مقدار _post
- AbstractClassHungarianPrefix با مقدار On
نام کلاسهای انتزاعی را شناسایی کرده و/یا به شکل زیر تبدیل میکند:
قبل:
class ABSTRACT_CLASS {
public:
ABSTRACT_CLASS();
};
بعد:
class pre_abstract_class_post {
public:
pre_abstract_class_post();
};
- AggressiveDependentMemberLookup
- هنگامی که روی true تنظیم شود، بررسی در کلاسهای پایهٔ وابسته (dependent base classes) به دنبال ارجاعات اعضای وابستهای که نیاز به تغییر دارند خواهد گشت. این امر میتواند منجر به بروز خطا در تخصیصهای قالب (template specializations) شود، بنابراین مقدار پیشفرض false است.
برای مثال با استفاده از مقادیر:
- •
- مقدار ClassMemberCase برابر با lower_case
قبل از اعمال:
template <typename T>
struct Base {
T BadNamedMember;
};
template <typename T>
struct Derived : Base<T> {
void reset() {
this->BadNamedMember = 0;
}
};
بعد از اعمال در صورتی که AggressiveDependentMemberLookup برابر با false باشد:
template <typename T>
struct Base {
T bad_named_member;
};
template <typename T>
struct Derived : Base<T> {
void reset() {
this->BadNamedMember = 0;
}
};
بعد از اعمال در صورتی که AggressiveDependentMemberLookup برابر با true باشد:
template <typename T>
struct Base {
T bad_named_member;
};
template <typename T>
struct Derived : Base<T> {
void reset() {
this->bad_named_member = 0;
}
};
- CheckAnonFieldInParent
- هنگامی که روی true تنظیم شود، فیلدها در رکوردهای بینام (مانند unionها و structهای بینام) به منظور بررسی نامگذاری، به جای اعضای عمومی رکورد بینام، به عنوان نامهایی در دامنهٔ دربرگیرنده (enclosing scope) در نظر گرفته میشوند.
برای مثال:
class Foo {
private:
union {
int iv_;
float fv_;
};
};
اگر CheckAnonFieldInParent برابر با false باشد، ممکن است هشدارهایی دریافت کنید مبنی بر این که iv_ و fv_ با نامهای اعضای عمومی (public) سازگار نیستند، زیرا iv_ و fv_ اعضای عمومی union بینام هستند. هنگامی که CheckAnonFieldInParent برابر با true باشد، iv_ و fv_ به منظور بررسی نامگذاری، به عنوان اعضای دادهای خصوصی Foo در نظر گرفته میشوند و بنابراین هیچ هشداری صادر نخواهد شد.
- ClassCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای کلاس از نحوهٔ نوشتن حروف (casing) انتخابشده پیروی کنند.
- ClassPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای کلاس پیشوندی با مقدار دادهشده اضافه کنند (صرفنظر از بزرگی یا کوچکی حروف).
- ClassIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای کلاسی که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- ClassSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای کلاس پسوندی با مقدار دادهشده اضافه کنند (صرفنظر از بزرگی یا کوچکی حروف).
- ClassHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسهٔ اعلانشده بر اساس نوع دادهٔ اعلانشده دارای پیشوند نشانهگذاری مجارستانی (Hungarian notation) باشد.
برای مثال با استفاده از مقادیر:
- مقدار ClassCase برابر با lower_case
- مقدار ClassPrefix برابر با pre_
- مقدار ClassSuffix برابر با _post
- مقدار ClassHungarianPrefix برابر با On
نامهای کلاس را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل از اعمال:
class FOO {
public:
FOO();
~FOO();
};
بعد از اعمال:
class pre_foo_post {
public:
pre_foo_post();
~pre_foo_post();
};
- ClassConstexprCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای constexpr کلاس از نحوهٔ نوشتن حروف انتخابشده پیروی کنند.
- ClassConstexprPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای constexpr کلاس پیشوندی با مقدار دادهشده اضافه کنند (صرفنظر از بزرگی یا کوچکی حروف).
- ClassConstexprIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای constexpr کلاسی که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- ClassConstexprSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای constexpr کلاس پسوندی با مقدار دادهشده اضافه کنند (صرفنظر از بزرگی یا کوچکی حروف).
- ClassConstexprHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسهٔ اعلانشده بر اساس نوع دادهٔ اعلانشده دارای یک پیشوند نشانهگذاری مجارستانی باشد.
برای مثال با استفاده از مقادیر:
- مقدار ClassConstexprCase برابر با lower_case
- مقدار ClassConstexprPrefix برابر با pre_
- مقدار ClassConstexprSuffix برابر با _post
- مقدار ClassConstexprHungarianPrefix برابر با On
نام متغیرهای constexpr کلاس را بهصورت زیر شناسایی و/یا تبدیل میکند:
قبل:
class FOO {
public:
static constexpr int CLASS_CONSTEXPR;
};
بعد:
class FOO {
public:
static const int pre_class_constexpr_post;
};
- ClassConstantCase
- در صورت تعیین، این بررسی اطمینان حاصل میکند که نامهای ثوابت کلاس با شیوهی نگارش حروف (casing) انتخابشده مطابقت داشته باشند.
- ClassConstantPrefix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار مشخصشده (صرف نظر از وضعیت بزرگ و کوچکی حروف) به نامهای ثوابت کلاس اضافه شود.
- ClassConstantIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای ثوابت کلاسی که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- ClassConstantSuffix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که پسوندی با مقدار مشخصشده (صرف نظر از وضعیت بزرگ و کوچکی حروف) به نامهای ثوابت کلاس اضافه شود.
- ClassConstantHungarianPrefix
- در صورت فعال بودن، این بررسی تضمین میکند که شناسه اعلانشده بر اساس نوع داده اعلانشدهی آن، دارای پیشوند نشانهگذاری مجارستانی (Hungarian notation) باشد.
برای مثال با استفاده از مقادیر:
- مقدار ClassConstantCase برابر با lower_case
- مقدار ClassConstantPrefix برابر با pre_
- مقدار ClassConstantSuffix برابر با _post
- مقدار ClassConstantHungarianPrefix برابر با On
نام ثوابت کلاس را بهصورت زیر شناسایی و/یا تبدیل میکند:
قبل:
class FOO {
public:
static const int CLASS_CONSTANT;
};
بعد:
class FOO {
public:
static const int pre_class_constant_post;
};
- ClassMemberCase
- در صورت تعیین، این بررسی اطمینان حاصل میکند که نام اعضای کلاس با شیوهی نگارش حروف (casing) انتخابشده مطابقت داشته باشند.
- ClassMemberPrefix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار مشخصشده (صرف نظر از وضعیت بزرگ و کوچکی حروف) به نام اعضای کلاس اضافه شود.
- ClassMemberIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام اعضای کلاسی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ClassMemberSuffix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که پسوندی با مقدار مشخصشده (صرف نظر از وضعیت بزرگ و کوچکی حروف) به نام اعضای کلاس اضافه شود.
- ClassMemberHungarianPrefix
- در صورت فعال بودن، این بررسی تضمین میکند که شناسه اعلانشده بر اساس نوع داده اعلانشدهی آن، دارای پیشوند نشانهگذاری مجارستانی باشد.
برای مثال با استفاده از مقادیر:
- مقدار ClassMemberCase برابر با lower_case
- مقدار ClassMemberPrefix برابر با pre_
- مقدار ClassMemberSuffix برابر با _post
- مقدار ClassMemberHungarianPrefix برابر با On
نام اعضای کلاس را بهصورت زیر شناسایی و/یا تبدیل میکند:
قبل:
class FOO {
public:
static int CLASS_CONSTANT;
};
بعد:
class FOO {
public:
static int pre_class_constant_post;
};
- ClassMethodCase
- در صورت تعیین، این بررسی اطمینان حاصل میکند که نام متدهای کلاس با شیوهی نگارش حروف (casing) انتخابشده مطابقت داشته باشند.
- ClassMethodPrefix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار مشخصشده (صرف نظر از وضعیت بزرگ و کوچکی حروف) به نام متدهای کلاس اضافه شود.
- ClassMethodIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام متدهای کلاسی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ClassMethodSuffix
- در صورت تعیین، این بررسی اطمینان حاصل میکند که پسوندی با مقدار مشخصشده (صرف نظر از وضعیت بزرگ و کوچکی حروف) به نام متدهای کلاس اضافه شود.
برای مثال با استفاده از مقادیر:
- مقدار ClassMethodCase برابر با lower_case
- مقدار ClassMethodPrefix برابر با pre_
- مقدار ClassMethodSuffix برابر با _post
نام متدهای کلاس را بهصورت زیر شناسایی و/یا تبدیل میکند:
قبل:
class FOO {
public:
int CLASS_MEMBER();
};
بعد:
class FOO {
public:
int pre_class_member_post();
};
- ConceptCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای concept با حالت بزرگی/کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- ConceptPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای concept افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConceptIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای concept که با این عبارت منظم (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- ConceptSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای concept افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
برای نمونه با استفاده از مقادیر:
- مقدار ConceptCase برابر با CamelCase
- مقدار ConceptPrefix برابر با Pre
- مقدار ConceptSuffix برابر با Post
نامهای concept را به این صورت شناسایی و/یا تبدیل میکند:
پیش از اعمال:
template<typename T> concept my_concept = requires (T t) { {t++}; };
پس از اعمال:
template<typename T> concept PreMyConceptPost = requires (T t) { {t++}; };
- ConstantCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای ثابتها با حالت بزرگی/کوچکی حروف انتخابشده مطابقت داشته باشند.
- ConstantPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای ثابتها افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConstantIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای ثابتهایی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- ConstantSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای ثابتها افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConstantHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسهٔ اعلانشده بر اساس نوع اعلانشده دارای یک پیشوند با نشانهگذاری مجارستانی (Hungarian notation) باشد.
برای نمونه با استفاده از مقادیر:
- مقدار ConstantCase برابر با lower_case
- مقدار ConstantPrefix برابر با pre_
- مقدار ConstantSuffix برابر با _post
- مقدار ConstantHungarianPrefix برابر با On
نامهای ثابتها را به این صورت شناسایی و/یا تبدیل میکند:
پیش از اعمال:
void function() { unsigned const MyConst_array[] = {1, 2, 3}; }
پس از اعمال:
void function() { unsigned const pre_myconst_array_post[] = {1, 2, 3}; }
- ConstantMemberCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای اعضای ثابت با حالت بزرگی/کوچکی حروف انتخابشده مطابقت داشته باشند.
- ConstantMemberPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای اعضای ثابت افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConstantMemberIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای اعضای ثابتی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- ConstantMemberSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای اعضای ثابت افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConstantMemberHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسهٔ اعلانشده بر اساس نوع اعلانشده دارای یک پیشوند با نشانهگذاری مجارستانی باشد.
برای نمونه با استفاده از مقادیر:
- مقدار ConstantMemberCase برابر با lower_case
- مقدار ConstantMemberPrefix برابر با pre_
- مقدار ConstantMemberSuffix برابر با _post
- مقدار ConstantMemberHungarianPrefix برابر با On
نامهای اعضای ثابت را به این صورت شناسایی و/یا تبدیل میکند:
پیش از اعمال:
class Foo {
char const MY_ConstMember_string[4] = "123";
}
پس از اعمال:
class Foo {
char const pre_my_constmember_string_post[4] = "123";
}
- ConstantParameterCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای پارامترهای ثابت با حالت بزرگی/کوچکی حروف انتخابشده مطابقت داشته باشند.
- ConstantParameterPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای پارامترهای ثابت افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConstantParameterIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای پارامترهای ثابتی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- ConstantParameterSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای پارامترهای ثابت افزوده شود (بدون در نظر گرفتن بزرگی و کوچکی حروف).
- ConstantParameterHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نمادگذاری مجارستانی (Hungarian notation) باشد.
برای مثال با استفاده از مقادیر زیر:
- مقدار ConstantParameterCase برابر با lower_case
- مقدار ConstantParameterPrefix برابر با pre_
- مقدار ConstantParameterSuffix برابر با _post
- مقدار ConstantParameterHungarianPrefix برابر با On
نامهای پارامتر ثابت را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void GLOBAL_FUNCTION(int PARAMETER_1, int const CONST_parameter);
بعد:
void GLOBAL_FUNCTION(int PARAMETER_1, int const pre_const_parameter_post);
- ConstantPointerParameterCase
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای پارامتر اشارهگر ثابت با حالت حروف (casing) انتخابشده مطابقت داشته باشند.
- ConstantPointerParameterPrefix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای پارامتر اشارهگر ثابت، پیشوندی با مقدار دادهشده (صرفنظر از بزرگی و کوچکی حروف) اضافه کنند.
- ConstantPointerParameterIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای پارامتر اشارهگر ثابت که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ConstantPointerParameterSuffix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای پارامتر اشارهگر ثابت، پسوندی با مقدار دادهشده (صرفنظر از بزرگی و کوچکی حروف) اضافه کنند.
- ConstantPointerParameterHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نمادگذاری مجارستانی باشد.
برای مثال با استفاده از مقادیر زیر:
- مقدار ConstantPointerParameterCase برابر با lower_case
- مقدار ConstantPointerParameterPrefix برابر با pre_
- مقدار ConstantPointerParameterSuffix برابر با _post
- مقدار ConstantPointerParameterHungarianPrefix برابر با On
نامهای پارامتر اشارهگر ثابت را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void GLOBAL_FUNCTION(int const *CONST_parameter);
بعد:
void GLOBAL_FUNCTION(int const *pre_const_parameter_post);
- ConstexprFunctionCase
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای تابع constexpr با حالت حروف انتخابشده مطابقت داشته باشند.
- ConstexprFunctionPrefix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای تابع constexpr، پیشوندی با مقدار دادهشده (صرفنظر از بزرگی و کوچکی حروف) اضافه کنند.
- ConstexprFunctionIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای تابع constexpr که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ConstexprFunctionSuffix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای تابع constexpr، پسوندی با مقدار دادهشده (صرفنظر از بزرگی و کوچکی حروف) اضافه کنند.
برای مثال با استفاده از مقادیر زیر:
- مقدار ConstexprFunctionCase برابر با lower_case
- مقدار ConstexprFunctionPrefix برابر با pre_
- مقدار ConstexprFunctionSuffix برابر با _post
نامهای تابع constexpr را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
constexpr int CE_function() { return 3; }
بعد:
constexpr int pre_ce_function_post() { return 3; }
- ConstexprMethodCase
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای متد constexpr با حالت حروف انتخابشده مطابقت داشته باشند.
- ConstexprMethodPrefix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای متد constexpr، پیشوندی با مقدار دادهشده (صرفنظر از بزرگی و کوچکی حروف) اضافه کنند.
- ConstexprMethodIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای متد constexpr که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ConstexprMethodSuffix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای متد constexpr، پسوندی با مقدار دادهشده (صرفنظر از بزرگی و کوچکی حروف) اضافه کنند.
برای مثال با استفاده از مقادیر زیر:
- مقدار ConstexprMethodCase برابر با lower_case
- مقدار ConstexprMethodPrefix برابر با pre_
- مقدار ConstexprMethodSuffix برابر با _post
نامهای متد constexpr را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
class Foo {
public:
constexpr int CST_expr_Method() { return 2; }
}
بعد:
class Foo {
public:
constexpr int pre_cst_expr_method_post() { return 2; }
}
- ConstexprVariableCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متغیرهای constexpr از حالت حروف (casing) انتخابشده پیروی کنند.
- ConstexprVariablePrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نام متغیرهای constexpr اضافه شود (بدون در نظر گرفتن حالت حروف).
- ConstexprVariableIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام متغیرهای constexpr که با این عبارت منظم (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- ConstexprVariableSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نام متغیرهای constexpr اضافه شود (بدون در نظر گرفتن حالت حروف).
- ConstexprVariableHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی (Hungarian notation) باشد.
برای مثال با استفاده از مقادیر:
- مقدار ConstexprVariableCase برابر با lower_case
- مقدار ConstexprVariablePrefix برابر با pre_
- مقدار ConstexprVariableSuffix برابر با _post
- مقدار ConstexprVariableHungarianPrefix برابر با On
نام متغیرهای constexpr را بهصورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
constexpr int ConstExpr_variable = MyConstant;
بعد:
constexpr int pre_constexpr_variable_post = MyConstant;
- EnumCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای شمارشی (enumeration) از حالت حروف انتخابشده پیروی کنند.
- EnumPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای شمارشی اضافه شود (بدون در نظر گرفتن حالت حروف).
- EnumIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای شمارشی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- EnumSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای شمارشی اضافه شود (بدون در نظر گرفتن حالت حروف).
برای مثال با استفاده از مقادیر:
- مقدار EnumCase برابر با lower_case
- مقدار EnumPrefix برابر با pre_
- مقدار EnumSuffix برابر با _post
نامهای شمارشی را بهصورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
enum FOO { One, Two, Three };
بعد:
enum pre_foo_post { One, Two, Three };
- EnumConstantCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام ثوابت شمارشی از حالت حروف انتخابشده پیروی کنند.
- EnumConstantPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نام ثوابت شمارشی اضافه شود (بدون در نظر گرفتن حالت حروف).
- EnumConstantIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام ثوابت شمارشی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- EnumConstantSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نام ثوابت شمارشی اضافه شود (بدون در نظر گرفتن حالت حروف).
- EnumConstantHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی باشد.
برای مثال با استفاده از مقادیر:
- مقدار EnumConstantCase برابر با lower_case
- مقدار EnumConstantPrefix برابر با pre_
- مقدار EnumConstantSuffix برابر با _post
- مقدار EnumConstantHungarianPrefix برابر با On
نام ثوابت شمارشی را بهصورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
enum FOO { One, Two, Three };
بعد:
enum FOO { pre_One_post, pre_Two_post, pre_Three_post };
- FunctionCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام توابع از حالت حروف انتخابشده پیروی کنند.
- FunctionPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نام توابع اضافه شود (بدون در نظر گرفتن حالت حروف).
- FunctionIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام توابعی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- FunctionSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نام توابع اضافه شود (بدون در نظر گرفتن حالت حروف).
برای مثال با استفاده از مقادیر:
- مقدار FunctionCase برابر با lower_case
- مقدار FunctionPrefix برابر با pre_
- مقدار FunctionSuffix برابر با _post
نامهای توابع را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
char MY_Function_string();
بعد:
char pre_my_function_string_post();
- GetConfigPerFile
- هنگامی که true باشد، بررسی به دنبال پیکربندی مربوط به محل اعلان شناسه میگردد. برای مواردی که فایلهای سرآیند ضمیمهشده (included) از شیوهای متفاوت استفاده میکنند مفید است. مقدار پیشفرض true است.
- GlobalConstexprVariableCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متغیرهای سراسری constexpr با شیوه بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشد.
- GlobalConstexprVariablePrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند مشخصشده (بدون در نظر گرفتن casing) به نام متغیرهای سراسری constexpr اضافه شود.
- GlobalConstexprVariableIgnoredRegexp
- بررسی نامگذاری شناسهها برای نام متغیرهای سراسری constexpr که با این عبارت باقاعده (regular expression) مطابقت داشته باشند اعمال نخواهد شد.
- GlobalConstexprVariableSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند مشخصشده (بدون در نظر گرفتن casing) به نام متغیرهای سراسری constexpr اضافه شود.
- GlobalConstexprVariableHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده دارای یک پیشوند با نشانهگذاری مجارستانی (Hungarian notation) بر اساس نوع اعلانشده باشد.
برای مثال با استفاده از مقادیر:
- مقدار GlobalConstexprVariableCase برابر با lower_case
- مقدار GlobalConstexprVariablePrefix برابر با pre_
- مقدار GlobalConstexprVariableSuffix برابر با _post
- مقدار GlobalConstexprVariableHungarianPrefix برابر با On
نامهای متغیرهای سراسری constexpr را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
constexpr unsigned ImportantValue = 69;
بعد:
constexpr unsigned pre_important_value_post = 69;
- GlobalConstantCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای ثوابت سراسری با شیوه بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشد.
- GlobalConstantPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند مشخصشده (بدون در نظر گرفتن casing) به نامهای ثوابت سراسری اضافه شود.
- GlobalConstantIgnoredRegexp
- بررسی نامگذاری شناسهها برای نامهای ثوابت سراسری که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- GlobalConstantSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند مشخصشده (بدون در نظر گرفتن casing) به نامهای ثوابت سراسری اضافه شود.
- GlobalConstantHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده دارای یک پیشوند با نشانهگذاری مجارستانی بر اساس نوع اعلانشده باشد.
برای مثال با استفاده از مقادیر:
- مقدار GlobalConstantCase برابر با lower_case
- مقدار GlobalConstantPrefix برابر با pre_
- مقدار GlobalConstantSuffix برابر با _post
- مقدار GlobalConstantHungarianPrefix برابر با On
نامهای ثوابت سراسری را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
unsigned const MyConstGlobal_array[] = {1, 2, 3};
بعد:
unsigned const pre_myconstglobal_array_post[] = {1, 2, 3};
- GlobalConstantPointerCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای اشارهگرهای ثابت سراسری با شیوه بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشد.
- GlobalConstantPointerPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند مشخصشده (بدون در نظر گرفتن casing) به نامهای اشارهگرهای ثابت سراسری اضافه شود.
- GlobalConstantPointerIgnoredRegexp
- بررسی نامگذاری شناسهها برای نامهای اشارهگرهای ثابت سراسری که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- GlobalConstantPointerSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند مشخصشده (بدون در نظر گرفتن casing) به نامهای اشارهگرهای ثابت سراسری اضافه شود.
- GlobalConstantPointerHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده دارای یک پیشوند با نشانهگذاری مجارستانی بر اساس نوع اعلانشده باشد.
برای مثال با استفاده از مقادیر:
- مقدار GlobalConstantPointerCase برابر با lower_case
- مقدار GlobalConstantPointerPrefix برابر با pre_
- مقدار GlobalConstantPointerSuffix برابر با _post
- مقدار GlobalConstantPointerHungarianPrefix برابر با On
نامهای اشارهگرهای ثابت سراسری را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
int *const MyConstantGlobalPointer = nullptr;
بعد:
int *const pre_myconstantglobalpointer_post = nullptr;
- GlobalFunctionCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای توابع سراسری با بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- GlobalFunctionPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای توابع سراسری اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
- GlobalFunctionIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای توابع سراسری که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- GlobalFunctionSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای توابع سراسری اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
به عنوان مثال با استفاده از مقادیر:
- مقدار GlobalFunctionCase برابر با lower_case
- مقدار GlobalFunctionPrefix برابر با pre_
- مقدار GlobalFunctionSuffix برابر با _post
نامهای توابع سراسری را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void GLOBAL_FUNCTION(int PARAMETER_1, int const CONST_parameter);
بعد:
void pre_global_function_post(int PARAMETER_1, int const CONST_parameter);
- GlobalPointerCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای اشارهگرهای سراسری با بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- GlobalPointerPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای اشارهگرهای سراسری اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
- GlobalPointerIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای اشارهگرهای سراسری که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- GlobalPointerSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای اشارهگرهای سراسری اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
- GlobalPointerHungarianPrefix
- در صورت فعال بودن، این بررسی اطمینان حاصل میکند که شناسه تعریفشده دارای یک پیشوند نمادگذاری مجارستانی (Hungarian notation) بر اساس نوع تعریفشده باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار GlobalPointerCase برابر با lower_case
- مقدار GlobalPointerPrefix برابر با pre_
- مقدار GlobalPointerSuffix برابر با _post
- مقدار GlobalPointerHungarianPrefix برابر با On
نامهای اشارهگرهای سراسری را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
int *GLOBAL3;
بعد:
int *pre_global3_post;
- GlobalVariableCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای متغیرهای سراسری با بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- GlobalVariablePrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای متغیرهای سراسری اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
- GlobalVariableIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای متغیرهای سراسری که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- GlobalVariableSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای متغیرهای سراسری اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
- GlobalVariableHungarianPrefix
- در صورت فعال بودن، این بررسی اطمینان حاصل میکند که شناسه تعریفشده دارای یک پیشوند نمادگذاری مجارستانی (Hungarian notation) بر اساس نوع تعریفشده باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار GlobalVariableCase برابر با lower_case
- مقدار GlobalVariablePrefix برابر با pre_
- مقدار GlobalVariableSuffix برابر با _post
- مقدار GlobalVariableHungarianPrefix برابر با On
نامهای متغیرهای سراسری را به شکل زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
int GLOBAL3;
بعد:
int pre_global3_post;
- IgnoreMainLikeFunctions
- هنگامی که روی true تنظیم شود، توابعی که امضایی مشابه با main یا wmain دارند، بررسیها را بر روی نامهای پارامترهای خود اعمال نخواهند کرد. مقدار پیشفرض false است.
- InlineNamespaceCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای فضاهای نام درونخطی (inline namespaces) با بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- InlineNamespacePrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نامهای فضاهای نام درونخطی اضافه شود (صرفنظر از بزرگی یا کوچکی حروف).
- InlineNamespaceIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای فضاهای نام درونخطی که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- InlineNamespaceSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نامهای فضای نام درونخطی (inline namespace) اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
برای نمونه با استفاده از مقادیر:
- مقدار InlineNamespaceCase برابر با lower_case
- مقدار InlineNamespacePrefix برابر با pre_
- مقدار InlineNamespaceSuffix برابر با _post
نامهای فضاهای نام درونخطی را بهصورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
namespace FOO_NS {
inline namespace InlineNamespace {
...
}
} // namespace FOO_NS
بعد:
namespace FOO_NS {
inline namespace pre_inlinenamespace_post {
...
}
} // namespace FOO_NS
- LocalConstexprVariableCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متغیرهای محلی constexpr از شیوه نگارش حروف انتخابشده پیروی کنند.
- LocalConstexprVariablePrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نام متغیرهای محلی constexpr اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
- LocalConstexprVariableIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای متغیرهای محلی constexpr که با این عبارت منظم (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- LocalConstexprVariableSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نام متغیرهای محلی constexpr اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
- LocalConstexprVariableHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی (Hungarian notation) باشد.
برای نمونه با استفاده از مقادیر:
- مقدار LocalConstexprVariableCase برابر با lower_case
- مقدار LocalConstexprVariablePrefix برابر با pre_
- مقدار LocalConstexprVariableSuffix برابر با _post
- مقدار LocalConstexprVariableHungarianPrefix برابر با On
نام متغیرهای محلی constexpr را بهصورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void foo() { int const local_Constexpr = 420; }
بعد:
void foo() { int const pre_local_constexpr_post = 420; }
- LocalConstantCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام ثوابت محلی از شیوه نگارش حروف انتخابشده پیروی کنند.
- LocalConstantPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نام ثوابت محلی اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
- LocalConstantIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام ثوابت محلی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- LocalConstantSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نام ثوابت محلی اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
- LocalConstantHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی باشد.
برای نمونه با استفاده از مقادیر:
- مقدار LocalConstantCase برابر با lower_case
- مقدار LocalConstantPrefix برابر با pre_
- مقدار LocalConstantSuffix برابر با _post
- مقدار LocalConstantHungarianPrefix برابر با On
نام ثوابت محلی را بهصورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void foo() { int const local_Constant = 3; }
بعد:
void foo() { int const pre_local_constant_post = 3; }
- LocalConstantPointerCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام اشارهگرهای ثابت محلی از شیوه نگارش حروف انتخابشده پیروی کنند.
- LocalConstantPointerPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوندی با مقدار دادهشده به نام اشارهگرهای ثابت محلی اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
- LocalConstantPointerIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام اشارهگرهای ثابت محلی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- LocalConstantPointerSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوندی با مقدار دادهشده به نام اشارهگرهای ثابت محلی اضافه شود (صرفنظر از کوچک یا بزرگ بودن حروف).
- LocalConstantPointerHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی باشد.
برای نمونه با استفاده از مقادیر:
- مقدار lower_case برای LocalConstantPointerCase
- مقدار pre_ برای LocalConstantPointerPrefix
- مقدار _post برای LocalConstantPointerSuffix
- مقدار On برای LocalConstantPointerHungarianPrefix
نامهای اشارهگر ثابت محلی را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void foo() { int const *local_Constant = 3; }
بعد:
void foo() { int const *pre_local_constant_post = 3; }
- LocalPointerCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای اشارهگر محلی با حالت نویسههای (casing) انتخابشده مطابقت داشته باشند.
- LocalPointerPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که به نامهای اشارهگر محلی، پیشوند با مقدار دادهشده (صرفنظر از حالت نویسهها) اضافه شود.
- LocalPointerIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای اشارهگر محلی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- LocalPointerSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که به نامهای اشارهگر محلی، پسوند با مقدار دادهشده (صرفنظر از حالت نویسهها) اضافه شود.
- LocalPointerHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسهٔ اعلانشده بر اساس نوع اعلانشده، دارای پیشوندی با نمادگذاری مجارستانی (Hungarian notation) باشد.
برای مثال با استفاده از مقادیر:
- مقدار lower_case برای LocalPointerCase
- مقدار pre_ برای LocalPointerPrefix
- مقدار _post برای LocalPointerSuffix
- مقدار On برای LocalPointerHungarianPrefix
نامهای اشارهگر محلی را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void foo() { int *local_Constant; }
بعد:
void foo() { int *pre_local_constant_post; }
- LocalVariableCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای متغیرهای محلی با حالت نویسههای انتخابشده مطابقت داشته باشند.
- LocalVariablePrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که به نامهای متغیرهای محلی، پیشوند با مقدار دادهشده (صرفنظر از حالت نویسهها) اضافه شود.
- LocalVariableIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای متغیرهای محلی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
برای مثال با استفاده از مقادیر:
- مقدار CamelCase برای LocalVariableCase
- مقدار \w{1,2} برای LocalVariableIgnoredRegexp
متغیرهایی با طول کمتر یا مساوی ۲ را از بررسی camel case اعمالشده روی سایر متغیرها مستثنی میکند.
- LocalVariableSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که به نامهای متغیرهای محلی، پسوند با مقدار دادهشده (صرفنظر از حالت نویسهها) اضافه شود.
- LocalVariableHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسهٔ اعلانشده بر اساس نوع اعلانشده، دارای پیشوندی با نمادگذاری مجارستانی باشد.
برای مثال با استفاده از مقادیر:
- مقدار lower_case برای LocalVariableCase
- مقدار pre_ برای LocalVariablePrefix
- مقدار _post برای LocalVariableSuffix
- مقدار On برای LocalVariableHungarianPrefix
نامهای متغیرهای محلی را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void foo() { int local_Constant; }
بعد:
void foo() { int pre_local_constant_post; }
- MacroDefinitionCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که تعاریف ماکرو با حالت نویسههای انتخابشده مطابقت داشته باشند.
- MacroDefinitionPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که به تعاریف ماکرو، پیشوند با مقدار دادهشده (صرفنظر از حالت نویسهها) اضافه شود.
- MacroDefinitionIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای تعاریف ماکرویی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- MacroDefinitionSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که به تعاریف ماکرو، پسوند با مقدار دادهشده (صرفنظر از حالت نویسهها) اضافه شود.
برای مثال با استفاده از مقادیر:
- مقدار lower_case برای MacroDefinitionCase
- مقدار pre_ برای MacroDefinitionPrefix
- مقدار _post برای MacroDefinitionSuffix
تعاریف ماکرو را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
#define MY_MacroDefinition
بعد:
#define pre_my_macro_definition_post
نکته: این بررسی روی ماکروهای داخلی (builtin) یا ماکروهای تعریفشده در خط فرمان با استفاده از فلگ -D هشدار نمیدهد.
- MemberCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای اعضا (member) با حالت حروف (casing) انتخابشده مطابقت داشته باشند.
- MemberPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای اعضا اضافه شود.
- MemberIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای اعضایی که با این عبارت منظم (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- MemberSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای اعضا اضافه شود.
- MemberHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نشانهگذاری مجارستانی (Hungarian notation) باشد.
برای مثال با استفاده از مقادیر:
- مقدار MemberCase برابر lower_case
- مقدار MemberPrefix برابر pre_
- مقدار MemberSuffix برابر _post
- مقدار MemberHungarianPrefix برابر On
نامهای اعضا را به صورت زیر شناسایی یا تبدیل میکند:
قبل:
class Foo {
char MY_ConstMember_string[4];
}
بعد:
class Foo {
char pre_my_constmember_string_post[4];
}
- MethodCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای متدها با حالت حروف انتخابشده مطابقت داشته باشند.
- MethodPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای متدها اضافه شود.
- MethodIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای متدهایی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- MethodSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای متدها اضافه شود.
برای مثال با استفاده از مقادیر:
- مقدار MethodCase برابر lower_case
- مقدار MethodPrefix برابر pre_
- مقدار MethodSuffix برابر _post
نامهای متدها را به صورت زیر شناسایی یا تبدیل میکند:
قبل:
class Foo {
char MY_Method_string();
}
بعد:
class Foo {
char pre_my_method_string_post();
}
- NamespaceCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای فضای نام (namespace) با حالت حروف انتخابشده مطابقت داشته باشند.
- NamespacePrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای فضای نام اضافه شود.
- NamespaceIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای فضای نامی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- NamespaceSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای فضای نام اضافه شود.
برای مثال با استفاده از مقادیر:
- مقدار NamespaceCase برابر lower_case
- مقدار NamespacePrefix برابر pre_
- مقدار NamespaceSuffix برابر _post
نامهای فضای نام را به صورت زیر شناسایی یا تبدیل میکند:
قبل:
namespace FOO_NS {
...
}
بعد:
namespace pre_foo_ns_post {
...
}
- ParameterCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای پارامترها با حالت حروف انتخابشده مطابقت داشته باشند.
- ParameterPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پیشوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای پارامترها اضافه شود.
- ParameterIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای پارامترهایی که با این عبارت منظم مطابقت داشته باشند، اعمال نخواهد شد.
- ParameterSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که پسوند با مقدار مشخصشده (بدون در نظر گرفتن حالت حروف) به نامهای پارامترها اضافه شود.
- ParameterHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نمادگذاری مجارستانی (Hungarian notation) باشد.
برای نمونه با استفاده از مقادیر:
- مقدار ParameterCase برابر با lower_case
- مقدار ParameterPrefix برابر با pre_
- مقدار ParameterSuffix برابر با _post
- مقدار ParameterHungarianPrefix برابر با On
نامهای پارامترها را به شرح زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void GLOBAL_FUNCTION(int PARAMETER_1, int const CONST_parameter);
بعد:
void GLOBAL_FUNCTION(int pre_parameter_post, int const CONST_parameter);
- ParameterPackCase
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای بستههای پارامتر (parameter pack) با حالت بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- ParameterPackPrefix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که پیشوندی با مقدار مشخصشده (صرفنظر از بزرگی و کوچکی حروف) به نامهای بسته پارامتر اضافه شود.
- ParameterPackIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای بسته پارامتری که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ParameterPackSuffix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که پسوندی با مقدار مشخصشده (صرفنظر از بزرگی و کوچکی حروف) به نامهای بسته پارامتر اضافه شود.
برای نمونه با استفاده از مقادیر:
- مقدار ParameterPackCase برابر با lower_case
- مقدار ParameterPackPrefix برابر با pre_
- مقدار ParameterPackSuffix برابر با _post
نامهای بسته پارامتر را به شرح زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
template <typename... TYPE_parameters> {
void FUNCTION(int... TYPE_parameters);
}
بعد:
template <typename... TYPE_parameters> {
void FUNCTION(int... pre_type_parameters_post);
}
- PointerParameterCase
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای پارامترهای اشارهگر با حالت بزرگی و کوچکی حروف انتخابشده مطابقت داشته باشند.
- PointerParameterPrefix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که پیشوندی با مقدار مشخصشده (صرفنظر از بزرگی و کوچکی حروف) به نامهای پارامترهای اشارهگر اضافه شود.
- PointerParameterIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای پارامترهای اشارهگری که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- PointerParameterSuffix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که پسوندی با مقدار مشخصشده (صرفنظر از بزرگی و کوچکی حروف) به نامهای پارامترهای اشارهگر اضافه شود.
- PointerParameterHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نمادگذاری مجارستانی باشد.
برای نمونه با استفاده از مقادیر:
- مقدار PointerParameterCase برابر با lower_case
- مقدار PointerParameterPrefix برابر با pre_
- مقدار PointerParameterSuffix برابر با _post
- مقدار PointerParameterHungarianPrefix برابر با On
نامهای پارامترهای اشارهگر را به شرح زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
void FUNCTION(int *PARAMETER);
بعد:
void FUNCTION(int *pre_parameter_post);
- PrivateMemberCase
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که نامهای اعضای خصوصی (private member) با حالت بزرگی و کوچکی حروف انتخابشده مطابقت داشته باشند.
- PrivateMemberPrefix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که پیشوندی با مقدار مشخصشده (صرفنظر از بزرگی و کوچکی حروف) به نامهای اعضای خصوصی اضافه شود.
- PrivateMemberIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای اعضای خصوصی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- PrivateMemberSuffix
- در صورت تعریف شدن، بررسی اطمینان حاصل میکند که پسوندی با مقدار مشخصشده (صرفنظر از بزرگی و کوچکی حروف) به نامهای اعضای خصوصی اضافه شود.
- PrivateMemberHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نمادگذاری مجارستانی باشد.
برای نمونه با استفاده از مقادیر:
- مقدار PrivateMemberCase برابر با lower_case
- مقدار PrivateMemberPrefix برابر با pre_
- مقدار PrivateMemberSuffix برابر با _post
- مقدار PrivateMemberHungarianPrefix برابر با On
نامهای اعضای خصوصی را به شرح زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
class Foo {
private:
int Member_Variable;
}
بعد:
class Foo {
private:
int pre_member_variable_post;
}
- PrivateMethodCase
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متدهای خصوصی (private) با حالت حروف (casing) انتخابشده مطابقت داشته باشد.
- PrivateMethodPrefix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متدهای خصوصی پیشوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
- PrivateMethodIgnoredRegexp
- بررسیهای نامگذاری شناسه برای متدهای خصوصی که با این عبارت منظم (regular expression) تطابق داشته باشند، اعمال نخواهد شد.
- PrivateMethodSuffix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متدهای خصوصی پسوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
به عنوان مثال، با استفاده از مقادیر زیر:
- مقدار lower_case برای PrivateMethodCase
- مقدار pre_ برای PrivateMethodPrefix
- مقدار _post برای PrivateMethodSuffix
نام متدهای خصوصی را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
class Foo {
private:
int Member_Method();
}
بعد:
class Foo {
private:
int pre_member_method_post();
}
- ProtectedMemberCase
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام اعضای محافظتشده (protected) با حالت حروف انتخابشده مطابقت داشته باشد.
- ProtectedMemberPrefix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام اعضای محافظتشده پیشوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
- ProtectedMemberIgnoredRegexp
- بررسیهای نامگذاری شناسه برای اعضای محافظتشدهای که با این عبارت منظم تطابق داشته باشند، اعمال نخواهد شد.
- ProtectedMemberSuffix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام اعضای محافظتشده پسوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
- ProtectedMemberHungarianPrefix
- در صورت فعال بودن، این بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده خود دارای یک پیشوند با نمادگذاری مجارستانی (Hungarian notation) باشد.
به عنوان مثال، با استفاده از مقادیر زیر:
- مقدار lower_case برای ProtectedMemberCase
- مقدار pre_ برای ProtectedMemberPrefix
- مقدار _post برای ProtectedMemberSuffix
- مقدار On برای ProtectedMemberHungarianPrefix
نام اعضای محافظتشده را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
class Foo {
protected:
int Member_Variable;
}
بعد:
class Foo {
protected:
int pre_member_variable_post;
}
- ProtectedMethodCase
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متدهای محافظتشده با حالت حروف انتخابشده مطابقت داشته باشد.
- ProtectedMethodPrefix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متدهای محافظتشده پیشوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
- ProtectedMethodIgnoredRegexp
- بررسیهای نامگذاری شناسه برای متدهای محافظتشدهای که با این عبارت منظم تطابق داشته باشند، اعمال نخواهد شد.
- ProtectedMethodSuffix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متدهای محافظتشده پسوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
به عنوان مثال، با استفاده از مقادیر زیر:
- مقدار lower_case برای ProtectedMethodCase
- مقدار pre_ برای ProtectedMethodPrefix
- مقدار _post برای ProtectedMethodSuffix
نام متدهای محافظتشده را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
class Foo {
protected:
int Member_Method();
}
بعد:
class Foo {
protected:
int pre_member_method_post();
}
- PublicMemberCase
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام اعضای عمومی (public) با حالت حروف انتخابشده مطابقت داشته باشد.
- PublicMemberPrefix
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام اعضای عمومی پیشوندی با مقدار دادهشده را اضافه کنند (صرفنظر از وضعیت بزرگی و کوچکی حروف).
- PublicMemberIgnoredRegexp
- بررسیهای نامگذاری شناسه برای اعضای عمومی که با این عبارت منظم تطابق داشته باشند، اعمال نخواهد شد.
- PublicMemberSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نامهای اعضای عمومی (public)، پسوند مشخصشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
- PublicMemberHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی (Hungarian notation) باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار lower_case برای PublicMemberCase
- مقدار pre_ برای PublicMemberPrefix
- مقدار _post برای PublicMemberSuffix
- مقدار On برای PublicMemberHungarianPrefix
نام اعضای عمومی را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
class Foo {
public:
int Member_Variable;
}
بعد:
class Foo {
public:
int pre_member_variable_post;
}
- PublicMethodCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متدهای عمومی (public) با حالت حروفچینی (casing) انتخابشده مطابقت داشته باشند.
- PublicMethodPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متدهای عمومی، پیشوند با مقدار دادهشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
- PublicMethodIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نام متدهای عمومی که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- PublicMethodSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متدهای عمومی، پسوند با مقدار دادهشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار lower_case برای PublicMethodCase
- مقدار pre_ برای PublicMethodPrefix
- مقدار _post برای PublicMethodSuffix
نام متدهای عمومی را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
class Foo {
public:
int Member_Method();
}
بعد:
class Foo {
public:
int pre_member_method_post();
}
- ScopedEnumConstantCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام ثابتهای enum دارای دامنه (scoped enum) با حالت حروفچینی انتخابشده مطابقت داشته باشند.
- ScopedEnumConstantPrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام ثابتهای enum دارای دامنه، پیشوند با مقدار دادهشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
- ScopedEnumConstantIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نام ثابتهای enum دارای دامنه که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ScopedEnumConstantSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام ثابتهای enum دارای دامنه، پسوند با مقدار دادهشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
- ScopedEnumConstantHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار lower_case برای ScopedEnumConstantCase
- مقدار pre_ برای ScopedEnumConstantPrefix
- مقدار _post برای ScopedEnumConstantSuffix
- مقدار On برای ScopedEnumConstantHungarianPrefix
نام ثابتهای شمارشی را به صورت زیر شناسایی کرده و/یا تغییر شکل میدهد:
قبل:
enum class FOO { One, Two, Three };
بعد:
enum class FOO { pre_One_post, pre_Two_post, pre_Three_post };
- StaticConstexprVariableCase
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متغیرهای constexpr ایستا (static) با حالت حروفچینی انتخابشده مطابقت داشته باشند.
- StaticConstexprVariablePrefix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متغیرهای constexpr ایستا، پیشوند با مقدار دادهشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
- StaticConstexprVariableIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نام متغیرهای constexpr ایستا که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- StaticConstexprVariableSuffix
- در صورت تعریف، بررسی اطمینان حاصل میکند که نام متغیرهای constexpr ایستا، پسوند با مقدار دادهشده را (بدون در نظر گرفتن کوچک یا بزرگ بودن حروف) اضافه کنند.
- StaticConstexprVariableHungarianPrefix
- در صورت فعال بودن، بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع داده اعلانشده دارای یک پیشوند با نمادگذاری مجارستانی باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار StaticConstexprVariableCase برابر با lower_case
- مقدار StaticConstexprVariablePrefix برابر با pre_
- مقدار StaticConstexprVariableSuffix برابر با _post
- مقدار StaticConstexprVariableHungarianPrefix برابر با On
نام متغیرهای ایستا (constexpr static) را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
static unsigned constexpr MyConstexprStatic_array[] = {1, 2, 3};
بعد:
static unsigned constexpr pre_my_constexpr_static_array_post[] = {1, 2, 3};
- StaticConstantCase
- در صورت تعریف، بررسی تضمین میکند که نامهای ثابتهای ایستا با شیوهی نوشتن حروف (casing) انتخابشده مطابقت داشته باشند.
- StaticConstantPrefix
- در صورت تعریف، بررسی تضمین میکند که به نامهای ثابتهای ایستا، پیشوند با مقدار دادهشده (صرفنظر از وضعیت حروف) اضافه شود.
- StaticConstantIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نام ثابتهای ایستایی که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- StaticConstantSuffix
- در صورت تعریف، بررسی تضمین میکند که به نامهای ثابتهای ایستا، پسوند با مقدار دادهشده (صرفنظر از وضعیت حروف) اضافه شود.
- StaticConstantHungarianPrefix
- در صورت فعال بودن، بررسی تضمین میکند که شناسه اعلامشده دارای یک پیشوند با نمادگذاری مجارستانی بر اساس نوع داده اعلامشده باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار StaticConstantCase برابر با lower_case
- مقدار StaticConstantPrefix برابر با pre_
- مقدار StaticConstantSuffix برابر با _post
- مقدار StaticConstantHungarianPrefix برابر با On
نام ثابتهای ایستا را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
static unsigned const MyConstStatic_array[] = {1, 2, 3};
بعد:
static unsigned const pre_myconststatic_array_post[] = {1, 2, 3};
- StaticVariableCase
- در صورت تعریف، بررسی تضمین میکند که نامهای متغیرهای ایستا با شیوهی نوشتن حروف (casing) انتخابشده مطابقت داشته باشند.
- StaticVariablePrefix
- در صورت تعریف، بررسی تضمین میکند که به نامهای متغیرهای ایستا، پیشوند با مقدار دادهشده (صرفنظر از وضعیت حروف) اضافه شود.
- StaticVariableIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نام متغیرهای ایستایی که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- StaticVariableSuffix
- در صورت تعریف، بررسی تضمین میکند که به نامهای متغیرهای ایستا، پسوند با مقدار دادهشده (صرفنظر از وضعیت حروف) اضافه شود.
- StaticVariableHungarianPrefix
- در صورت فعال بودن، بررسی تضمین میکند که شناسه اعلامشده دارای یک پیشوند با نمادگذاری مجارستانی بر اساس نوع داده اعلامشده باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار StaticVariableCase برابر با lower_case
- مقدار StaticVariablePrefix برابر با pre_
- مقدار StaticVariableSuffix برابر با _post
- مقدار StaticVariableHungarianPrefix برابر با On
نام متغیرهای ایستا را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
static unsigned MyStatic_array[] = {1, 2, 3};
بعد:
static unsigned pre_mystatic_array_post[] = {1, 2, 3};
- StructCase
- در صورت تعریف، بررسی تضمین میکند که نامهای struct با شیوهی نوشتن حروف (casing) انتخابشده مطابقت داشته باشند.
- StructPrefix
- در صورت تعریف، بررسی تضمین میکند که به نامهای struct، پیشوند با مقدار دادهشده (صرفنظر از وضعیت حروف) اضافه شود.
- StructIgnoredRegexp
- بررسیهای نامگذاری شناسه برای نامهای struct که با این عبارت باقاعده مطابقت داشته باشند اعمال نخواهد شد.
- StructSuffix
- در صورت تعریف، بررسی تضمین میکند که به نامهای struct، پسوند با مقدار دادهشده (صرفنظر از وضعیت حروف) اضافه شود.
به عنوان مثال با استفاده از مقادیر:
- مقدار StructCase برابر با lower_case
- مقدار StructPrefix برابر با pre_
- مقدار StructSuffix برابر با _post
نامهای struct را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
struct FOO {
FOO();
~FOO();
};
بعد:
struct pre_foo_post {
pre_foo_post();
~pre_foo_post();
};
- TemplateParameterCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگو (template) با شیوه حروفبندی (casing) انتخابشده مطابقت داشته باشند.
- TemplateParameterPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگو پیشوند دادهشده را (صرفنظر از شیوه حروفبندی) به ابتدا اضافه کنند.
- TemplateParameterIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام پارامترهای الگویی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهند شد.
- TemplateParameterSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگو پسوند دادهشده را (صرفنظر از شیوه حروفبندی) به انتها اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار TemplateParameterCase برابر با lower_case
- مقدار TemplateParameterPrefix برابر با pre_
- مقدار TemplateParameterSuffix برابر با _post
نام پارامترهای الگو را به شرح زیر شناسایی و/یا دگرگون میکند:
پیش از تغییر:
template <typename T> class Foo {};
پس از تغییر:
template <typename pre_t_post> class Foo {};
- TemplateTemplateParameterCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی الگو (template template parameter) با شیوه حروفبندی انتخابشده مطابقت داشته باشند.
- TemplateTemplateParameterPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی الگو پیشوند دادهشده را (صرفنظر از شیوه حروفبندی) به ابتدا اضافه کنند.
- TemplateTemplateParameterIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام پارامترهای الگوی الگویی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهند شد.
- TemplateTemplateParameterSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی الگو پسوند دادهشده را (صرفنظر از شیوه حروفبندی) به انتها اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار TemplateTemplateParameterCase برابر با lower_case
- مقدار TemplateTemplateParameterPrefix برابر با pre_
- مقدار TemplateTemplateParameterSuffix برابر با _post
نام پارامترهای الگوی الگو را به شرح زیر شناسایی و/یا دگرگون میکند:
پیش از تغییر:
template <template <typename> class TPL_parameter, int COUNT_params,
typename... TYPE_parameters>
پس از تغییر:
template <template <typename> class pre_tpl_parameter_post, int COUNT_params,
typename... TYPE_parameters>
- TypeAliasCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام نامهای مستعار نوع (type alias) با شیوه حروفبندی انتخابشده مطابقت داشته باشند.
- TypeAliasPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام نامهای مستعار نوع، پیشوند دادهشده را (صرفنظر از شیوه حروفبندی) به ابتدا اضافه کنند.
- TypeAliasIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام نامهای مستعار نوعی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهند شد.
- TypeAliasSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام نامهای مستعار نوع، پسوند دادهشده را (صرفنظر از شیوه حروفبندی) به انتها اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار TypeAliasCase برابر با lower_case
- مقدار TypeAliasPrefix برابر با pre_
- مقدار TypeAliasSuffix برابر با _post
نام نامهای مستعار نوع را به شرح زیر شناسایی و/یا دگرگون میکند:
پیش از تغییر:
using MY_STRUCT_TYPE = my_structure;
پس از تغییر:
using pre_my_struct_type_post = my_structure;
- TypedefCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای typedef با شیوه حروفبندی انتخابشده مطابقت داشته باشند.
- TypedefPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای typedef پیشوند دادهشده را (صرفنظر از شیوه حروفبندی) به ابتدا اضافه کنند.
- TypedefIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای typedef که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهند شد.
- TypedefSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای typedef پسوند دادهشده را (صرفنظر از شیوه حروفبندی) به انتها اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار TypedefCase برابر با lower_case
- مقدار TypedefPrefix برابر با pre_
- مقدار TypedefSuffix برابر با _post
نامهای typedef را به شرح زیر شناسایی و/یا دگرگون میکند:
پیش از تغییر:
typedef int MYINT;
پس از تغییر:
typedef int pre_myint_post;
- TypeTemplateParameterCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی نوع (type template parameter) با حالت بزرگی و کوچکی حروف (casing) انتخابشده مطابقت داشته باشند.
- TypeTemplateParameterPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی نوع، پیشوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
- TypeTemplateParameterIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای الگوی نوع که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- TypeTemplateParameterSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی نوع، پسوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار TypeTemplateParameterCase برابر با lower_case
- مقدار TypeTemplateParameterPrefix برابر با pre_
- مقدار TypeTemplateParameterSuffix برابر با _post
نام پارامترهای الگوی نوع به صورت زیر شناسایی و/یا دگرگون میشوند:
قبل:
template <template <typename> class TPL_parameter, int COUNT_params,
typename... TYPE_parameters>
بعد:
template <template <typename> class TPL_parameter, int COUNT_params,
typename... pre_type_parameters_post>
- UnionCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای union با حالت بزرگی و کوچکی حروف انتخابشده مطابقت داشته باشند.
- UnionPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای union پیشوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
- UnionIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای union که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- UnionSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نامهای union پسوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار UnionCase برابر با lower_case
- مقدار UnionPrefix برابر با pre_
- مقدار UnionSuffix برابر با _post
نامهای union به صورت زیر شناسایی و/یا دگرگون میشوند:
قبل:
union FOO {
int a;
char b;
};
بعد:
union pre_foo_post {
int a;
char b;
};
- ValueTemplateParameterCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی مقدار (value template parameter) با حالت بزرگی و کوچکی حروف انتخابشده مطابقت داشته باشند.
- ValueTemplateParameterPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی مقدار، پیشوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
- ValueTemplateParameterIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نامهای پارامتر الگوی مقدار که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- ValueTemplateParameterSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام پارامترهای الگوی مقدار، پسوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
به عنوان مثال با استفاده از مقادیر:
- مقدار ValueTemplateParameterCase برابر با lower_case
- مقدار ValueTemplateParameterPrefix برابر با pre_
- مقدار ValueTemplateParameterSuffix برابر با _post
نام پارامترهای الگوی مقدار به صورت زیر شناسایی و/یا دگرگون میشوند:
قبل:
template <template <typename> class TPL_parameter, int COUNT_params,
typename... TYPE_parameters>
بعد:
template <template <typename> class TPL_parameter, int pre_count_params_post,
typename... TYPE_parameters>
- VariableCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام متغیرها با حالت بزرگی و کوچکی حروف انتخابشده مطابقت داشته باشند.
- VariablePrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام متغیرها پیشوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
- VariableIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام متغیرهایی که با این عبارت باقاعده مطابقت داشته باشند، اعمال نخواهد شد.
- VariableSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام متغیرها پسوند مشخصشده با این مقدار را (بدون در نظر گرفتن حالت بزرگی و کوچکی حروف) اضافه کنند.
- VariableHungarianPrefix
- در صورت فعال بودن، این بررسی اطمینان حاصل میکند که شناسه اعلانشده بر اساس نوع اعلانشده، دارای یک پیشوند با نشانهگذاری مجارستانی (Hungarian notation) باشد.
به عنوان مثال با استفاده از مقادیر:
- مقدار VariableCase برابر با lower_case
- مقدار VariablePrefix برابر با pre_
- مقدار VariableSuffix برابر با _post
- مقدار VariableHungarianPrefix برابر با On
نام متغیرها را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
unsigned MyVariable;
بعد:
unsigned pre_myvariable_post;
- VirtualMethodCase
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که نام متدهای مجازی با حالت حروف (casing) انتخابشده مطابقت داشته باشد.
- VirtualMethodPrefix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پیشوند مشخصشده (صرف نظر از حالت حروف) به نام متدهای مجازی اضافه شود.
- VirtualMethodIgnoredRegexp
- بررسیهای نامگذاری شناسهها برای نام متدهای مجازی که با این عبارت باقاعده (regular expression) مطابقت داشته باشند، اعمال نخواهد شد.
- VirtualMethodSuffix
- در صورت تعریف شدن، این بررسی اطمینان حاصل میکند که پسوند مشخصشده (صرف نظر از حالت حروف) به نام متدهای مجازی اضافه شود.
برای مثال با استفاده از مقادیر زیر:
- مقدار VirtualMethodCase برابر با lower_case
- مقدار VirtualMethodPrefix برابر با pre_
- مقدار VirtualMethodSuffix برابر با _post
نام متدهای مجازی را به صورت زیر شناسایی کرده و/یا تغییر میدهد:
قبل:
class Foo {
public:
virtual int MemberFunction();
}
بعد:
class Foo {
public:
virtual int pre_member_function_post();
}
در نشانهگذاری مجارستانی، نام یک متغیر با دستهای از حروف کوچک آغاز میشود که یادافزایی (نماد اختصاری) برای نوع یا کاربرد آن متغیر هستند، و در ادامه نامی که برنامهنویس انتخاب کرده قرار میگیرد؛ به این بخش آخر گاهی نام اختصاصیافته (given name) گفته میشود. حرف اولِ نامِ اختصاصیافته میتواند با حروف بزرگ نوشته شود تا از نشانگرهای نوع داده متمایز گردد (همچنین CamelCase را ببینید). در غیر این صورت، بزرگی یا کوچکی این نویسه نشاندهنده حوزه (scope) خواهد بود.
جدول زیر، جدول نگاشت پیشفرض نشانهگذاری مجارستانی است که Decl را به رشته پیشوند مربوط به آن نگاشت میکند. شما همچنین میتوانید سبک دلخواه خود را در فایل پیکربندی تنظیم کنید.
| نوع پایه (Primitive) | نوع مایکروسافت (Microsoft) | ||||
| نوع | پیشوند | نوع | پیشوند | نوع | پیشوند |
| int8_t | i8 | signed int | si | BOOL | b |
| int16_t | i16 | signed short | ss | BOOLEAN | b |
| int32_t | i32 | signed short int | ssi | BYTE | by |
| int64_t | i64 | signed long long int | slli | CHAR | c |
| uint8_t | u8 | signed long long | sll | UCHAR | uc |
| uint16_t | u16 | signed long int | sli | SHORT | s |
| uint32_t | u32 | signed long | sl | USHORT | us |
| uint64_t | u64 | signed | s | WORD | w |
| char8_t | c8 | unsigned long long int | ulli | DWORD | dw |
| char16_t | c16 | unsigned long long | ull | DWORD32 | dw32 |
| char32_t | c32 | unsigned long int | uli | DWORD64 | dw64 |
| float | f | unsigned long | ul | LONG | l |
| double | d | unsigned short int | usi | ULONG | ul |
| char | c | unsigned short | us | ULONG32 | ul32 |
| bool | b | unsigned int | ui | ULONG64 | ul64 |
| _Bool | b | unsigned char | uc | ULONGLONG | ull |
| int | i | unsigned | u | HANDLE | h |
| size_t | n | long long int | lli | INT | i |
| short | s | long double | ld | INT8 | i8 |
| signed | i | long long | ll | INT16 | i16 |
| unsigned | u | long int | li | INT32 | i32 |
| long | l | long | l | INT64 | i64 |
| long long | ll | ptrdiff_t | p | UINT | ui |
| unsigned long | ul | void | none | UINT8 | u8 |
| long double | ld | UINT16 | u16 | ||
| ptrdiff_t | p | UINT32 | u32 | ||
| wchar_t | wc | UINT64 | u64 | ||
| short int | si | PVOID | p | ||
| short | s |
گزینههای جزئیتری برای نشانهگذاری مجارستانی (Hungarian Notation) وجود دارد:
- HungarianNotation.General.*
- گزینههایی که متعلق به هیچ Decl خاصی نیستند.
- HungarianNotation.CString.*
- گزینههای مربوط به رشتههای پایانیافته با NULL.
- HungarianNotation.DerivedType.*
- گزینههای مربوط به انواع مشتقشده (Derived types).
- HungarianNotation.PrimitiveType.*
- گزینههای مربوط به انواع پایهای (Primitive types).
- HungarianNotation.UserDefinedType.*
- گزینههای مربوط به انواع تعریفشده توسط کاربر (User-defined types).
گزینههای Hungarian Notation
- HungarianNotation.General.TreatStructAsClass
- HungarianNotation.DerivedType.Array
- HungarianNotation.DerivedType.Pointer
- HungarianNotation.DerivedType.FunctionPointer
- HungarianNotation.CString.CharPointer
- HungarianNotation.CString.CharArray
- HungarianNotation.CString.WideCharPointer
- HungarianNotation.CString.WideCharArray
- HungarianNotation.PrimitiveType.*
- HungarianNotation.UserDefinedType.*
- HungarianNotation.General.TreatStructAsClass
- در صورت تعریف، این بررسی با نامگذاری struct مانند یک class رفتار خواهد کرد. مقدار پیشفرض false است.
- HungarianNotation.DerivedType.Array
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض a است.
- HungarianNotation.DerivedType.Pointer
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض p است.
- HungarianNotation.DerivedType.FunctionPointer
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض fn است.
قبل:
// Array
int DataArray[2] = {0};
// Pointer
void *DataBuffer = NULL;
// FunctionPointer
typedef void (*FUNC_PTR)();
FUNC_PTR FuncPtr = NULL;
بعد:
// Array
int aDataArray[2] = {0};
// Pointer
void *pDataBuffer = NULL;
// FunctionPointer
typedef void (*FUNC_PTR)();
FUNC_PTR fnFuncPtr = NULL;
- HungarianNotation.CString.CharPointer
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض sz است.
- HungarianNotation.CString.CharArray
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض sz است.
- HungarianNotation.CString.WideCharPointer
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض wsz است.
- HungarianNotation.CString.WideCharArray
- در صورت تعریف، این بررسی اطمینان حاصل میکند که پیشوندی با رشته دادهشده به نام متغیر اضافه شود. پیشوند پیشفرض wsz است.
قبل:
// CharPointer const char *NamePtr = "Name"; // CharArray const char NameArray[] = "Name"; // WideCharPointer const wchar_t *WideNamePtr = L"Name"; // WideCharArray const wchar_t WideNameArray[] = L"Name";
بعد:
// CharPointer const char *szNamePtr = "Name"; // CharArray const char szNameArray[] = "Name"; // WideCharPointer const wchar_t *wszWideNamePtr = L"Name"; // WideCharArray const wchar_t wszWideNameArray[] = L"Name";
- HungarianNotation.PrimitiveType.*
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متغیر مربوط به انواع پایهای درگیر، پیشوندی با رشته دادهشده دریافت کند. پیشوندهای پیشفرض در جدول نگاشت پیشفرض تعریف شدهاند.
- HungarianNotation.UserDefinedType.*
- در صورت تعریف، این بررسی اطمینان حاصل میکند که نام متغیر مربوط به انواع پایهای درگیر، پیشوندی با رشته دادهشده دریافت کند. پیشوندهای پیشفرض در جدول نگاشت پیشفرض تعریف شدهاند.
قبل:
int8_t ValueI8 = 0; int16_t ValueI16 = 0; int32_t ValueI32 = 0; int64_t ValueI64 = 0; uint8_t ValueU8 = 0; uint16_t ValueU16 = 0; uint32_t ValueU32 = 0; uint64_t ValueU64 = 0; float ValueFloat = 0.0; double ValueDouble = 0.0; ULONG ValueUlong = 0; DWORD ValueDword = 0;
بعد:
int8_t i8ValueI8 = 0; int16_t i16ValueI16 = 0; int32_t i32ValueI32 = 0; int64_t i64ValueI64 = 0; uint8_t u8ValueU8 = 0; uint16_t u16ValueU16 = 0; uint32_t u32ValueU32 = 0; uint64_t u64ValueU64 = 0; float fValueFloat = 0.0; double dValueDouble = 0.0; ULONG ulValueUlong = 0; DWORD dwValueDword = 0;
readability-implicit-bool-cast
نام این بررسی به readability-implicit-bool-conversion <> تغییر یافته است.
readability-implicit-bool-conversion
این بررسی میتواند برای یافتن تبدیلهای ضمنی (implicit conversions) بین انواع توکار (built-in) و بولیها (booleans) استفاده شود. بسته به مورد استفاده، این ابزار ممکن است صرفاً به خوانایی کد کمک کند، یا در برخی موارد، باگهای بالقوهای را که به دلیل تبدیلهای ضمنی ناشناخته ماندهاند، مشخص نماید.
کد زیر یک نمونه واقعی از باگی است که در پسِ تبدیل ضمنی bool پنهان شده بود:
class Foo {
int m_foo;
public:
void setFoo(bool foo) { m_foo = foo; } // warning: implicit conversion bool -> int
int getFoo() { return m_foo; }
};
void use(Foo& foo) {
bool value = foo.getFoo(); // warning: implicit conversion int -> bool
}
این کد نتیجه یک بازآفرینی (refactoring) ناموفق است که در آن نوعِ m_foo از bool به int تغییر یافته است. برنامهنویس تغییر دادن تمام موارد bool را فراموش کرده و کد باقیمانده دیگر صحیح نیست، با این حال همچنان بدون هیچ هشدار مشهودی کامپایل میشود.
علاوه بر صدور هشدارها، راهنماییهای تصحیح (fix-it hints) نیز برای کمک به حل مشکلات گزارششده ارائه میشوند. این موضوع میتواند برای بهبود خوانایی کد استفاده شود، برای مثال:
void conversionsToBool() {
float floating;
bool boolean = floating;
// ^ propose replacement: bool boolean = floating != 0.0f;
int integer;
if (integer) {}
// ^ propose replacement: if (integer != 0) {}
int* pointer;
if (!pointer) {}
// ^ propose replacement: if (pointer == nullptr) {}
while (1) {}
// ^ propose replacement: while (true) {}
}
void functionTakingInt(int param);
void conversionsFromBool() {
bool boolean;
functionTakingInt(boolean);
// ^ propose replacement: functionTakingInt(static_cast<int>(boolean));
functionTakingInt(true);
// ^ propose replacement: functionTakingInt(1);
}
به طور کلی، انواع تبدیلهای زیر بررسی میشوند:
- عبارت/لیترال عدد صحیح (integer) به بولی (تبدیل از یک فیلد بیتی تکبیتی به بولی صراحتاً مجاز است، زیرا در این حالت هیچ ابهام یا از دست رفتن اطلاعاتی وجود ندارد)،
- عبارت/لیترال ممیز شناور (floating) به بولی،
- اشارهگر/اشارهگر به عضو/nullptr/NULL به بولی،
- عبارت/لیترال بولی به عدد صحیح (تبدیل از بولی به یک فیلد بیتی تکبیتی صراحتاً مجاز است)،
- عبارت/لیترال بولی به ممیز شناور.
قوانین تولید راهنماییهای تصحیح (fix-it hints) عبارتند از:
- •
- در صورت تبدیل از انواع توکار دیگر به bool، یک مقایسه صریح پیشنهاد میشود تا مشخص گردد دقیقاً چه چیزی مقایسه میشود:
- bool boolean = floating; به bool boolean = floating == 0.0f; تغییر مییابد،
- برای انواع دیگر، از لیترالهای مناسب استفاده میشود (0، 0u، 0.0f، 0.0، nullptr)،
- •
- در صورت تبدیل عبارتهای نقیضشده به bool، جایگزینی پیشنهادی همراه با مقایسه سادهسازی میشود:
- •
- if (!pointer) به if (pointer == nullptr) تغییر مییابد،
- •
- در صورت تبدیل از bool به انواع توکار دیگر، یک static_cast صریح (یا تبدیل نوع سبک C از زمان C23) پیشنهاد میشود تا مشخص شود که یک تبدیل در حال انجام است:
- •
- int integer = boolean; به int integer = static_cast<int>(boolean); تغییر مییابد،
- •
- اگر تبدیل روی لیترالهای نوع انجام شود، بر اساس نوعی که واقعاً مورد انتظار است، یک لیترال معادل پیشنهاد میگردد، برای مثال:
- functionTakingBool(0); به functionTakingBool(false); تغییر مییابد،
- functionTakingInt(true); به functionTakingInt(1); تغییر مییابد،
- برای انواع دیگر، لیترالهای مناسب استفاده میشوند (false، true، 0، 1، 0u، 1u، 0.0f، 1.0f، 0.0، 1.0f).
برخی تطبیقهای اضافه برای نسخههای پیش از C++11 در نظر گرفته شده است:
- تبدیل لیترال false به اشارهگر تشخیص داده میشود،
- به جای لیترال nullptr، مقدار 0 به عنوان جایگزین پیشنهاد میشود.
برخی تطبیقهای اضافه برای C در نظر گرفته شده است:
- •
- عملوندهای bool (یا _Bool) در عملگرهای منطقی (&&، ||) نادیده گرفته میشوند.
موارد رخدادِ تبدیلهای ضمنی در ماکروها و نمونهسازیهای الگو (template instantiations) عمداً نادیده گرفته میشوند، زیرا مشخص نیست که چگونه باید با این موارد برخورد شود.
گزینهها
- AllowIntegerConditions
- وقتی true باشد، بررسی اجازه تبدیلات شرطی اعداد صحیح را میدهد. مقدار پیشفرض false است.
- AllowPointerConditions
- وقتی true باشد، بررسی اجازه تبدیلات شرطی اشارهگرها را میدهد. مقدار پیشفرض false است.
- UseUpperCaseLiteralSuffix
- وقتی true باشد، جایگزینیها در اصلاحات ارائهشده از پسوند لیترال با حروف بزرگ استفاده خواهند کرد. مقدار پیشفرض false است.
uint32_t foo;
if (foo) {}
// ^ propose replacement default: if (foo != 0u) {}
// ^ propose replacement with option `UseUpperCaseLiteralSuffix`: if (foo != 0U) {}
readability-inconsistent-declaration-parameter-name
اعلانهای تابعی را که در نام پارامترها با یکدیگر تفاوت دارند پیدا میکند.
مثال:
// in foo.hpp: void foo(int a, int b, int c); // in foo.cpp: void foo(int d, int e, int f); // warning
این بررسی به اعمال یکپارچگی در پروژههای بزرگ کمک میکند؛ جایی که اغلب تعریف یک تابع بازنویسی (refactor) شده و نام پارامترهای آن تغییر میکند، اما اعلان آن در فایل سرایند (header) بهروزرسانی نمیشود. با این بررسی، میتوان بهسادگی چنین ناهماهنگیهایی را پیدا و اصلاح کرد و اعلان و تعریف را همواره همگام نگه داشت.
پارامترهای بدون نام مجاز هستند و هنگام مقایسه اعلانهای توابع در نظر گرفته نمیشوند، به عنوان مثال:
void foo(int a); void foo(int); // no warning
همچنین مجاز است که یک نام، پیشوند یا پسوند غیرحساس به حروف کوچک و بزرگ برای نام دیگر باشد:
void foo(int count);
void foo(int count_input) { // no warning
int count = adjustCount(count_input);
}
جهت کمک به بازنویسی کد، در برخی موارد راهنماییهای اصلاح خودکار (fix-it hints) برای همگامسازی نام پارامترها با یک قرارداد نامگذاری واحد تولید میشوند. این کار بر اساس این فرض عمل میکند که تعریف تابع بهروزترین نسخه است، زیرا مستقیماً در بدنه خود به نام پارامترها ارجاع میدهد. مثال:
void foo(int a); // warning and fix-it hint (replace "a" to "b")
int foo(int b) { return b + 2; } // definition with use of "b"
در صورت وجود چندین اعلان مجدد یا تخصیص قالب تابع (specialization)، برای هر اعلان مجدد یا تخصیصی که با تعریف یا اولین اعلان دیدهشده در یک واحد ترجمه (translation unit) ناسازگار باشد، یک هشدار صادر میشود.
گزینهها
- IgnoreMacros
- اگر این گزینه روی true تنظیم شود (پیشفرض true است)، بررسی درباره نامهای اعلانشده در داخل ماکروها هشدار نخواهد داد.
- Strict
- اگر این گزینه روی true تنظیم شود (پیشفرض false است)، نامها باید دقیقاً مطابقت داشته باشند (یا اصلاً وجود نداشته باشند).
readability-inconsistent-ifelse-braces
عبارات if/else را که در آنها یک شاخه از آکsupported آکولاد استفاده کرده و شاخه دیگر استفاده نکرده است، تشخیص میدهد.
قبل:
if (condition) {
statement;
} else
statement;
if (condition)
statement;
if (condition)
statement;
else
statement;
بعد:
if (condition) {
statement;
} else {
statement;
}
if (condition)
statement;
if (condition)
statement;
else
statement;
readability-isolate-declaration
تعریف متغیرهای محلی که بیش از یک متغیر را تعریف میکنند شناسایی کرده و تلاش میکند کد را به یک دستور در هر اعلان بازنویسی (refactor) کند.
تبدیل خودکار کد از همان تورفتگیِ نسخه اصلی برای هر دستور ایجادشده استفاده خواهد کرد و پس از هر دستور یک شکستگی خط (line break) اضافه میکند. همچنین ترتیب اعلان متغیرها را ثابت نگه میدارد.
void f() {
int * pointer = nullptr, value = 42, * const const_ptr = &value;
// This declaration will be diagnosed and transformed into:
// int * pointer = nullptr;
// int value = 42;
// int * const const_ptr = &value;
}
این بررسی مکانهایی را که در آنها اعلان چندین متغیر در یک دستور ضروری یا رایج است و راه دیگری در زبان پشتیبانی نمیشود، مستثنی میکند. لطفاً توجه داشته باشید که structured bindings در نظر گرفته نمیشوند.
// It is not possible to transform this declaration and doing the declaration // before the loop will increase the scope of the variable 'Begin' and 'End' // which is undesirable. for (int Begin = 0, End = 100; Begin < End; ++Begin); if (int Begin = 42, Result = some_function(Begin); Begin == Result); // It is not possible to transform this declaration because the result is // not functionality preserving as 'j' and 'k' would not be part of the // 'if' statement anymore. if (SomeCondition()) int i = 42, j = 43, k = function(i,j);
محدودیتها
متغیرهای سراسری (global) و متغیرهای عضو (member variables) مستثنی هستند.
این بررسی در حال حاضر از تبدیل خودکار انواع اشارهگر-به-عضو (member-pointer-types) پشتیبانی نمیکند.
struct S {
int a;
const int b;
void f() {}
};
void f() {
// Only a diagnostic message is emitted
int S::*p = &S::a, S::*const q = &S::a;
}
علاوه بر این، در صورت شناسایی انواع ماکروها یا دستورات پیشپردازنده در محدوده دستور، تبدیل بسیار محتاطانه عمل میکند. در این حالت، برای جلوگیری از اثرات جانبی غیرمنتظره ناشی از ماکروها، تبدیل انجام نخواهد شد.
#define NULL 0
#define MY_NICE_TYPE int **
#define VAR_NAME(name) name##__LINE__
#define A_BUNCH_OF_VARIABLES int m1 = 42, m2 = 43, m3 = 44;
void macros() {
int *p1 = NULL, *p2 = NULL;
// Will be transformed to
// int *p1 = NULL;
// int *p2 = NULL;
MY_NICE_TYPE p3, v1, v2;
// Won't be transformed, but a diagnostic is emitted.
int VAR_NAME(v3),
VAR_NAME(v4),
VAR_NAME(v5);
// Won't be transformed, but a diagnostic is emitted.
A_BUNCH_OF_VARIABLES
// Won't be transformed, but a diagnostic is emitted.
int Unconditional,
#if CONFIGURATION
IfConfigured = 42,
#else
IfConfigured = 0;
#endif
// Won't be transformed, but a diagnostic is emitted.
}
readability-magic-numbers
اعداد جادویی (magic numbers) را شناسایی میکند؛ لیترالهای عدد صحیح یا ممیز شناور که در کد تعبیه شدهاند و از طریق ثوابت یا نمادها معرفی نشدهاند.
بسیاری از شیوهنامههای کدنویسی برای بهبود خوانایی، جایگزینی مقادیر جادویی با ثوابت نمادین را توصیه میکنند. در اینجا چند مرجع آورده شده است:
- قاعده ES.45: از "magic constants" پرهیز کنید؛ از ثوابت نمادین در C++ Core Guidelines استفاده کنید https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-magic
- قاعده 5.1.1 از نامهای نمادین به جای مقادیر لیترال در کد در High Integrity C++ استفاده کنید https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard-expressions
- مورد ۱۷ در "C++ Coding Standards: 101 Rules, Guidelines and Best Practices" نوشته Herb Sutter و Andrei Alexandrescu
- فصل ۱۷ در "Clean Code - A handbook of agile software craftsmanship." نوشته Robert C. Martin
- قاعده ۲۰۷۰۱ در "TRAIN REAL TIME DATA PROTOCOL Coding Rules" نوشته Armin-Hagen Weiss، شرکت Bombardier
- http://wiki.c2.com/?MagicNumber
نمونههایی از مقادیر جادویی:
template<typename T, size_t N>
struct CustomType {
T arr[N];
};
struct OtherType {
CustomType<int, 30> container;
}
CustomType<int, 30> values;
double circleArea = 3.1415926535 * radius * radius;
double totalCharge = 1.08 * itemPrice;
int getAnswer() {
return -3; // FILENOTFOUND
}
for (int mm = 1; mm <= 12; ++mm) {
std::cout << month[mm] << '\n';
}
نمونه با مقادیر جادویی بازنویسیشده:
template<typename T, size_t N>
struct CustomType {
T arr[N];
};
const size_t NUMBER_OF_ELEMENTS = 30;
using containerType = CustomType<int, NUMBER_OF_ELEMENTS>;
struct OtherType {
containerType container;
}
containerType values;
double circleArea = M_PI * radius * radius;
const double TAX_RATE = 0.08; // or make it variable and read from a file
double totalCharge = (1.0 + TAX_RATE) * itemPrice;
int getAnswer() {
return E_FILE_NOT_FOUND;
}
for (int mm = 1; mm <= MONTHS_IN_A_YEAR; ++mm) {
std::cout << month[mm] << '\n';
}
برای لیترالهای عدد صحیح، بهطور پیشفرض تنها مقادیر صحیح 0 و 1 (و -1) بدون هشدار پذیرفته میشوند. این مورد میتواند با گزینه IgnoredIntegerValues بازنویسی شود. مقادیر منفی در صورتی پذیرفته میشوند که قدر مطلق آنها در فهرست IgnoredIntegerValues موجود باشد.
به عنوان یک مورد خاص برای مقادیر صحیح، تمام توانهای دو را میتوان با فعال کردن گزینه IgnorePowersOf2IntegerValues بدون هشدار پذیرفت.
برای لیترالهای ممیز شناور، بهطور پیشفرض مقدار ممیز شناور 0.0 بدون هشدار پذیرفته میشود. مجموعه لیترالهای ممیز شناور نادیدهگرفتهشده میتواند با استفاده از گزینه IgnoredFloatingPointValues پیکربندی شود. برای هر مقدار در آن مجموعه، مقدار رشتهای دادهشده به نمایشی از مقدار ممیز شناور که توسط معماری مقصد استفاده میشود، تبدیل میگردد. اگر یک مقدار لیترال ممیز شناور با یکی از مقادیر تبدیلشده برابر باشد، آن لیترال توسط این بررسی عیبیابی نمیشود. از آنجا که برابری ممیز شناور برای تعیین نیاز به عیبیابی استفاده میشود، کاربر باید از جزئیات نمایشهای ممیز شناور برای هر مقداری که نمیتواند به طور دقیق برای معماری مقصد آنها نشان داده شود، آگاه باشد.
برای هر مقدار در مجموعه IgnoredFloatingPointValues، هر دو شکل با دقت یگانه (single-precision) و دقت دوگانه (double-precision) پذیرفته میشوند (به عنوان مثال، اگر ۳.۱۴ در مجموعه باشد، نه 3.14f و نه 3.14 هیچ هشداری تولید نخواهند کرد).
نماد علمی هم برای ورودی کد منبع و هم برای گزینه پشتیبانی میشود. از طرف دیگر، بررسی اعداد ممیز شناور را میتوان برای تمام مقادیر ممیز شناور با فعال کردن گزینه IgnoreAllFloatingPointValues غیرفعال کرد.
از آنجا که مقادیر 0 و 0.0 به عنوان شمارنده پایه حلقهها یا مقادیر مقداردهی اولیه برای مجموعها بسیار رایج هستند، همیشه بدون هشدار پذیرفته میشوند، حتی اگر در فهرست مقادیر نادیدهگرفتهشده مربوطه وجود نداشته باشند.
گزینهها
- IgnoredIntegerValues
- فهرستی از اعداد صحیح مثبت جادویی (magic numbers) که با نقطه-ویرگول (semicolon) از هم جدا شدهاند و بدون هشدار پذیرفته خواهند شد. مقادیر پیشفرض {1, 2, 3, 4} هستند، و 0 بدون قید و شرط پذیرفته میشود.
- IgnorePowersOf2IntegerValues
- یک مقدار بولی که مشخص میکند آیا تمام مقادیر صحیح توانهای دو بدون هشدار پذیرفته شوند یا خیر. مقدار پیشفرض false است.
- IgnoredFloatingPointValues
- فهرستی از مقادیر ممیز شناور مثبت جادویی که با نقطه-ویرگول از هم جدا شدهاند و بدون هشدار پذیرفته خواهند شد. مقادیر پیشفرض {1.0, 100.0} هستند و 0.0 بدون قید و شرط پذیرفته میشود.
- IgnoreAllFloatingPointValues
- یک مقدار بولی که مشخص میکند آیا تمام مقادیر ممیز شناور بدون هشدار پذیرفته شوند یا خیر. مقدار پیشفرض false است.
- IgnoreBitFieldsWidths
- یک مقدار بولی که مشخص میکند آیا اعداد جادویی به عنوان عرض فیلدهای بیتی بدون هشدار پذیرفته شوند یا خیر. این مورد به عنوان مثال برای تعاریف ثباتها (registers) که از مشخصات سختافزاری تولید میشوند مفید است. مقدار پیشفرض true است.
- IgnoreTypeAliases
- یک مقدار بولی که مشخص میکند آیا اعداد جادویی در اعلانهای typedef یا using پذیرفته شوند یا خیر. مقدار پیشفرض false است.
- IgnoreUserDefinedLiterals
- یک مقدار بولی که مشخص میکند آیا اعداد جادویی در لیترالهای تعریفشده توسط کاربر (user-defined literals) پذیرفته شوند یا خیر. مقدار پیشفرض false است.
readability-make-member-function-const
توابع عضو غیر ایستا (non-static) را که میتوانند const شوند، پیدا میکند، زیرا این توابع از this به صورت غیر const استفاده نمیکنند.
این بررسی تلاش میکند متدها را بر اساس const بودن منطقی (logical constness) https://isocpp.org/wiki/faq/const-correctness#logical-vs-physical-state (نه const بودن فیزیکی) نشانهگذاری کند. بنابراین، تنها در صورتی اضافه کردن یک توصیفکننده const به یک متد غیر const را پیشنهاد میدهد که آن متد کاری را انجام دهد که پیش از این از طریق رابط عمومی (public interface) روی یک اشارهگر const به شیء امکانپذیر باشد:
- خواندن یک متغیر عضو عمومی
- فراخوانی یک تابع عضو عمومی با توصیفکننده const
- بازگرداندن this با توصیفکننده const
- ارسال this با توصیفکننده const به عنوان یک پارامتر.
این بررسی همچنین در صورتی اضافه کردن توصیفکننده const به یک متد غیر const را پیشنهاد میکند که این متد از دادهها و توابع خصوصی به شیوههای محدودی استفاده کند که در آنها const بودن منطقی و فیزیکی بر هم منطبق باشند:
- •
- خواندن یک متغیر عضو از نوع توکار (builtin)
بهطور مشخص، این بررسی اضافه کردن const به یک متد غیر const را در صورتی که متد یک متغیر عضو خصوصی از نوع اشارهگر را بخواند پیشنهاد نمیدهد، زیرا این کار امکان تغییر شیء اشارهشده (pointee) را فراهم میکند که ممکن است const بودن منطقی را حفظ نکند. به همین دلیل، فراخوانی توابع عضو خصوصی یا توابع عضو روی متغیرهای عضو خصوصی را مجاز نمیداند.
علاوه بر این، این بررسی توابعی را نادیده میگیرد که:
- به صورت virtual اعلان شده باشند
- حاوی یک const_cast باشند
- قالب (templated) باشند یا بخشی از یک قالب کلاس باشند
- بدنه خالی داشته باشند
- اصلاً (به طور ضمنی) از this استفاده نکنند (نگاه کنید به readability-convert-member-functions-to-static <>).
مثالهای واقعی زیر توسط این بررسی بدون تغییر حفظ خواهند شد:
class E1 {
Pimpl &getPimpl() const;
public:
int &get() {
// Calling a private member function disables this check.
return getPimpl()->i;
}
...
};
class E2 {
public:
const int *get() const;
// const_cast disables this check.
S *get() {
return const_cast<int*>(const_cast<const C*>(this)->get());
}
...
};
پس از اعمال تغییرات طبق پیشنهادات این بررسی، اجرای مجدد آن ممکن است فرصتهای بیشتری برای علامتگذاری توابع عضو با const پیدا کند.
readability-math-missing-parentheses
بررسی برای پرانتزهای مفقود در عبارات ریاضی که شامل عملگرهایی با اولویتهای متفاوت هستند.
پرانتزها در عبارات ریاضی ترتیب عملیات را روشن میسازند، بهویژه در مورد عملگرهایی با اولویت متفاوت. عبارات طولانی یا چندخطی میتوانند این ترتیب را مبهم کرده و منجر به خطاهای برنامهنویسی شوند. محیطهای توسعه یکپارچه (IDE) میتوانند با برجستهسازی پرانتزها به شفافیت کمک کنند. استفاده صریح از پرانتزها همچنین مشخص میکند که توسعهدهنده هنگام نوشتن عبارت چه چیزی در ذهن داشته است. اطمینان از وجود آنها ابهام و خطاها را کاهش داده و کدی شفافتر و نگهداریپذیرتر را ترویج میکند.
قبل:
int x = 1 + 2 * 3 - 4 / 5;
بعد:
int x = 1 + (2 * 3) - (4 / 5);
readability-misleading-indentation
دندانهگذاری (indentation) صحیح به درک کد کمک میکند. عدم تطابق ساختار نحوی و دندانهگذاری کد ممکن است مشکلات جدی را پنهان کند. نبود آکولادها نیز میتواند خواندن کد را به طور قابل توجهی سختتر کند، بنابراین استفاده از آکولادها مهم است.
راه اجتناب از else سرگردان (dangling else) این است که همیشه بررسی شود یک else متعلق به ifی است که در همان ستون آغاز شده است.
در صورتی که بخش درونی مثلاً یک دستور if تنها شامل یک دستور باشد، میتوانید از آکولادها صرفنظر کنید. با این حال در آن صورت باید دستور بعدی را در همان ستون if آغاز نمایید.
مثالها:
// Dangling else:
if (cond1)
if (cond2)
foo1();
else
foo2(); // دندانهگذاری اشتباه: else به دستور if(cond2) تعلق دارد.
// Missing braces:
if (cond1)
foo1();
foo2(); // توسط if(cond1) محافظت نشده است.
محدودیتها
توجه داشته باشید که این بررسی تنها زمانی طبق انتظار کار میکند که از تبها یا فاصلهها بهصورت یکدست استفاده شده باشد و با یکدیگر ترکیب نشده باشند.
readability-misplaced-array-index
این بررسی درباره نحو نامتعارف ایندکس آرایه هشدار میدهد.
کد زیر دارای نحو نامتعارف ایندکس آرایه است:
void f(int *X, int Y) {
Y[X] = 0;
}
به شکل زیر تبدیل میشود:
void f(int *X, int Y) {
X[Y] = 0;
}
- این بررسی به دلایل خوانایی درباره چنین نحو نامتعارفی هشدار میدهد:
- برنامهنویسانی هستند که با این نحو نامتعارف آشنا نیستند.
- این احتمال وجود دارد که متغیرها به اشتباه جابهجا شده باشند.
readability-named-parameter
یافتن توابعی با آرگومانهای بدون نام.
این بررسی قاعده زیر را که برگرفته از Google C++ Style Guide است پیادهسازی میکند:
https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions
تمام پارامترها باید در اعلان و تعریف تابع نام یکسانی داشته باشند. اگر از یک پارامتر استفاده نمیشود، میتوان نام آن را در تعریف تابع کامنت کرد.
int doingSomething(int a, int b, int c);
int doingSomething(int a, int b, int /*c*/) {
// درست: از پارامتر سوم استفاده نشده است
return a + b;
}
نام بررسی متناظر در cpplint.py: readability/function.
گزینهها
- InsertPlainNamesInForwardDecls
- اگر روی true تنظیم شود، این بررسی نام پارامترها را تنها برای پیشاعلانها (forward declarations) بدون کامنت درج میکند. در غیر این صورت، این بررسی نام پارامترها را بهصورت کامنت (مانند /*param*/) درج خواهد کرد. پیشفرض false است.
readability-non-const-parameter
این بررسی پارامترهایی از توابع را که از نوع اشارهگر هستند و میتوانند به جای آن به یک نوع ثابت (constant) اشاره کنند پیدا میکند.
هنگامی که const بهدرستی استفاده شود، میتوان از بسیاری از خطاها جلوگیری کرد. مزایای استفاده صحیح از const:
- جلوگیری از تغییر ناخواسته دادهها؛
- دریافت هشدارهای اضافی مانند استفاده از دادههای مقداردهینشده؛
- آسانتر کردن مشاهده عوارض جانبی (side effects) احتمالی برای توسعهدهندگان.
این بررسی سختگیری زیادی درباره ثابتبودن (constness) ندارد، و تنها زمانی هشدار میدهد که ثابتبودن باعث ایمنتر شدن رابط تابع شود.
// در اینجا هشدار میدهد؛ اعلان "const char *p" رابط تابع را
// ایمنتر میکند.
char f1(char *p) {
return *p;
}
// بدون هشدار؛ اعلان میتوانست بیشتر ثابت باشد "const int * const p" اما
// این کار رابط تابع را ایمنتر نمیکند.
int f2(const int *p) {
return *p;
}
// بدون هشدار؛ ثابت کردن x رابط تابع را ایمنتر نمیکند
int f3(int x) {
return x;
}
// بدون هشدار؛ از نظر فنی، *p میتواند const باشد ("const struct S *p"). اما const کردن
// *p میتواند گمراهکننده باشد. ممکن است افراد فکر کنند که ارسال دادههای
// const به این تابع ایمن است.
struct S { int *a; int *b; };
int f3(struct S *p) {
*(p->a) = 0;
}
// بدون هشدار؛ به p توسط یک lvalue ارجاع داده شده است.
void f4(int *p) {
int &x = *p;
}
readability-operators-representation
نمایش یکپارچه نشانهها (tokens) را برای عملگرهای دودویی، یگانی و سربارگذاریشدهی فراخوانیشده در کد ++C اعمال میکند. این بررسی هم از نمایشهای سنتی و هم از نمایشهای جایگزین عملگرها پشتیبانی میکند، مانند && و and، || و or، و غیره.
در قلمرو برنامهنویسی ++C، توسعهدهندگان این امکان را دارند که بین دو نمایش متمایز برای عملگرها انتخاب کنند: نمایش نشانهای سنتی و نمایش نشانهای جایگزین. نشانههای سنتی از نمادها استفاده میکنند، مانند &&، || و !، در حالی که نشانههای جایگزین واژههای توصیفیتری مانند and، or و not را به کار میگیرند.
در جدول نگاشت زیر، فهرست جامعی از نشانههای سنتی و جایگزین، به همراه نمایشهای متناظر آنها ارائه شده است:
جدول نگاشت نمایش نشانهها
| سنتی | جایگزین |
| && | and |
| &= | and_eq |
| & | bitand |
| | | bitor |
| ~ | compl |
| ! | not |
| != | not_eq |
| || | or |
| |= | or_eq |
| ^ | xor |
| ^= | xor_eq |
مثال
// Traditional Token Representation:
if (!a||!b)
{
// do something
}
// Alternative Token Representation:
if (not a or not b)
{
// do something
}
گزینهها
به دلیل مزایا و معایب متمایز هر نمایش، پیکربندی پیشفرض هیچیک را اجباری نمیکند. پیکربندی صریح لازم است.
برای پیکربندی بررسی جهت اجباری کردن نمایش نشانهای سنتی برای همه عملگرها، گزینهها را روی &&;&=;&;|;~;!;!=;||;|=;^;^= تنظیم کنید.
برای پیکربندی بررسی جهت اجباری کردن نمایش نشانهای جایگزین برای همه عملگرها، گزینهها را روی and;and_eq;bitand;bitor;compl;not;not_eq;or;or_eq;xor;xor_eq تنظیم کنید.
توسعهدهندگان نیازی به اجباری کردن همه عملگرها ندارند، و میتوانند نمایشها را بر اساس تمایل با تعیین فهرستی جداشده با نقطه ویرگول (semicolon) از هر دو نشانههای سنتی و جایگزین در پیکربندی ترکیب کنند، مانند and;||;not.
- BinaryOperators
- این گزینه به شما اجازه میدهد فهرستی جداشده با نقطه ویرگول از عملگرهای دودویی را مشخص کنید که میخواهید نمایش نشانهای خاصی برای آنها اعمال شود. مقدار پیشفرض رشته خالی است.
- OverloadedOperators
- این گزینه به شما اجازه میدهد فهرستی جداشده با نقطه ویرگول از عملگرهای سربارگذاریشده را مشخص کنید که میخواهید نمایش نشانهای خاصی برای آنها اعمال شود. مقدار پیشفرض رشته خالی است.
readability-qualified-auto
توصیفکنندههای اشارهگر (pointer qualifications) را به متغیرهای با نوع auto که به عنوان اشارهگر استنتاج میشوند اضافه میکند.
استانداردهای کدنویسی LLVM https://llvm.org/docs/CodingStandards.html#beware-unnecessary-copies-with-auto توصیه میکند که در صورت اشارهگر بودن یک متغیر با نوع auto، این موضوع آشکار شود. این بررسی هنگامی که نوع به صورت یک اشارهگر استنتاج شود، auto را به auto * تبدیل میکند.
for (auto Data : MutatablePtrContainer) {
change(*Data);
}
for (auto Data : ConstantPtrContainer) {
observe(*Data);
}
تبدیل میشود به:
for (auto *Data : MutatablePtrContainer) {
change(*Data);
}
for (const auto *Data : ConstantPtrContainer) {
observe(*Data);
}
توجه داشته باشید که انواع توصیفشده با const volatile توصیفکنندههای const و volatile خود را حفظ خواهند کرد. اشارهگرها به اشارهگرها بهطور کامل توصیف نخواهند شد.
const auto Foo = cast<int *>(Baz1); const auto Bar = cast<const int *>(Baz2); volatile auto FooBar = cast<int *>(Baz3); auto BarFoo = cast<int **>(Baz4);
تبدیل میشود به:
auto *const Foo = cast<int *>(Baz1); const auto *const Bar = cast<const int *>(Baz2); auto *volatile FooBar = cast<int *>(Baz3); auto *BarFoo = cast<int **>(Baz4);
گزینهها
- AddConstToQualified
- هنگامی که روی true تنظیم شود، این بررسی در صورت امکان توصیفکنندههای const را به متغیرهای تعریفشده به عنوان auto * یا auto & اضافه میکند. مقدار پیشفرض true است.
auto Foo1 = cast<const int *>(Bar1); auto *Foo2 = cast<const int *>(Bar2); auto &Foo3 = cast<const int &>(Bar3);
اگر AddConstToQualified روی false تنظیم شده باشد، به صورت زیر تبدیل خواهد شد:
const auto *Foo1 = cast<const int *>(Bar1); auto *Foo2 = cast<const int *>(Bar2); auto &Foo3 = cast<const int &>(Bar3);
در غیر این صورت به صورت زیر تبدیل خواهد شد:
const auto *Foo1 = cast<const int *>(Bar1); const auto *Foo2 = cast<const int *>(Bar2); const auto &Foo3 = cast<const int &>(Bar3);
توجه داشته باشید که در نام مستعار (alias) مربوط به LLVM، مقدار پیشفرض false است.
- AllowedTypes
- فهرستی از نامهای انواع (types) جداشده با نقطهویرگول (semicolon) برای نادیده گرفتن هنگامی که auto به آن نوع یا اشارهگری به آن نوع استنتاج میشود. توجه داشته باشید که این گزینه بین نامهای مستعار نوع (type aliases) و نوع اصلی تفاوت قائل میشود؛ بنابراین مشخص کردن مثلاً my_int مانع از گزارشها دربارهٔ int نخواهد شد، حتی اگر به عنوان یک نام مستعار typedef برای int تعریف شده باشد. عبارتهای منظم (regex) پذیرفته میشوند؛ برای نمونه [Rr]ef(erence)?$ با هر نوعی با پسوند Ref، ref، Reference و reference مطابقت دارد. اگر نامی در فهرست شامل توالی :: باشد، با نام کامل و واجد شرایط نوع (یعنی namespace::Type) مطابقت داده میشود؛ در غیر این صورت تنها با نام نوع (یعنی Type) تطبیق داده خواهد شد. به عنوان مثال، برای متوقف کردن گزارشها برای تکرارکنندههای std::array از رشتهٔ std::array<.*>::(const_)?iterator استفاده کنید. مقدار پیشفرض یک رشتهٔ خالی است.
- IgnoreAliasing
- اگر روی true تنظیم شود، بررسی از نوع زیربنایی برای تعیین نوعی که auto به آن استنتاج میشود استفاده خواهد کرد. اگر روی false تنظیم شود، بررسی به فراتر از نخستین نام مستعار نوع نگاه نخواهد کرد. مقدار پیشفرض true است.
using IntPtr = int*; IntPtr foo(); auto bar = foo();
اگر IgnoreAliasing روی true تنظیم شده باشد، به صورت زیر تبدیل خواهد شد:
auto *bar = foo();
در غیر این صورت هیچ تغییری رخ نخواهد داد.
محدودیتها
هنگامی که IgnoreAliasing روی false تنظیم شده است، مواردی وجود دارد که در آنها Clang نام مستعار نوع را حفظ نکرده است و نوع زیربنایی استفاده خواهد شد؛ بنابراین ممکن است موارد مثبت کاذب رخ دهد.
برای مثال:
using IntPtr = int *;
void loopPtr(const std::vector<IntPtr> &VectorIntPtr) {
// May fail for IgnoreAliasing==false as AST does not have the 'IntPtr'
for (auto Data : VectorIntPtr) {
}
}
readability-redundant-access-specifiers
کلاسها (classes)، ساختارها (structs) و یونیونهایی (unions) را پیدا میکند که شامل مشخصکنندههای دسترسی عضو (فیلد و متد) تکراری و زائد هستند.
مثال
class Foo {
public:
int x;
int y;
public:
int z;
protected:
int a;
public:
int c;
}
در مثال بالا، اعلان دوم public میتواند بدون هیچگونه تغییری در رفتار حذف شود.
گزینهها
- CheckFirstDeclaration
- اگر روی true تنظیم شود، بررسی همچنین زائد بودن نخستین اعلان مشخصکنندهٔ دسترسی را تشخیص میدهد (برای نمونه private در داخل class، یا public در داخل struct یا union). پیشفرض false است.
مثال
struct Bar {
public:
int x;
}
اگر گزینهٔ CheckFirstDeclaration فعال باشد، هشداری دربارهٔ مشخصکنندهٔ دسترسی زائد صادر خواهد شد، زیرا public دسترسی پیشفرض اعضا برای ساختارها (structs) است.
readability-redundant-casting
عملیات تبدیل نوع (type casting) صریح که دارای نوع مبدأ و مقصد یکسان هستند را شناسایی کرده و متعاقباً حذف آنها را پیشنهاد میدهد. طیف وسیعی از تبدیلهای صریح، از جمله static_cast، const_cast، تبدیلهای سبک C و reinterpret_cast را پوشش میدهد. هدف اصلی آن بهبود خوانایی و قابلیت نگهداری کد از طریق حذف تبدیلهای نوع غیرضروری است.
int value = 42; int result = static_cast<int>(value);
در این مثال، عبارت static_cast<int>(value) زائد است، چرا که تبدیلی از نوع int به یک int دیگر را انجام میدهد.
عملیات تبدیل شامل تبدیلهای سازنده (constructor conversions)، تبدیلهای تعریفشده توسط کاربر (user-defined conversions)، تبدیلهای تابعی (functional casts)، تبدیلهای وابسته به نوع (type-dependent casts)، تبدیل بین نامهای مستعار نوع (type aliases) متمایز که به یک نوع پایهای یکسان اشاره دارند، و همچنین تبدیلهای مربوط به میدان بیتی (bitfield) و تبدیلهای مستقیم از lvalue به rvalue، همگی توسط این بررسی نادیده گرفته میشوند.
گزینهها
- IgnoreMacros
- در صورت تنظیم روی true، این بررسی هشداری درون ماکروها صادر نخواهد کرد. مقدار پیشفرض true است.
- IgnoreTypeAliases
- در صورت تنظیم روی false، این بررسی نامهای مستعار نوع را در نظر میگیرد و هنگامی که روی true تنظیم شود، تمام نامهای مستعار نوع را حل کرده و روی نوعهای زیرین عمل میکند. مقدار پیشفرض false است.
readability-redundant-control-flow
این بررسی رویههایی (توابعی بدون مقدار بازگشتی) را که دارای دستور return در انتهای تابع هستند جستجو میکند. چنین دستورات return ای زائد هستند.
دستورات حلقه (for، while، do while) نیز برای دستورات زائد continue در انتهای بدنه حلقه بررسی میشوند.
مثالها:
تابع f زیر شامل یک دستور زائد return است:
extern void g();
void f() {
g();
return;
}
تبدیل میشود به:
extern void g();
void f() {
g();
}
تابع k زیر شامل یک دستور زائد continue است:
void k() {
for (int i = 0; i < 10; ++i) {
continue;
}
}
تبدیل میشود به:
void k() {
for (int i = 0; i < 10; ++i) {
}
}
readability-redundant-declaration
اعلانهای زائد متغیرها و توابع را پیدا میکند.
extern int X; extern int X;
تبدیل میشود به:
extern int X;
چنین اعلانهای زائدی را میتوان بدون تغییر در رفتار برنامه حذف کرد. آنها ممکن است به عنوان مثال، بازماندههای ناخواستهای از بازآفرینیهای کد (refactorings) قبلی هنگام جابهجایی کدها باشند. داشتن اعلانهای زائد در بدترین حالت میتواند به این معنی باشد که غلطهای املایی در کد وجود دارد که باعث بروز باگ میشوند.
به طور معمول کد میتواند به صورت خودکار اصلاح شود؛ clang-tidy میتواند اعلان دوم را حذف کند. با این حال، در ۲ مورد نیاز است که کد را به صورت دستی اصلاح کنید:
- زمانی که اعلانها در فایلهای هدر متفاوتی قرار دارند؛
- زمانی که چندین متغیر با هم اعلان شدهاند.
گزینهها
- IgnoreMacros
- در صورت تنظیم روی true، این بررسی هشداری درون ماکروها صادر نخواهد کرد. مقدار پیشفرض true است.
readability-redundant-function-ptr-dereference
ارجاعزداییهای (dereference) زائد اشارهگر تابع را پیدا میکند.
قبل:
int f(int,int); int (*p)(int, int) = &f; int i = (**p)(10, 50);
بعد:
int f(int,int); int (*p)(int, int) = &f; int i = (*p)(10, 50);
readability-redundant-inline-specifier
مشخصکنندههای افزونه (redundant) inline را در اعلانهای توابع و متغیرها شناسایی میکند.
مثالها:
constexpr inline void f() {}
در مثال بالا، کلمه کلیدی inline افزونه است زیرا توابع constexpr بهطور ضمنی inline هستند.
class MyClass {
inline void myMethod() {}
};
در مثال بالا، کلمه کلیدی inline افزونه است زیرا توابع عضوی که بهطور کامل درون تعریف یک class/struct/union تعریف میشوند بهطور ضمنی inline هستند.
گزینهها
- StrictMode
- اگر روی true تنظیم شود، این بررسی توابع و متغیرهایی را که از قبل پیوند درونی (internal linkage) دارند نیز به عنوان افزونه علامتگذاری میکند. پیشفرض false است.
readability-redundant-member-init
مقداردهیهای اولیهای از اعضا را پیدا میکند که غیرضروری هستند، زیرا در صورت عدم وجود آنها نیز همان سازنده پیشفرض فراخوانی میشد.
مثال
// Explicitly initializing the member s and v is unnecessary.
class Foo {
public:
Foo() : s() {}
private:
std::string s;
std::vector<int> v {};
};
گزینهها
- IgnoreBaseInCopyConstructors
- پیشفرض false
است.
هنگامی که true باشد، این بررسی مقداردهیهای اولیه غیرضروری کلاس پایه را در سازندههای کپی (copy constructors) نادیده میگیرد، چرا که برخی کامپایلرها در صورت عدم مقداردهی اولیه صریح کلاسهای پایه در سازندههای کپی، هشدار/خطا صادر میکنند. برای مثال، gcc همراه با -Wextra یا -Werror=extra، در صورتی که Bar() در مثال زیر حذف شود، هشدار یا خطای base class 'Bar' should be explicitly initialized in the copy constructor را صادر میکند:
// Explicitly initializing member s and base class Bar is unnecessary.
struct Foo : public Bar {
// Remove s() below. If IgnoreBaseInCopyConstructors!=0, keep Bar().
Foo(const Foo& foo) : Bar(), s() {}
std::string s;
};
readability-redundant-parentheses
پرانتزهای افزونه را شناسایی میکند.
هنگام تغییر کد، اغلب افراد حذف پرانتزهای مربوطه را فراموش میکنند. این امر منجر به طولانی شدن بیش از حد کد میشود. زمانی که عبارت پیچیده باشد، پیدا کردن پرانتزهای متناظر بهویژه دشوار میگردد.
مثال
(1);
((a + 2)) * 3;
(a);
("aaa");
در حال حاضر این بررسی تقدم عملگرها را در نظر نمیگیرد؛ حتی اگر عبارت درون پرانتز اولویت بالاتری نسبت به بیرون پرانتز داشته باشد. به عبارت دیگر، حذف پرانتزها تأثیری بر معنای کد نخواهد داشت.
int a = (1 * 2) + 3; // no warning
گزینهها
- AllowedDecls
- فهرستی از
عبارات
باقاعده (regular
expressions) جداشده
با
نقطهویرگول
که نام
اعلانهای
مورد نظر
برای
نادیده
گرفتن در
زمان
قرارگیری
پرانتز دور
آنها را
مطابقت
میدهد.
اعلانها
میتوانند
شامل
متغیرها یا
توابع
باشند.
مقدار
پیشفرض
std::max;std::min است.
برخی از توابع کتابخانه STL ممکن است همنام با ماکروهای شبهتابعی پرکاربرد باشند؛ برای نمونه، تابع std::max و ماکروی max. یک راهکار برای متمایز ساختن آنها، قرار دادن پرانتز دور توابع جهت جلوگیری از بسط ماکروی شبهتابعی است.
readability-redundant-preprocessor
دستورات پیشپردازنده بالقوه افزونه را پیدا میکند. در حال حاضر موارد زیر شناسایی میشوند:
- •
- جفتهای #ifdef .. #endif که درون یک جفت بیرونی با همان شرط تو در تو شدهاند. برای مثال:
#ifdef FOO #ifdef FOO // inner ifdef is considered redundant void f(); #endif #endif
- •
- همین حالت برای جفتهای #ifndef .. #endif. برای مثال:
#ifndef FOO #ifndef FOO // inner ifndef is considered redundant void f(); #endif #endif
- •
- دستور #ifndef درون یک #ifdef با همان شرط:
#ifdef FOO #ifndef FOO // inner ifndef is considered redundant void f(); #endif #endif
- •
- دستور #ifdef درون یک #ifndef با همان شرط:
#ifndef FOO #ifdef FOO // inner ifdef is considered redundant void f(); #endif #endif
- •
- جفتهای #if .. #endif که درون یک جفت بیرونی با همان شرط تو در تو شدهاند. برای مثال:
#define FOO 4 #if FOO == 4 #if FOO == 4 // inner if is considered redundant void f(); #endif #endif
readability-redundant-smartptr-get
فراخوانیهای زائد متد .get() از اشارهگرهای هوشمند را یافته و حذف میکند.
مثالها:
ptr.get()->Foo() ==> ptr->Foo() *ptr.get() ==> *ptr *ptr->get() ==> **ptr if (ptr.get() == nullptr) ... => if (ptr == nullptr) ...
گزینهها
- IgnoreMacros
- اگر این گزینه روی true تنظیم شود (پیشفرض true است)، بررسی درباره فراخوانیهای درون ماکروها هشدار نخواهد داد.
readability-redundant-string-cstr
فراخوانیهای غیرضروری به std::string::c_str() و std::string::data() را مییابد.
گزینهها
- StringParameterFunctions
- فهرستی جداشده با نقطه-ویرگول از عبارات باقاعده (regex) که با نامهای (کاملاً واجد شرایطِ) توابع/متدها/عملگرها مطابقت دارند؛ با این شرط که هر پارامتری که در حال حاضر ورودی const char* میپذیرد باید بتواند ورودیهای std::string را نیز بپذیرد، یا کاندیداهای بارگذاری بیش از حد (overload) مناسبی که قادر به انجام این کار باشند وجود داشته باشند. از این گزینه میتوان برای پیکربندی توابعی مانند fmt::format، spdlog::logger::info یا توابع پوشاننده پیرامون این موارد و توابع مشابه استفاده کرد. مقدار پیشفرض یک رشته خالی است.
readability-redundant-string-init
مقداردهیهای اولیه غیرضروری رشتهها را مییابد.
مثالها
// مقداردهی اولیه رشته با لیترال رشته خالی غیرضروری است.
std::string a = "";
std::string b("");
// تبدیل میشود به
std::string a;
std::string b;
// مقداردهی اولیه یک string_view با یک لیترال رشته خالی، نمونهای
// ایجاد میکند که با ()string_view برابر است.
std::string_view a = "";
std::string_view b("");
// تبدیل میشود به
std::string_view a;
std::string_view b;
گزینهها
- StringNames
- پیشفرض
::std::basic_string;::std::basic_string_view
است.
فهرستی جداشده با نقطه-ویرگول از نام کلاسها جهت اعمال این بررسی بر روی آنها. بهطور پیشفرض ::std::basic_string بر روی std::string و std::wstring اعمال میشود. برای اجرای این بررسی بر روی کلاسهای سفارشی، آن را مثلاً به ::std::basic_string;llvm::StringRef;QString تنظیم کنید.
readability-redundant-typename
استفادههای زائد از کلمه کلیدی typename را مییابد.
کلمه کلیدی typename در دو حالت زائد است. نخست، قبل از نامهای غیروابسته (non-dependent):
/*typename*/ std::vector<int>::size_type size;
و دوم، از C++20 به بعد، قبل از نامهای وابسته که در زمینهای ظاهر میشوند که در آنجا فقط یک نوع مجاز است (مثال زیر تنها چند مورد از آنها را نشان میدهد):
template <typename T>
using trait = /*typename*/ T::type;
template <typename T>
/*typename*/ T::underlying_type as_underlying(T n) {
return static_cast</*typename*/ T::underlying_type>(n);
}
template <typename T>
struct S {
/*typename*/ T::type variable;
/*typename*/ T::type function(/*typename*/ T::type);
};
readability-reference-to-constructed-temporary
کدهای C++ را شناسایی میکند که در آنها از یک متغیر ارجاعی (reference) برای تمدید طول عمر یک شیء موقت که بهتازگی ساخته شده، استفاده شده است.
این نوع ساختار اغلب نتیجه بازسازیهای مکرر کد یا کمبود دانش توسعهدهنده است که منجر به سردرگمی یا باگهای نامحسوس میشود. در اکثر موارد، هدف واقعی توسعهدهنده ایجاد یک متغیر جدید بوده است نه تمدید طول عمر یک شیء موقت.
نمونههایی از کدهای مشکلساز عبارتند از:
const std::string& str("hello");
struct Point { int x; int y; };
const Point& p = { 1, 2 };
در مثال اول، به یک متغیر ارجاعی const std::string& به نام str، یک شیء موقت اختصاص داده میشود که توسط سازنده std::string("hello") ایجاد شده است. در مثال دوم، به یک متغیر ارجاعی const Point& به نام p، شیئی اختصاص مییابد که از لیست مقداردهی اولیه { 1, 2 } ساخته شده است. هر دوی این مثالها طول عمر شیء موقت را به طول عمر متغیر ارجاعی تمدید میکنند، که میتواند تحلیل رفتار کد را دشوار ساخته و منجر به باگهای نامحسوس یا کجفهمی شود.
برای جلوگیری از این مشکلات، توصیه میشود متغیر ارجاعی را به یک متغیر مقداری (const) تغییر دهید.
readability-simplify-boolean-expr
عبارات بولی شامل ثابتهای بولی را جستجو کرده و آنها را به شکلی ساده میکند که مستقیماً از عبارت بولی مناسب استفاده شود. عبارات بولی را با اعمال قضیه دمورگان (DeMorgan) سادهسازی میکند.
مثالها:
| عبارت اولیه | نتیجه |
| if (b == true) | if (b) |
| if (b == false) | if (!b) |
| if (b && true) | if (b) |
| if (b && false) | if (false) |
| if (b || true) | if (true) |
| if (b || false) | if (b) |
| e ? true : false | e |
| e ? false : true | !e |
| if (true) t(); else f(); | t(); |
| if (false) t(); else f(); | f(); |
| if (e) return true; else return false; | return e; |
| if (e) return false; else return true; | return !e; |
| if (e) b = true; else b = false; | b = e; |
| if (e) b = false; else b = true; | b = !e; |
| if (e) return true; return false; | return e; |
| if (e) return false; return true; | return !e; |
| !(!a || b) | a && !b |
| !(a || !b) | !a && b |
| !(!a || !b) | a && b |
| !(!a && b) | a || !b |
| !(a && !b) | !a || b |
| !(!a && !b) | a || b |
- عبارت حاصل e به شرح زیر تغییر مییابد:
- 1.
- پرانتزهای غیرضروری در اطراف عبارت حذف میشوند.
- 2.
- اعمال نقیض ! برداشته میشوند.
- 3.
- اعمال نقیض عملگرهای مقایسهای به استفاده از شرط معکوس تغییر مییابند.
- 4.
- تبدیلهای ضمنی اشارهگرها، شامل اشارهگرها به اعضا، به bool با مقایسههای صریح با nullptr در C++11 یا NULL در C++98/03 جایگزین میشوند.
- 5.
- تبدیلهای نوع ضمنی (cast) به bool با تبدیلهای صریح به bool جایگزین میشوند.
- 6.
- عبارات شیء دارای عملگرهای تبدیل explicit operator bool با تبدیلهای صریح نوع به bool جایگزین میشوند.
- 7.
- تبدیلهای ضمنی انواع عدد صحیح (integral) به bool با مقایسههای صریح با 0 جایگزین میشوند.
- مثالها:
- 1.
- انتساب سهتایی bool b = (i < 0) ? true : false; دارای پرانتزهای زائد است و به bool b = i < 0; تبدیل میشود.
- 2.
- بازگشت شرطی if (!b) return false; return true; دارای نفی مضاعف ضمنی است و به return b; تبدیل میشود.
- 3.
- بازگشت
شرطی if (i < 0) return false; return
true; به return i >= 0;
تبدیل
میشود.
بازگشت شرطی if (i != 0) return false; return true; به return i == 0; تبدیل میشود.
- 4.
- بازگشت
شرطی if (p) return true; return false;
دارای یک
تبدیل ضمنی
اشارهگر
به bool است و
به return p != nullptr;
تبدیل
میشود.
انتساب سهتایی bool b = (i & 1) ? true : false; دارای یک تبدیل ضمنی i & 1 به bool است و به bool b = (i & 1) != 0; تبدیل میشود.
- 5.
- بازگشت شرطی if (i & 1) return true; else return false; دارای تبدیل ضمنی مقدار عدد صحیح i & 1 به bool است و به return (i & 1) != 0; تبدیل میشود.
- 6.
- با فرض struct X { explicit operator bool(); }; و نمونهای از آن به نام x از struct X، بازگشت شرطی if (x) return true; return false; به return static_cast<bool>(x); تبدیل میشود.
گزینهها
- IgnoreMacros
- اگر true باشد، از عبارات بولی که ناشی از گسترش ماکروها هستند چشمپوشی میکند. پیشفرض false است.
- ChainedConditionalReturn
- اگر true باشد، دستورات بازگشت بولی شرطی در انتهای زنجیره if/else if دگرگون میشوند. پیشفرض false است.
- ChainedConditionalAssignment
- اگر true باشد، انتسابهای بولی شرطی در انتهای یک زنجیره if/else if دگرگون میشوند. پیشفرض false است.
- SimplifyDeMorgan
- اگر true باشد، قضیه دمرگان برای سادهسازی عطفها و فصلهای منفیشده اعمال خواهد شد. پیشفرض true است.
- SimplifyDeMorganRelaxed
- اگر true
باشد،
گزینه SimplifyDeMorgan
عطفها و
فصلهای
نقیضشده
را حتی
زمانی که
هیچکدام
از
عملوندها
منفی نشده
باشند نیز
دگرگون
میکند. این
گزینه اگر
SimplifyDeMorgan مقدار
false داشته
باشد اثری
ندارد.
پیشفرض false
است.
در صورت فعال بودن:
bool X = !(A && B) bool Y = !(A || B)
تبدیل میشود به:
bool X = !A || !B bool Y = !A && !B
readability-simplify-subscript-expr
این بررسی عبارات اندیس (subscript) را سادهسازی میکند. در حال حاضر این بررسی مواردی را پوشش میدهد که .data() فراخوانی شده و بلافاصله عملیات اندیس آرایه برای دریافت یک تک عنصر روی آن انجام میگیرد، که در این حالت فراخوانی مستقیم operator[] کفایت میکند.
مثالها:
std::string s = ...; char c = s.data()[i]; // char c = s[i];
گزینهها
- Types
- فهرستی از انواع داده که این بررسی را فعال میکنند. پیشفرض عبارت است از ::std::basic_string;::std::basic_string_view;::std::vector;::std::array;::std::span
readability-static-accessed-through-instance
عبارات عضوی را که به اعضای ایستا (static) از طریق نمونهها دسترسی دارند بررسی کرده و آنها را با استفاده از شناسه واجد شرایط (qualified-id) مناسب جایگزین میکند.
مثال:
کد زیر:
struct C {
static void foo();
static int x;
enum { E1 };
enum E { E2 };
};
C *c1 = new C();
c1->foo();
c1->x;
c1->E1;
c1->E2;
تغییر مییابد به:
C *c1 = new C(); C::foo(); C::x; C::E1; C::E2;
گزینه خط فرمان --fix پشتیبانی پیشفرضی برای اصلاحات ایمن فراهم میکند، در حالی که --fix-notes اصلاحاتی را فعال میکند که ممکن است عبارات دارای اثرات جانبی (side effects) را جایگزین کنند، که به طور بالقوه میتواند رفتار برنامه را تغییر دهد.
readability-static-definition-in-anonymous-namespace
تعریفهای توابع و متغیرهای static را در فضای نام بینام (anonymous namespace) پیدا میکند.
در این حالت، static زائد است؛ زیرا فضای نام بینام، دیدهشدن (visibility) تعاریف را به یک واحد ترجمه (translation unit) واحد محدود میکند.
namespace {
static int a = 1; // Warning.
static const int b = 1; // Warning.
namespace inner {
static int c = 1; // Warning.
}
}
این بررسی با حذف توصیفکننده زائد static، یک اصلاح را اعمال میکند.
readability-string-compare
مقایسههای رشتهای را که از متد compare استفاده میکنند، پیدا میکند.
یک اشتباه رایج استفاده از متد compare رشته به جای استفاده از عملگرهای برابری یا نابرابری است. متد compare برای توابع مرتبسازی در نظر گرفته شده است و بنابراین بسته به رابطه الفبایی بین رشتههای مقایسهشده، یک عدد منفی، یک عدد مثبت یا صفر را برمیگرداند. اگر بررسی برابری یا نابرابری کفایت کند، استفاده از آن توصیه میشود. این کار به منظور جلوگیری از خطر تفسیر نادرست مقدار بازگشتی و سادهسازی کد توصیه میشود. همچنین عملگرهای برابری و نابرابری رشته میتوانند به دلیل خاتمه زودهنگام (early termination)، از متد compare سریعتر باشند.
مثال
// The same rules apply to std::string_view.
std::string str1{"a"};
std::string str2{"b"};
// use str1 != str2 instead.
if (str1.compare(str2)) {
}
// use str1 == str2 instead.
if (!str1.compare(str2)) {
}
// use str1 == str2 instead.
if (str1.compare(str2) == 0) {
}
// use str1 != str2 instead.
if (str1.compare(str2) != 0) {
}
// use str1 == str2 instead.
if (0 == str1.compare(str2)) {
}
// use str1 != str2 instead.
if (0 != str1.compare(str2)) {
}
// Use str1 == "foo" instead.
if (str1.compare("foo") == 0) {
}
نمونه کدهای بالا فهرستی از دستورهای if را نشان میدهند که این بررسی برای آنها هشدار صادر میکند. همه آنها به جای استفاده از عملگرهای صحیح، از compare برای بررسی برابری یا نابرابری دو رشته استفاده میکنند..SS گزینهها
- StringLikeClasses
- رشتهای شامل نامهای کلاسهای شبیه به رشته (string-like) که با نقطه-ویرگول از هم جدا شدهاند. بهطور پیشفرض تنها شامل ::std::basic_string و ::std::basic_string_view است. اگر کلاسی از این فهرست دارای متد compare مشابه با متد مربوطه در std::string باشد، به همان شیوه بررسی خواهد شد.
مثال
struct CustomString {
public:
int compare (const CustomString& other) const;
}
CustomString str1;
CustomString str2;
// use str1 != str2 instead.
if (str1.compare(str2)) {
}
اگر StringLikeClasses حاوی CustomString باشد، این بررسی پیشنهاد میکند که compare با عملگر برابری جایگزین شود.
readability-suspicious-call-argument
فراخوانیهای تابعی را پیدا میکند که در آنها آرگومانهای ارسالی بر اساس تفاوت بین نام آرگومان و نام پارامترهای تابع، بدون رعایت ترتیب مناسب ارائه شدهاند.
با در نظر گرفتن فراخوانی تابع f(foo, bar); و امضای تابع void f(T tvar, U uvar)، آرگومانهای foo و bar جابهجا در نظر گرفته میشوند اگر foo (نام آرگومان) نسبت به tvar (پارامتری که در حال حاضر به آن ارسال شده است) شباهت بیشتری به uvar (پارامتر دیگر) داشته باشد و bar نسبت به uvar شباهت بیشتری به tvar داشته باشد.
هشدارها ممکن است نشاندهندهٔ این باشند که یا آرگومانها جابهجا شدهاند، یا شباهت متقاطع نامها درک کد را مختل میکند.
روشهای اکتشافی (Heuristics)
روشهای اکتشافی زیر در این بررسی پیادهسازی شدهاند. اگر هر یک از روشهای فعالشده تشخیص دهند که آرگومانها بدون رعایت ترتیب ارائه شدهاند، یک هشدار صادر خواهد شد.
خود روشهای اکتشافی با در نظر گرفتن جفترشتهها پیادهسازی شدهاند و متقارن هستند، بنابراین در ادامه تمایزی در مورد اینکه کدام رشته نام آرگومان و کدام رشته نام پارامتر است وجود ندارد.
برابری (Equality)
سادهترین روش اکتشافی که دو رشته را برای برابری بدون حساسیت به حروف کوچک و بزرگ مقایسه میکند.
مخففسازی (Abbreviation)
میتوان مخففهای رایج را مشخص کرد تا در صورتی که شکل مخفف و عبارت اصلی در کنار یکدیگر قرار گیرند، رشتهها مشابه در نظر گرفته شوند. به عنوان مثال، اگر src به عنوان مخفف source ثبت شده باشد، در مورد نمونه کد زیر هشدار داده خواهد شد.
void foo(int source, int x); foo(b, src);
مخففهایی که باید شناسایی شوند را میتوان با گزینهٔ بررسی Abbreviations پیکربندی کرد. این روش اکتشافی به حروف کوچک و بزرگ حساس نیست.
پیشوند (Prefix)
روش اکتشافی prefix در صورتی گزارش میدهد که یکی از رشتهها یک پیشوند بهاندازهٔ کافی طولانی از رشتهٔ دیگر باشد، مانند target نسبت به targetPtr. درصد شباهت، نسبت طول پیشوند به رشتهٔ طولانیتر است؛ در مثال قبلی، این مقدار 6 / 9 = 66.66...% خواهد بود.
این روش اکتشافی را میتوان با محدودهها (bounds) پیکربندی کرد. محدودههای پیشفرض عبارتند از: زیر 25% غیرمشابه و بالای 30% مشابه. این روش اکتشافی به حروف کوچک و بزرگ حساس نیست.
پسوند (Suffix)
مشابه با روش اکتشافی Prefix. در صورت مقایسهٔ oldValue و value، درصد شباهت برابر با 8 / 5 = 62.5% خواهد بود.
این روش اکتشافی را میتوان با محدودهها پیکربندی کرد. محدودههای پیشفرض عبارتند از: زیر 25% غیرمشابه و بالای 30% مشابه. این روش اکتشافی به حروف کوچک و بزرگ حساس نیست.
زیررشته (Substring)
روش اکتشافی زیررشته، روشهای پیشوند و پسوند را ترکیب کرده و تلاش میکند تا طولانیترین زیررشتهٔ مشترک را در دو رشتهٔ دادهشده پیدا کند. درصد شباهت، نسبت طولانیترین زیررشتهٔ مشترکِ یافتشده به رشتهٔ طولانیتر از میان دو رشتهٔ ورودی است. به عنوان مثال، برای val و rvalue، شباهت 3 / 6 = 50% است. اگر هیچ نویسهٔ مشترکی در دو رشته وجود نداشته باشد، این مقدار 0% خواهد بود.
این روش اکتشافی را میتوان با محدودهها پیکربندی کرد. محدودههای پیشفرض عبارتند از: زیر 40% غیرمشابه و بالای 50% مشابه. این روش اکتشافی به حروف کوچک و بزرگ حساس نیست.
فاصله لوناشتاین (به عنوان Levenshtein)
فاصله لوناشتاین http://en.wikipedia.org/wiki/Levenshtein_distance بیانگر این است که چه تعداد تغییر تکنویسهای (افزودن، تغییر یا حذف) باید اعمال شود تا یک رشته به رشتهای دیگر تبدیل گردد.
فاصله لوناشتاین با تقسیم آن بر طول رشتهٔ طولانیتر و گرفتن متمم آن نسبت به 100%، به درصد شباهت تبدیل میشود. به عنوان مثال، برای something و anything، فاصله برابر با 4 ویرایش است و درصد شباهت برابر است با 100% - 4 / 9 = 55.55...%.
این روش اکتشافی را میتوان با محدودهها پیکربندی کرد. محدودههای پیشفرض عبارتند از: زیر 50% غیرمشابه و بالای 66% مشابه. این روش اکتشافی به حروف کوچک و بزرگ حساس است.
فاصله جارو-وینکلر (به عنوان JaroWinkler)
فاصله جارو-وینکلر <http://en.wikipedia.org/wiki/Jaro–Winkler_distance> یک فاصله ویرایشی مانند فاصله لوناشتاین است. این معیار از روی تعداد نویسههای مشترکی که از نظر موقعیت به اندازهٔ کافی به یکدیگر نزدیک هستند و نویسههایی که باید تغییر کنند، محاسبه میشود. تعریف اولیه جارو توسط وینکلر گسترش یافته است تا به شباهتهای پیشوند وزن بیشتری داده شود. درصد شباهت به صورت میانگین نویسههای مشترک و غیرمشترک نسبت به طول هر دو رشته بیان میشود.
این روش اکتشافی را میتوان با محدودهها پیکربندی کرد. محدودههای پیشفرض عبارتند از: زیر 75% غیرمشابه و بالای 85% مشابه. این روش اکتشافی به حروف کوچک و بزرگ حساس نیست.
ضریب سورنسن-دایس (به عنوان Dice)
ضریب سورنسن-دایس <http://en.wikipedia.org/wiki/Sørensen–Dice_coefficient> در ابتدا برای اندازهگیری شباهت دو مجموعه تعریف شد. بهطور رسمی، این ضریب از تقسیم 2 * #(اشتراک) بر #(مجموعه۱) + #(مجموعه۲) محاسبه میشود، که در آن #() تابع اندازه (کاردینالیتی) مجموعهها است. این معیار با ایجاد دوگرامها (bigrams - توالیهای زیررشتهای به طول ۲) از دو رشته و استفاده از مجموعهٔ دوگرامهای دو رشته به عنوان دو مجموعه، بر روی رشتهها اعمال میشود.
این روش اکتشافی را میتوان با محدودهها پیکربندی کرد. محدودههای پیشفرض عبارتند از: زیر 60% غیرمشابه و بالای 70% مشابه. این روش اکتشافی به حروف کوچک و بزرگ حساس نیست.
گزینهها
- MinimumIdentifierNameLength
- حداقل طول مورد نیاز برای نامهای شناسه و پارامتر را تعیین میکند. نامهای کوتاهتر از این طول نادیده گرفته خواهند شد. پیشفرض 3 است.
- Abbreviations
- برای روش
ابتکاری (heuristic)
Abbreviation (اینجا
را ببینید)،
این گزینه
اختصارات
را در قالب
"abbreviation=abbreviated_value"
پیکربندی
میکند. این
گزینه یک
رشته است که
مقادیر آن
با ";" از
یکدیگر جدا
میشوند.
بهطور پیشفرض، اختصارات زیر تنظیم شدهاند:
- addr=address
- arr=array
- attr=attribute
- buf=buffer
- cl=client
- cnt=count
- col=column
- cpy=copy
- dest=destination
- dist=distance
- dst=distance
- elem=element
- hght=height
- i=index
- idx=index
- len=length
- ln=line
- lst=list
- nr=number
- num=number
- pos=position
- ptr=pointer
- ref=reference
- src=source
- srv=server
- stmt=statement
- str=string
- val=value
- var=variable
- vec=vector
- wdth=width
گزینههای پیکربندی برای هر روش ابتکاریِ پیادهسازیشده (بالا را ببینید) بهصورت پویا ساخته میشوند. در ادامه، <HeuristicName> به یکی از کلیدهای روشهای ابتکاری پیادهسازیشده اشاره دارد.
- <HeuristicName>
- True یا False،
برای تعیین
فعال بودن
یا نبودن یک
روش
ابتکاری
خاص مانند
Equality یا Levenshtein.
پیشفرض برای هر روش ابتکاری True است.
- <HeuristicName>DissimilarBelow, <HeuristicName>SimilarAbove
- مقداری بین
0 و 100 که
بیانگر
درصد است.
این
محدودهها
مشخص
میکنند که
روش
ابتکاری
باید چه
درصدی از
شباهت را
استنتاج
کند تا دو
شناسه توسط
این بررسی،
مشابه یا
نامشابه در
نظر گرفته
شوند.
با فرض آرگومانهای arg1 و arg2 که بهترتیب به param1 و param2 ارسال شدهاند، بررسی مرزها به این صورت انجام میشود: اگر شباهت ترتیب آرگومان فعلی ارسالشده (arg1 به param1) کمتر از آستانه DissimilarBelow باشد، و شباهت ترتیب جابهجا شدهی پیشنهادی (arg1 به param2) بیشتر از آستانه SimilarAbove باشد، جابهجایی گزارش میشود.
برای مقادیر پیشفرض هر روش ابتکاری، بالا را ببینید.
ساخت نام (Name synthesis)
هنگام مقایسه نامهای آرگومان و پارامتر، از منطق زیر برای جمعآوری نامها جهت مقایسه استفاده میشود:
نامهای پارامتر همان شناسههایی هستند که در کد منبع نوشته شدهاند.
نامهای آرگومان عبارتند از:
- در صورت ارسال یک متغیر، نام آن متغیر.
- در صورتی که مقدار بازگشتی حاصل از فراخوانی یک تابع بعدی به عنوان آرگومان استفاده شود، نام تابع فراخوانیشده.
- در غیر این صورت، یک رشته خالی.
نامهای خالی آرگومان یا پارامتر توسط روشهای ابتکاری نادیده گرفته میشوند.
readability-uniqueptr-delete-release
جایگزینی delete <unique_ptr>.release() با <unique_ptr> = nullptr. روش دوم کوتاهتر و سادهتر است و نیازی به استفاده از APIهای اشارهگر خام ندارد.
std::unique_ptr<int> P; delete P.release(); // becomes std::unique_ptr<int> P; P = nullptr;
گزینهها
- PreferResetCall
- اگر true باشد، بازنویسی با فراخوانی تابع عضو reset بهجای انتساب به nullptr انجام میشود. مقدار پیشفرض false است.
std::unique_ptr<int> P; delete P.release(); // becomes std::unique_ptr<int> P; P.reset();
readability-uppercase-literal-suffix
بررسی cert-dcl16-c بهعنوان یک نام مستعار برای این بررسی به اینجا هدایت میشود. بهطور پیشفرض، این نام مستعار تنها پسوندهایی را که با l آغاز میشوند (l، ll، lu، llu، اما نه u، ul، ull) مورد عیبیابی قرار میدهد.
بررسی hicpp-uppercase-literal-suffix بهعنوان یک نام مستعار برای این بررسی به اینجا هدایت میشود.
مواردی را که لیترال عددی صحیح یا ممیز شناور (دهدهی یا هگزادسیمال) دارای پسوند غیرحروفبزرگ است شناسایی کرده و یک راهنمای اصلاح (fix-it hint) با پسوند حروف بزرگ ارائه میدهد.
تمام ترکیبهای معتبر پسوندها پشتیبانی میشوند.
auto x = 1; // OK, no suffix. auto x = 1u; // warning: integer literal suffix 'u' is not upper-case auto x = 1U; // OK, suffix is uppercase. ...
گزینهها
- NewSuffixes
- بهصورت اختیاری، میتوان فهرستی از پسوندهای مقصد را مشخص کرد. هنگامی که پسوند پیدا شود، یک جستجوی بدون حساسیت به حروف کوچک و بزرگ در آن فهرست انجام میگیرد، و اگر جایگزینی پیدا شود که با پسوند فعلی تفاوت داشته باشد، پیام عیبیابی صادر میشود. این امر امکان کنترل دقیق بر روی پسوندهای مد نظر و جایگزینهای آنها را فراهم میکند.
مثال
با فرض وجود فهرست L;uL:
- l -> L
- L بدون تغییر باقی میماند.
- ul -> uL
- Ul -> uL
- UL -> uL
- uL بدون تغییر باقی میماند.
- ull بدون تغییر باقی میماند، زیرا در فهرست نیست
- و به همین ترتیب.
- IgnoreMacros
- اگر این گزینه روی true تنظیم شود (پیشفرض true است)، بررسی در مورد پسوندهای لیترال داخل ماکروها هشداری نمیدهد.
readability-use-anyofallof
حلقههای for مبتنی بر دامنه (range-based) را که میتوان با فراخوانی std::any_of یا std::all_of جایگزین کرد پیدا میکند. در حالت C++20، استفاده از std::ranges::any_of یا std::ranges::all_of را پیشنهاد میدهد.
مثال:
bool all_even(std::vector<int> V) {
for (int I : V) {
if (I % 2)
return false;
}
return true;
// Replace loop by
// return std::ranges::all_of(V, [](int I) { return I % 2 == 0; });
}
readability-use-concise-preprocessor-directives
موارد استفاده از #if را که میتوانند به #ifdef یا #ifndef سادهسازی شوند، و از C23 و C++23 به بعد، موارد استفاده از #elif را که میتوانند به #elifdef یا #elifndef سادهسازی شوند پیدا میکند:
#if defined(MEOW) #if !defined(MEOW) // becomes #ifdef MEOW #ifndef MEOW
از C23 و C++23 به بعد:
#elif defined(MEOW) #elif !defined(MEOW) // becomes #elifdef MEOW #elifndef MEOW
readability-use-std-min-max
دستورات شرطی خاصی را با فراخوانیهای معادل std::min یا std::max جایگزین میکند. نکته: این مورد ممکن است بهدلیل احتمال ذخیرهسازیهای بیشتر در حافظه (stores) در مقایسه با دستور if اولیه، روی عملکرد در کدهای حساس تأثیر بگذارد.
قبل:
void foo() {
int a = 2, b = 3;
if (a < b)
a = b;
}
بعد:
void foo() {
int a = 2, b = 3;
a = std::max(a, b);
}
zircon-temporary-objects
نکته:
| نام | ارائه اصلاحات |
| abseil-cleanup-ctad <> | بله |
| abseil-duration-addition <> | بله |
| abseil-duration-comparison <> | بله |
| abseil-duration-conversion-cast <> | بله |
| abseil-duration-division <> | بله |
| abseil-duration-factory-float <> | بله |
| abseil-duration-factory-scale <> | بله |
| abseil-duration-subtraction <> | بله |
| abseil-duration-unnecessary-conversion <> | بله |
| abseil-faster-strsplit-delimiter <> | بله |
| abseil-no-internal-dependencies <> | |
| abseil-no-namespace <> | |
| abseil-redundant-strcat-calls <> | بله |
| abseil-str-cat-append <> | بله |
| abseil-string-find-startswith <> | بله |
| abseil-string-find-str-contains <> | بله |
| abseil-time-comparison <> | بله |
| abseil-time-subtraction <> | بله |
| abseil-unchecked-statusor-access <> | |
| abseil-upgrade-duration-conversions <> | بله |
| altera-id-dependent-backward-branch <> | |
| altera-kernel-name-restriction <> | |
| altera-single-work-item-barrier <> | |
| altera-struct-pack-align <> | بله |
| altera-unroll-loops <> | |
| android-cloexec-accept <> | بله |
| android-cloexec-accept4 <> | بله |
| android-cloexec-creat <> | بله |
| android-cloexec-dup <> | بله |
| android-cloexec-epoll-create <> | بله |
| android-cloexec-epoll-create1 <> | بله |
| android-cloexec-fopen <> | بله |
| android-cloexec-inotify-init <> | بله |
| android-cloexec-inotify-init1 <> | بله |
| android-cloexec-memfd-create <> | بله |
| android-cloexec-open <> | بله |
| android-cloexec-pipe <> | بله |
| android-cloexec-pipe2 <> | بله |
| android-cloexec-socket <> | بله |
| android-comparison-in-temp-failure-retry <> | |
| boost-use-ranges <> | بله |
| boost-use-to-string <> | بله |
| bugprone-argument-comment <> | بله |
| bugprone-assert-side-effect <> | |
| bugprone-assignment-in-if-condition <> | |
| bugprone-bad-signal-to-kill-thread <> | |
| bugprone-bitwise-pointer-cast <> | |
| bugprone-bool-pointer-implicit-conversion <> | بله |
| bugprone-branch-clone <> | |
| bugprone-capturing-this-in-member-variable <> | |
| bugprone-casting-through-void <> | |
| bugprone-chained-comparison <> | |
| bugprone-command-processor <> | |
| bugprone-compare-pointer-to-member-virtual-function <> | |
| bugprone-copy-constructor-init <> | بله |
| bugprone-copy-constructor-mutates-argument <> | |
| bugprone-crtp-constructor-accessibility <> | بله |
| bugprone-dangling-handle <> | |
| bugprone-default-operator-new-on-overaligned-type <> | |
| bugprone-derived-method-shadowing-base-method <> | |
| bugprone-dynamic-static-initializers <> | |
| bugprone-easily-swappable-parameters <> | |
| bugprone-empty-catch <> | |
| bugprone-exception-copy-constructor-throws <> | |
| bugprone-exception-escape <> | |
| bugprone-float-loop-counter <> | |
| bugprone-fold-init-type <> | |
| bugprone-forward-declaration-namespace <> | |
| bugprone-forwarding-reference-overload <> | |
| bugprone-implicit-widening-of-multiplication-result <> | بله |
| bugprone-inaccurate-erase <> | بله |
| bugprone-inc-dec-in-conditions <> | |
| bugprone-incorrect-enable-if <> | بله |
| bugprone-incorrect-enable-shared-from-this <> | بله |
| bugprone-incorrect-roundings <> | |
| bugprone-infinite-loop <> | |
| bugprone-integer-division <> | |
| bugprone-invalid-enum-default-initialization <> | |
| bugprone-lambda-function-name <> | |
| bugprone-macro-parentheses <> | بله |
| bugprone-macro-repeated-side-effects <> | |
| bugprone-misleading-setter-of-reference <> | |
| bugprone-misplaced-operator-in-strlen-in-alloc <> | بله |
| bugprone-misplaced-pointer-arithmetic-in-alloc <> | بله |
| bugprone-misplaced-widening-cast <> | |
| bugprone-move-forwarding-reference <> | بله |
| bugprone-multi-level-implicit-pointer-conversion <> | |
| bugprone-multiple-new-in-one-expression <> | |
| bugprone-multiple-statement-macro <> | |
| bugprone-narrowing-conversions <> | |
| bugprone-no-escape <> | |
| bugprone-non-zero-enum-to-bool-conversion <> | |
| bugprone-nondeterministic-pointer-iteration-order <> | |
| bugprone-not-null-terminated-result <> | بله |
| bugprone-optional-value-conversion <> | بله |
| bugprone-parent-virtual-call <> | بله |
| bugprone-pointer-arithmetic-on-polymorphic-object <> | |
| bugprone-posix-return <> | بله |
| bugprone-random-generator-seed <> | |
| bugprone-raw-memory-call-on-non-trivial-type <> | |
| bugprone-redundant-branch-condition <> | بله |
| bugprone-reserved-identifier <> | بله |
| bugprone-return-const-ref-from-parameter <> | |
| bugprone-shared-ptr-array-mismatch <> | بله |
| bugprone-signal-handler <> | |
| bugprone-signed-char-misuse <> | |
| bugprone-sizeof-container <> | |
| bugprone-sizeof-expression <> | |
| bugprone-spuriously-wake-up-functions <> | |
| bugprone-standalone-empty <> | بله |
| bugprone-std-namespace-modification <> | |
| bugprone-string-constructor <> | بله |
| bugprone-string-integer-assignment <> | بله |
| bugprone-string-literal-with-embedded-nul <> | |
| bugprone-stringview-nullptr <> | بله |
| bugprone-suspicious-enum-usage <> | |
| bugprone-suspicious-include <> | |
| bugprone-suspicious-memory-comparison <> | |
| bugprone-suspicious-memset-usage <> | بله |
| bugprone-suspicious-missing-comma <> | |
| bugprone-suspicious-realloc-usage <> | |
| bugprone-suspicious-semicolon <> | بله |
| bugprone-suspicious-string-compare <> | بله |
| bugprone-suspicious-stringview-data-usage <> | |
| bugprone-swapped-arguments <> | بله |
| bugprone-switch-missing-default-case <> | |
| bugprone-tagged-union-member-count <> | |
| bugprone-terminating-continue <> | بله |
| bugprone-throw-keyword-missing <> | |
| bugprone-throwing-static-initialization <> | |
| bugprone-too-small-loop-variable <> | |
| bugprone-unchecked-optional-access <> | |
| bugprone-unchecked-string-to-number-conversion <> | |
| bugprone-undefined-memory-manipulation <> | |
| bugprone-undelegated-constructor <> | |
| bugprone-unhandled-exception-at-new <> | |
| bugprone-unhandled-self-assignment <> | |
| bugprone-unintended-char-ostream-output <> | بله |
| bugprone-unique-ptr-array-mismatch <> | بله |
| bugprone-unsafe-functions <> | |
| bugprone-unused-local-non-trivial-variable <> | |
| bugprone-unused-raii <> | بله |
| bugprone-unused-return-value <> | |
| bugprone-use-after-move <> | |
| bugprone-virtual-near-miss <> | بله |
| cert-err33-c <> | |
| cert-err60-cpp <> | |
| cert-flp30-c <> | |
| cert-msc50-cpp <> | |
| cert-oop58-cpp <> | |
| concurrency-mt-unsafe <> | |
| concurrency-thread-canceltype-asynchronous <> | |
| cppcoreguidelines-avoid-capturing-lambda-coroutines <> | |
| cppcoreguidelines-avoid-const-or-ref-data-members <> | |
| cppcoreguidelines-avoid-do-while <> | |
| cppcoreguidelines-avoid-goto <> | |
| cppcoreguidelines-avoid-non-const-global-variables <> | |
| cppcoreguidelines-avoid-reference-coroutine-parameters <> | |
| cppcoreguidelines-init-variables <> | بله |
| cppcoreguidelines-interfaces-global-init <> | |
| cppcoreguidelines-macro-usage <> | |
| cppcoreguidelines-misleading-capture-default-by-value <> | بله |
| cppcoreguidelines-missing-std-forward <> | |
| cppcoreguidelines-no-malloc <> | |
| cppcoreguidelines-no-suspend-with-lock <> | |
| cppcoreguidelines-owning-memory <> | |
| cppcoreguidelines-prefer-member-initializer <> | بله |
| cppcoreguidelines-pro-bounds-array-to-pointer-decay <> | |
| cppcoreguidelines-pro-bounds-avoid-unchecked-container-access <> | بله |
| cppcoreguidelines-pro-bounds-constant-array-index <> | بله |
| cppcoreguidelines-pro-bounds-pointer-arithmetic <> | |
| cppcoreguidelines-pro-type-const-cast <> | |
| cppcoreguidelines-pro-type-cstyle-cast <> | بله |
| cppcoreguidelines-pro-type-member-init <> | بله |
| cppcoreguidelines-pro-type-reinterpret-cast <> | |
| cppcoreguidelines-pro-type-static-cast-downcast <> | بله |
| cppcoreguidelines-pro-type-union-access <> | |
| cppcoreguidelines-pro-type-vararg <> | |
| cppcoreguidelines-rvalue-reference-param-not-moved <> | |
| cppcoreguidelines-slicing <> | |
| cppcoreguidelines-special-member-functions <> | |
| cppcoreguidelines-use-enum-class <> | |
| cppcoreguidelines-virtual-class-destructor <> | بله |
| darwin-avoid-spinlock <> | |
| darwin-dispatch-once-nonstatic <> | بله |
| fuchsia-default-arguments-calls <> | |
| fuchsia-default-arguments-declarations <> | بله |
| fuchsia-overloaded-operator <> | |
| fuchsia-statically-constructed-objects <> | |
| fuchsia-temporary-objects <> | |
| fuchsia-trailing-return <> | |
| fuchsia-virtual-inheritance <> | |
| google-build-explicit-make-pair <> | |
| google-build-using-namespace <> | |
| google-default-arguments <> | |
| google-explicit-constructor <> | بله |
| google-global-names-in-headers <> | |
| google-objc-avoid-nsobject-new <> | |
| google-objc-avoid-throwing-exception <> | |
| google-objc-function-naming <> | |
| google-objc-global-variable-declaration <> | |
| google-readability-avoid-underscore-in-googletest-name <> | |
| google-readability-todo <> | |
| google-runtime-float <> | |
| google-runtime-int <> | |
| google-runtime-operator <> | |
| google-upgrade-googletest-case <> | بله |
| hicpp-exception-baseclass <> | |
| hicpp-ignored-remove-result <> | |
| hicpp-multiway-paths-covered <> | |
| hicpp-no-assembler <> | |
| hicpp-signed-bitwise <> | |
| linuxkernel-must-check-errs <> | |
| llvm-header-guard <> | |
| llvm-include-order <> | بله |
| llvm-namespace-comment <> | |
| llvm-prefer-isa-or-dyn-cast-in-conditionals <> | بله |
| llvm-prefer-register-over-unsigned <> | بله |
| llvm-prefer-static-over-anonymous-namespace <> | |
| llvm-twine-local <> | بله |
| llvm-use-new-mlir-op-builder <> | بله |
| llvm-use-ranges <> | بله |
| llvmlibc-callee-namespace <> | |
| llvmlibc-implementation-in-namespace <> | |
| llvmlibc-inline-function-decl <> | بله |
| llvmlibc-restrict-system-libc-headers <> | بله |
| misc-anonymous-namespace-in-header <> | |
| misc-confusable-identifiers <> | |
| misc-const-correctness <> | بله |
| misc-coroutine-hostile-raii <> | |
| misc-definitions-in-headers <> | بله |
| misc-header-include-cycle <> | |
| misc-include-cleaner <> | بله |
| misc-misleading-bidirectional <> | |
| misc-misleading-identifier <> | |
| misc-misplaced-const <> | |
| misc-multiple-inheritance <> | |
| misc-new-delete-overloads <> | |
| misc-no-recursion <> | |
| misc-non-copyable-objects <> | |
| misc-non-private-member-variables-in-classes <> | |
| misc-override-with-different-visibility <> | |
| misc-predictable-rand <> | |
| misc-redundant-expression <> | بله |
| misc-static-assert <> | بله |
| misc-throw-by-value-catch-by-reference <> | |
| misc-unconventional-assign-operator <> | |
| misc-uniqueptr-reset-release <> | بله |
| misc-unused-alias-decls <> | بله |
| misc-unused-parameters <> | بله |
| misc-unused-using-decls <> | بله |
| misc-use-anonymous-namespace <> | |
| misc-use-internal-linkage <> | بله |
| modernize-avoid-bind <> | بله |
| modernize-avoid-c-arrays <> | |
| modernize-avoid-c-style-cast <> | |
| modernize-avoid-setjmp-longjmp <> | |
| modernize-avoid-variadic-functions <> | |
| modernize-concat-nested-namespaces <> | بله |
| modernize-deprecated-headers <> | بله |
| modernize-deprecated-ios-base-aliases <> | بله |
| modernize-loop-convert <> | بله |
| modernize-macro-to-enum <> | بله |
| modernize-make-shared <> | بله |
| modernize-make-unique <> | بله |
| modernize-min-max-use-initializer-list <> | بله |
| modernize-pass-by-value <> | بله |
| modernize-raw-string-literal <> | بله |
| modernize-redundant-void-arg <> | بله |
| modernize-replace-auto-ptr <> | بله |
| modernize-replace-disallow-copy-and-assign-macro <> | بله |
| modernize-replace-random-shuffle <> | بله |
| modernize-return-braced-init-list <> | بله |
| modernize-shrink-to-fit <> | بله |
| modernize-type-traits <> | بله |
| modernize-unary-static-assert <> | بله |
| modernize-use-auto <> | بله |
| modernize-use-bool-literals <> | بله |
| modernize-use-constraints <> | بله |
| modernize-use-default-member-init <> | بله |
| modernize-use-designated-initializers <> | بله |
| modernize-use-emplace <> | بله |
| modernize-use-equals-default <> | بله |
| modernize-use-equals-delete <> | بله |
| modernize-use-integer-sign-comparison <> | بله |
| modernize-use-nodiscard <> | بله |
| modernize-use-noexcept <> | بله |
| modernize-use-nullptr <> | بله |
| modernize-use-override <> | بله |
| modernize-use-ranges <> | بله |
| modernize-use-scoped-lock <> | بله |
| modernize-use-starts-ends-with <> | بله |
| modernize-use-std-format <> | بله |
| modernize-use-std-numbers <> | بله |
| modernize-use-std-print <> | بله |
| modernize-use-trailing-return-type <> | بله |
| modernize-use-transparent-functors <> | بله |
| modernize-use-uncaught-exceptions <> | بله |
| modernize-use-using <> | بله |
| mpi-buffer-deref <> | بله |
| mpi-type-mismatch <> | بله |
| objc-assert-equals <> | بله |
| objc-avoid-nserror-init <> | |
| objc-dealloc-in-category <> | |
| objc-forbidden-subclassing <> | |
| objc-missing-hash <> | |
| objc-nsdate-formatter <> | |
| objc-nsinvocation-argument-lifetime <> | بله |
| objc-property-declaration <> | بله |
| objc-super-self <> | بله |
| openmp-exception-escape <> | |
| openmp-use-default-none <> | |
| performance-avoid-endl <> | بله |
| performance-enum-size <> | |
| performance-faster-string-find <> | بله |
| performance-for-range-copy <> | بله |
| performance-implicit-conversion-in-loop <> | |
| performance-inefficient-algorithm <> | بله |
| performance-inefficient-string-concatenation <> | |
| performance-inefficient-vector-operation <> | بله |
| performance-move-const-arg <> | بله |
| performance-move-constructor-init <> | |
| performance-no-automatic-move <> | |
| performance-no-int-to-ptr <> | |
| performance-noexcept-destructor <> | بله |
| performance-noexcept-move-constructor <> | بله |
| performance-noexcept-swap <> | بله |
| performance-trivially-destructible <> | بله |
| performance-type-promotion-in-math-fn <> | بله |
| performance-unnecessary-copy-initialization <> | بله |
| performance-unnecessary-value-param <> | بله |
| portability-avoid-pragma-once <> | |
| portability-restrict-system-includes <> | بله |
| portability-simd-intrinsics <> | |
| portability-std-allocator-const <> | |
| portability-template-virtual-member-function <> | |
| readability-ambiguous-smartptr-reset-call <> | بله |
| readability-avoid-const-params-in-decls <> | بله |
| readability-avoid-nested-conditional-operator <> | |
| readability-avoid-return-with-void-value <> | بله |
| readability-avoid-unconditional-preprocessor-if <> | |
| readability-braces-around-statements <> | بله |
| readability-const-return-type <> | بله |
| readability-container-contains <> | بله |
| readability-container-data-pointer <> | بله |
| readability-container-size-empty <> | بله |
| readability-convert-member-functions-to-static <> | بله |
| readability-delete-null-pointer <> | بله |
| readability-duplicate-include <> | بله |
| readability-else-after-return <> | بله |
| readability-enum-initial-value <> | بله |
| readability-function-cognitive-complexity <> | |
| readability-function-size <> | |
| readability-identifier-length <> | |
| readability-identifier-naming <> | بله |
| readability-implicit-bool-conversion <> | بله |
| readability-inconsistent-declaration-parameter-name <> | بله |
| readability-inconsistent-ifelse-braces <> | بله |
| readability-isolate-declaration <> | بله |
| readability-magic-numbers <> | |
| readability-make-member-function-const <> | بله |
| readability-math-missing-parentheses <> | بله |
| readability-misleading-indentation <> | |
| readability-misplaced-array-index <> | بله |
| readability-named-parameter <> | بله |
| readability-non-const-parameter <> | بله |
| readability-operators-representation <> | بله |
| readability-qualified-auto <> | بله |
| readability-redundant-access-specifiers <> | بله |
| readability-redundant-casting <> | بله |
| readability-redundant-control-flow <> | بله |
| readability-redundant-declaration <> | بله |
| readability-redundant-function-ptr-dereference <> | بله |
| readability-redundant-inline-specifier <> | بله |
| readability-redundant-member-init <> | بله |
| readability-redundant-parentheses <> | بله |
| readability-redundant-preprocessor <> | |
| readability-redundant-smartptr-get <> | بله |
| readability-redundant-string-cstr <> | بله |
| readability-redundant-string-init <> | بله |
| readability-redundant-typename <> | بله |
| readability-reference-to-constructed-temporary <> | |
| readability-simplify-boolean-expr <> | بله |
| readability-simplify-subscript-expr <> | بله |
| readability-static-accessed-through-instance <> | بله |
| readability-static-definition-in-anonymous-namespace <> | بله |
| readability-string-compare <> | بله |
| readability-suspicious-call-argument <> | |
| readability-uniqueptr-delete-release <> | بله |
| readability-uppercase-literal-suffix <> | بله |
| readability-use-anyofallof <> | |
| readability-use-concise-preprocessor-directives <> | بله |
| readability-use-std-min-max <> | بله |
| zircon-temporary-objects <> |
نامهای مستعار بررسیها (Check aliases)
| نام | تغییر مسیر | ارائه اصلاحات |
| cert-arr39-c <> | bugprone-sizeof-expression <> | |
| cert-con36-c <> | bugprone-spuriously-wake-up-functions <> | |
| cert-con54-cpp <> | bugprone-spuriously-wake-up-functions <> | |
| cert-ctr56-cpp <> | bugprone-pointer-arithmetic-on-polymorphic-object <> | |
| cert-dcl03-c <> | misc-static-assert <> | بله |
| cert-dcl16-c <> | readability-uppercase-literal-suffix <> | بله |
| cert-dcl37-c <> | bugprone-reserved-identifier <> | بله |
| cert-dcl50-cpp <> | modernize-avoid-variadic-functions <> | |
| cert-dcl51-cpp <> | bugprone-reserved-identifier <> | بله |
| cert-dcl54-cpp <> | misc-new-delete-overloads <> | |
| cert-dcl58-cpp <> | bugprone-std-namespace-modification <> | |
| cert-dcl59-cpp <> | misc-anonymous-namespace-in-header <> | |
| cert-env33-c <> | bugprone-command-processor <> | |
| cert-err09-cpp <> | misc-throw-by-value-catch-by-reference <> | |
| cert-err34-c <> | bugprone-unchecked-string-to-number-conversion <> | |
| cert-err52-cpp <> | modernize-avoid-setjmp-longjmp <> | |
| cert-err58-cpp <> | bugprone-throwing-static-initialization <> | |
| cert-err60-cpp <> | bugprone-exception-copy-constructor-throws <> | |
| cert-err61-cpp <> | misc-throw-by-value-catch-by-reference <> | |
| cert-exp42-c <> | bugprone-suspicious-memory-comparison <> | |
| cert-fio38-c <> | misc-non-copyable-objects <> | |
| cert-flp30-c <> | bugprone-float-loop-counter <> | |
| cert-flp37-c <> | bugprone-suspicious-memory-comparison <> | |
| cert-int09-c <> | readability-enum-initial-value <> | بله |
| cert-mem57-cpp <> | bugprone-default-operator-new-on-overaligned-type <> | |
| cert-msc24-c <> | bugprone-unsafe-functions <> | |
| cert-msc30-c <> | misc-predictable-rand <> | |
| cert-msc32-c <> | bugprone-random-generator-seed <> | |
| cert-msc33-c <> | bugprone-unsafe-functions <> | |
| cert-msc50-cpp <> | misc-predictable-rand <> | |
| cert-msc51-cpp <> | bugprone-random-generator-seed <> | |
| cert-msc54-cpp <> | bugprone-signal-handler <> | |
| cert-oop11-cpp <> | performance-move-constructor-init <> | |
| cert-oop54-cpp <> | bugprone-unhandled-self-assignment <> | |
| cert-oop57-cpp <> | bugprone-raw-memory-call-on-non-trivial-type <> | |
| cert-oop58-cpp <> | bugprone-copy-constructor-mutates-argument <> | |
| cert-pos44-c <> | bugprone-bad-signal-to-kill-thread <> | |
| cert-pos47-c <> | concurrency-thread-canceltype-asynchronous <> | |
| cert-sig30-c <> | bugprone-signal-handler <> | |
| cert-str34-c <> | bugprone-signed-char-misuse <> | |
| clang-analyzer-core.BitwiseShift <> | Clang Static Analyzer core.BitwiseShift https://clang.llvm.org/docs/analyzer/checkers.html#core-bitwiseshift | |
| clang-analyzer-core.CallAndMessage <> | Clang Static Analyzer core.CallAndMessage https://clang.llvm.org/docs/analyzer/checkers.html#core-callandmessage | |
| clang-analyzer-core.DivideZero <> | Clang Static Analyzer core.DivideZero https://clang.llvm.org/docs/analyzer/checkers.html#core-dividezero | |
| clang-analyzer-core.NonNullParamChecker <> | Clang Static Analyzer core.NonNullParamChecker https://clang.llvm.org/docs/analyzer/checkers.html#core-nonnullparamchecker | |
| clang-analyzer-core.NullDereference <> | Clang Static Analyzer core.NullDereference https://clang.llvm.org/docs/analyzer/checkers.html#core-nulldereference | |
| clang-analyzer-core.StackAddressEscape <> | Clang Static Analyzer core.StackAddressEscape https://clang.llvm.org/docs/analyzer/checkers.html#core-stackaddressescape | |
| clang-analyzer-core.UndefinedBinaryOperatorResult <> | Clang Static Analyzer core.UndefinedBinaryOperatorResult https://clang.llvm.org/docs/analyzer/checkers.html#core-undefinedbinaryoperatorresult | |
| clang-analyzer-core.VLASize <> | Clang Static Analyzer core.VLASize https://clang.llvm.org/docs/analyzer/checkers.html#core-vlasize | |
| clang-analyzer-core.uninitialized.ArraySubscript <> | Clang Static Analyzer core.uninitialized.ArraySubscript https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-arraysubscript | |
| clang-analyzer-core.uninitialized.Assign <> | Clang Static Analyzer core.uninitialized.Assign https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-assign | |
| clang-analyzer-core.uninitialized.Branch <> | Clang Static Analyzer core.uninitialized.Branch https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-branch | |
| clang-analyzer-core.uninitialized.CapturedBlockVariable <> | Clang Static Analyzer core.uninitialized.CapturedBlockVariable https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-capturedblockvariable | |
| clang-analyzer-core.uninitialized.NewArraySize <> | Clang Static Analyzer core.uninitialized.NewArraySize https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-newarraysize | |
| clang-analyzer-core.uninitialized.UndefReturn <> | Clang Static Analyzer core.uninitialized.UndefReturn https://clang.llvm.org/docs/analyzer/checkers.html#core-uninitialized-undefreturn | |
| clang-analyzer-cplusplus.ArrayDelete <> | Clang Static Analyzer cplusplus.ArrayDelete https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-arraydelete | |
| clang-analyzer-cplusplus.InnerPointer <> | Clang Static Analyzer cplusplus.InnerPointer https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-innerpointer | |
| clang-analyzer-cplusplus.Move <> | Clang Static Analyzer cplusplus.Move https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-move | |
| clang-analyzer-cplusplus.NewDelete <> | Clang Static Analyzer cplusplus.NewDelete https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdelete | |
| clang-analyzer-cplusplus.NewDeleteLeaks <> | Clang Static Analyzer cplusplus.NewDeleteLeaks https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-newdeleteleaks | |
| clang-analyzer-cplusplus.PlacementNew <> | Clang Static Analyzer cplusplus.PlacementNew https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-placementnew | |
| clang-analyzer-cplusplus.SelfAssignment <> | Clang Static Analyzer cplusplus.SelfAssignment https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-selfassignment | |
| clang-analyzer-cplusplus.StringChecker <> | Clang Static Analyzer cplusplus.StringChecker https://clang.llvm.org/docs/analyzer/checkers.html#cplusplus-stringchecker | |
| clang-analyzer-deadcode.DeadStores <> | Clang Static Analyzer deadcode.DeadStores https://clang.llvm.org/docs/analyzer/checkers.html#deadcode-deadstores | |
| clang-analyzer-fuchsia.HandleChecker <> | Clang Static Analyzer fuchsia.HandleChecker https://clang.llvm.org/docs/analyzer/checkers.html#fuchsia-handlechecker | |
| clang-analyzer-nullability.NullPassedToNonnull <> | Clang Static Analyzer nullability.NullPassedToNonnull https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullpassedtononnull | |
| clang-analyzer-nullability.NullReturnedFromNonnull <> | Clang Static Analyzer nullability.NullReturnedFromNonnull https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullreturnedfromnonnull | |
| clang-analyzer-nullability.NullableDereferenced <> | Clang Static Analyzer nullability.NullableDereferenced https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullabledereferenced | |
| clang-analyzer-nullability.NullablePassedToNonnull <> | Clang Static Analyzer nullability.NullablePassedToNonnull https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullablepassedtononnull | |
| clang-analyzer-nullability.NullableReturnedFromNonnull <> | Clang Static Analyzer nullability.NullableReturnedFromNonnull https://clang.llvm.org/docs/analyzer/checkers.html#nullability-nullablereturnedfromnonnull | |
| clang-analyzer-optin.core.EnumCastOutOfRange <> | Clang Static Analyzer optin.core.EnumCastOutOfRange https://clang.llvm.org/docs/analyzer/checkers.html#optin-core-enumcastoutofrange | |
| clang-analyzer-optin.cplusplus.UninitializedObject <> | Clang Static Analyzer optin.cplusplus.UninitializedObject https://clang.llvm.org/docs/analyzer/checkers.html#optin-cplusplus-uninitializedobject | |
| clang-analyzer-optin.cplusplus.VirtualCall <> | Clang Static Analyzer optin.cplusplus.VirtualCall https://clang.llvm.org/docs/analyzer/checkers.html#optin-cplusplus-virtualcall | |
| clang-analyzer-optin.mpi.MPI-Checker <> | Clang Static Analyzer optin.mpi.MPI-Checker https://clang.llvm.org/docs/analyzer/checkers.html#optin-mpi-mpi-checker | |
| clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker <> | Clang Static Analyzer optin.osx.cocoa.localizability.EmptyLocalizationContextChecker https://clang.llvm.org/docs/analyzer/checkers.html#optin-osx-cocoa-localizability-emptylocalizationcontextchecker | |
| clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker <> | Clang Static Analyzer optin.osx.cocoa.localizability.NonLocalizedStringChecker https://clang.llvm.org/docs/analyzer/checkers.html#optin-osx-cocoa-localizability-nonlocalizedstringchecker | |
| clang-analyzer-optin.performance.GCDAntipattern <> | Clang Static Analyzer optin.performance.GCDAntipattern https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-gcdantipattern | |
| clang-analyzer-optin.performance.Padding <> | Clang Static Analyzer optin.performance.Padding https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-padding | |
| clang-analyzer-optin.portability.UnixAPI <> | Clang Static Analyzer optin.portability.UnixAPI https://clang.llvm.org/docs/analyzer/checkers.html#optin-portability-unixapi | |
| clang-analyzer-optin.taint.TaintedAlloc <> | Clang Static Analyzer optin.taint.TaintedAlloc https://clang.llvm.org/docs/analyzer/checkers.html#optin-taint-taintedalloc | |
| clang-analyzer-osx.API <> | Clang Static Analyzer osx.API https://clang.llvm.org/docs/analyzer/checkers.html#osx-api | |
| clang-analyzer-osx.NumberObjectConversion <> | Clang Static Analyzer osx.NumberObjectConversion https://clang.llvm.org/docs/analyzer/checkers.html#osx-numberobjectconversion | |
| clang-analyzer-osx.ObjCProperty <> | Clang Static Analyzer osx.ObjCProperty https://clang.llvm.org/docs/analyzer/checkers.html#osx-objcproperty | |
| clang-analyzer-osx.SecKeychainAPI <> | Clang Static Analyzer osx.SecKeychainAPI https://clang.llvm.org/docs/analyzer/checkers.html#osx-seckeychainapi | |
| clang-analyzer-osx.cocoa.AtSync <> | Clang Static Analyzer osx.cocoa.AtSync https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-atsync | |
| clang-analyzer-osx.cocoa.AutoreleaseWrite <> | Clang Static Analyzer osx.cocoa.AutoreleaseWrite https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-autoreleasewrite | |
| clang-analyzer-osx.cocoa.ClassRelease <> | Clang Static Analyzer osx.cocoa.ClassRelease https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-classrelease | |
| clang-analyzer-osx.cocoa.Dealloc <> | Clang Static Analyzer osx.cocoa.Dealloc https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-dealloc | |
| clang-analyzer-osx.cocoa.IncompatibleMethodTypes <> | Clang Static Analyzer osx.cocoa.IncompatibleMethodTypes https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-incompatiblemethodtypes | |
| clang-analyzer-osx.cocoa.Loops <> | Clang Static Analyzer osx.cocoa.Loops https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-loops | |
| clang-analyzer-osx.cocoa.MissingSuperCall <> | Clang Static Analyzer osx.cocoa.MissingSuperCall https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-missingsupercall | |
| clang-analyzer-osx.cocoa.NSAutoreleasePool <> | Clang Static Analyzer osx.cocoa.NSAutoreleasePool https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nsautoreleasepool | |
| clang-analyzer-osx.cocoa.NSError <> | Clang Static Analyzer osx.cocoa.NSError https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nserror | |
| clang-analyzer-osx.cocoa.NilArg <> | Clang Static Analyzer osx.cocoa.NilArg https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nilarg | |
| clang-analyzer-osx.cocoa.NonNilReturnValue <> | Clang Static Analyzer osx.cocoa.NonNilReturnValue https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-nonnilreturnvalue | |
| clang-analyzer-osx.cocoa.ObjCGenerics <> | Clang Static Analyzer osx.cocoa.ObjCGenerics https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-objcgenerics | |
| clang-analyzer-osx.cocoa.RetainCount <> | Clang Static Analyzer osx.cocoa.RetainCount https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-retaincount | |
| clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak <> | Clang Static Analyzer osx.cocoa.RunLoopAutoreleaseLeak https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-runloopautoreleaseleak | |
| clang-analyzer-osx.cocoa.SelfInit <> | Clang Static Analyzer osx.cocoa.SelfInit https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-selfinit | |
| clang-analyzer-osx.cocoa.SuperDealloc <> | Clang Static Analyzer osx.cocoa.SuperDealloc https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-superdealloc | |
| clang-analyzer-osx.cocoa.UnusedIvars <> | Clang Static Analyzer osx.cocoa.UnusedIvars https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-unusedivars | |
| clang-analyzer-osx.cocoa.VariadicMethodTypes <> | Clang Static Analyzer osx.cocoa.VariadicMethodTypes https://clang.llvm.org/docs/analyzer/checkers.html#osx-cocoa-variadicmethodtypes | |
| clang-analyzer-osx.coreFoundation.CFError <> | Clang Static Analyzer osx.coreFoundation.CFError https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-cferror | |
| clang-analyzer-osx.coreFoundation.CFNumber <> | Clang Static Analyzer osx.coreFoundation.CFNumber https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-cfnumber | |
| clang-analyzer-osx.coreFoundation.CFRetainRelease <> | Clang Static Analyzer osx.coreFoundation.CFRetainRelease https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-cfretainrelease | |
| clang-analyzer-osx.coreFoundation.containers.OutOfBounds <> | Clang Static Analyzer osx.coreFoundation.containers.OutOfBounds https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-outofbounds | |
| clang-analyzer-osx.coreFoundation.containers.PointerSizedValues <> | Clang Static Analyzer osx.coreFoundation.containers.PointerSizedValues https://clang.llvm.org/docs/analyzer/checkers.html#osx-corefoundation-containers-pointersizedvalues | |
| clang-analyzer-security.FloatLoopCounter <> | Clang Static Analyzer security.FloatLoopCounter https://clang.llvm.org/docs/analyzer/checkers.html#security-floatloopcounter | |
| clang-analyzer-security.PutenvStackArray <> | Clang Static Analyzer security.PutenvStackArray https://clang.llvm.org/docs/analyzer/checkers.html#security-putenvstackarray-c | |
| clang-analyzer-security.SetgidSetuidOrder <> | Clang Static Analyzer security.SetgidSetuidOrder https://clang.llvm.org/docs/analyzer/checkers.html#security-setgidsetuidorder-c | |
| clang-analyzer-security.cert.env.InvalidPtr <> | Clang Static Analyzer security.cert.env.InvalidPtr https://clang.llvm.org/docs/analyzer/checkers.html#security-cert-env-invalidptr | |
| clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling <> | Clang Static Analyzer security.insecureAPI.DeprecatedOrUnsafeBufferHandling https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-deprecatedorunsafebufferhandling | |
| clang-analyzer-security.insecureAPI.UncheckedReturn <> | Clang Static Analyzer security.insecureAPI.UncheckedReturn https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-uncheckedreturn | |
| clang-analyzer-security.insecureAPI.bcmp <> | Clang Static Analyzer security.insecureAPI.bcmp https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bcmp | |
| clang-analyzer-security.insecureAPI.bcopy <> | Clang Static Analyzer security.insecureAPI.bcopy https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bcopy | |
| clang-analyzer-security.insecureAPI.bzero <> | Clang Static Analyzer security.insecureAPI.bzero https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-bzero | |
| clang-analyzer-security.insecureAPI.decodeValueOfObjCType <> | Clang Static Analyzer security.insecureAPI.decodeValueOfObjCType https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-decodevalueofobjctype | |
| clang-analyzer-security.insecureAPI.getpw <> | Clang Static Analyzer security.insecureAPI.getpw https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-getpw | |
| clang-analyzer-security.insecureAPI.gets <> | Clang Static Analyzer security.insecureAPI.gets https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-gets | |
| clang-analyzer-security.insecureAPI.mkstemp <> | Clang Static Analyzer security.insecureAPI.mkstemp https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-mkstemp | |
| clang-analyzer-security.insecureAPI.mktemp <> | Clang Static Analyzer security.insecureAPI.mktemp https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-mktemp | |
| clang-analyzer-security.insecureAPI.rand <> | Clang Static Analyzer security.insecureAPI.rand https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-rand | |
| clang-analyzer-security.insecureAPI.strcpy <> | Clang Static Analyzer security.insecureAPI.strcpy https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-strcpy | |
| clang-analyzer-security.insecureAPI.vfork <> | Clang Static Analyzer security.insecureAPI.vfork https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-vfork | |
| clang-analyzer-unix.API <> | Clang Static Analyzer unix.API https://clang.llvm.org/docs/analyzer/checkers.html#unix-api | |
| clang-analyzer-unix.BlockInCriticalSection <> | Clang Static Analyzer unix.BlockInCriticalSection https://clang.llvm.org/docs/analyzer/checkers.html#unix-blockincriticalsection | |
| clang-analyzer-unix.Errno <> | Clang Static Analyzer unix.Errno https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno | |
| clang-analyzer-unix.Malloc <> | Clang Static Analyzer unix.Malloc https://clang.llvm.org/docs/analyzer/checkers.html#unix-malloc | |
| clang-analyzer-unix.MallocSizeof <> | Clang Static Analyzer unix.MallocSizeof https://clang.llvm.org/docs/analyzer/checkers.html#unix-mallocsizeof | |
| clang-analyzer-unix.MismatchedDeallocator <> | Clang Static Analyzer unix.MismatchedDeallocator https://clang.llvm.org/docs/analyzer/checkers.html#unix-mismatcheddeallocator | |
| clang-analyzer-unix.StdCLibraryFunctions <> | Clang Static Analyzer unix.StdCLibraryFunctions https://clang.llvm.org/docs/analyzer/checkers.html#unix-stdclibraryfunctions | |
| clang-analyzer-unix.Stream <> | Clang Static Analyzer unix.Stream https://clang.llvm.org/docs/analyzer/checkers.html#unix-stream | |
| clang-analyzer-unix.Vfork <> | Clang Static Analyzer unix.Vfork https://clang.llvm.org/docs/analyzer/checkers.html#unix-vfork | |
| clang-analyzer-unix.cstring.BadSizeArg <> | Clang Static Analyzer unix.cstring.BadSizeArg https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-badsizearg | |
| clang-analyzer-unix.cstring.NullArg <> | Clang Static Analyzer unix.cstring.NullArg https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-nullarg | |
| clang-analyzer-webkit.NoUncountedMemberChecker <> | Clang Static Analyzer webkit.NoUncountedMemberChecker https://clang.llvm.org/docs/analyzer/checkers.html#webkit-nouncountedmemberchecker | |
| clang-analyzer-webkit.RefCntblBaseVirtualDtor <> | Clang Static Analyzer webkit.RefCntblBaseVirtualDtor https://clang.llvm.org/docs/analyzer/checkers.html#webkit-refcntblbasevirtualdtor | |
| clang-analyzer-webkit.UncountedLambdaCapturesChecker <> | Clang Static Analyzer webkit.UncountedLambdaCapturesChecker https://clang.llvm.org/docs/analyzer/checkers.html#webkit-uncountedlambdacaptureschecker | |
| cppcoreguidelines-avoid-c-arrays <> | modernize-avoid-c-arrays <> | |
| cppcoreguidelines-avoid-magic-numbers <> | readability-magic-numbers <> | |
| cppcoreguidelines-c-copy-assignment-signature <> | misc-unconventional-assign-operator <> | |
| cppcoreguidelines-explicit-virtual-functions <> | modernize-use-override <> | بله |
| cppcoreguidelines-macro-to-enum <> | modernize-macro-to-enum <> | بله |
| cppcoreguidelines-narrowing-conversions <> | bugprone-narrowing-conversions <> | |
| cppcoreguidelines-noexcept-destructor <> | performance-noexcept-destructor <> | بله |
| cppcoreguidelines-noexcept-move-operations <> | performance-noexcept-move-constructor <> | بله |
| cppcoreguidelines-noexcept-swap <> | performance-noexcept-swap <> | بله |
| cppcoreguidelines-non-private-member-variables-in-classes <> | misc-non-private-member-variables-in-classes <> | |
| cppcoreguidelines-use-default-member-init <> | modernize-use-default-member-init <> | بله |
| fuchsia-header-anon-namespaces <> | misc-anonymous-namespace-in-header <> | |
| fuchsia-multiple-inheritance <> | misc-multiple-inheritance <> | |
| google-build-namespaces <> | misc-anonymous-namespace-in-header <> | |
| google-readability-braces-around-statements <> | readability-braces-around-statements <> | بله |
| google-readability-casting <> | modernize-avoid-c-style-cast <> | |
| google-readability-function-size <> | readability-function-size <> | |
| google-readability-namespace-comments <> | llvm-namespace-comment <> | |
| hicpp-avoid-c-arrays <> | modernize-avoid-c-arrays <> | |
| hicpp-avoid-goto <> | cppcoreguidelines-avoid-goto <> | |
| hicpp-braces-around-statements <> | readability-braces-around-statements <> | بله |
| hicpp-deprecated-headers <> | modernize-deprecated-headers <> | بله |
| hicpp-explicit-conversions <> | google-explicit-constructor <> | بله |
| hicpp-function-size <> | readability-function-size <> | |
| hicpp-invalid-access-moved <> | bugprone-use-after-move <> | |
| hicpp-member-init <> | cppcoreguidelines-pro-type-member-init <> | بله |
| hicpp-move-const-arg <> | performance-move-const-arg <> | بله |
| hicpp-named-parameter <> | readability-named-parameter <> | بله |
| hicpp-new-delete-operators <> | misc-new-delete-overloads <> | |
| hicpp-no-array-decay <> | cppcoreguidelines-pro-bounds-array-to-pointer-decay <> | |
| hicpp-no-malloc <> | cppcoreguidelines-no-malloc <> | |
| hicpp-noexcept-move <> | performance-noexcept-move-constructor <> | بله |
| hicpp-special-member-functions <> | cppcoreguidelines-special-member-functions <> | |
| hicpp-static-assert <> | misc-static-assert <> | بله |
| hicpp-undelegated-constructor <> | bugprone-undelegated-constructor <> | |
| hicpp-uppercase-literal-suffix <> | readability-uppercase-literal-suffix <> | بله |
| hicpp-use-auto <> | modernize-use-auto <> | بله |
| hicpp-use-emplace <> | modernize-use-emplace <> | بله |
| hicpp-use-equals-default <> | modernize-use-equals-default <> | بله |
| hicpp-use-equals-delete <> | modernize-use-equals-delete <> | بله |
| hicpp-use-noexcept <> | modernize-use-noexcept <> | بله |
| hicpp-use-nullptr <> | modernize-use-nullptr <> | بله |
| hicpp-use-override <> | modernize-use-override <> | بله |
| hicpp-vararg <> | cppcoreguidelines-pro-type-vararg <> | |
| llvm-else-after-return <> | readability-else-after-return <> | بله |
| llvm-qualified-auto <> | readability-qualified-auto <> | بله |
بررسیهای سفارشی مبتنی بر پرسوجو در Clang-Tidy
مقدمه
این صفحه مثالهایی دربارهٔ نحوهٔ افزودن بررسیهای سفارشی مبتنی بر پرسوجو برای clang-tidy ارائه میدهد.
بررسیهای سفارشی بر پایهٔ نحو clang-query هستند. هر بررسی سفارشی برای جلوگیری از تداخل نام، در ماژول custom ثبت خواهد شد. آنها میتوانند مانند بررسیهای توکار از طریق گزینهٔ checks فعال یا غیرفعال شوند.
بررسیهای سفارشی مانند سایر موارد پیکربندی، از ارثبری از پیکربندیهای والد پشتیبانی میکنند.
اهداف: جعبهابزاری با نوشتن آسان، چندسکویی و با پشتیبانی از چندین نسخه برای قوانین سفارشی clang-tidy. غیر اهداف: بررسیهای پیچیده، کارایی، اصلاحات خودکار (fix-its) و غیره.
پیکربندی
- Name: نام بررسی که میتواند در گزینهٔ -checks استفاده شود.
- Query: رشتهٔ clang-query
- Diagnostic: فهرستی از عیبیابیها برای گزارش شدن. - BindName: نام گرهای که باید در Query مقید (bind) شود. - Message: پیامی که باید گزارش شود. - Level: سطح شدت عیبیابی؛ مقادیر ممکن عبارتند از: Note، Warning.
CustomChecks میتواند توسط گزینهٔ Checks در فایل پیکربندی تنظیم شود.
مثال
Checks: -*,custom-call-main-function
CustomChecks:
- Name: call-main-function
Query: |
match callExpr(
callee(
functionDecl(isMain()).bind("fn")
)
).bind("callee")
Diagnostic:
- BindName: fn
Message: main function.
Level: Note
- BindName: callee
Message: call to main function.
Level: Warning
int main(); // note: main function.
void bar() {
main(); // warning: call to main function. [custom-call-main-function]
}
نکاتی که باید به آنها توجه کرد
این قابلیت باید بهطور صریح با --experimental-custom-checks فعال شود زیرا در حال حاضر در مرحلهٔ آزمایشی قرار دارد. ارسال هرگونه پیشنهاد از طریق پیوند https://discourse.llvm.org/t/support-query-based-clang-tidy-external-check/85331 مورد استقبال است.
در طول مرحلهٔ آزمایشی، ساختار پیکربندیِ مورد نیاز این قابلیت ممکن است در آینده تغییر کند. تغییرات آتی تا حد امکان سازگار با قبل (forward-compatible) خواهند بود، اما این موضوع تضمین نمیشود.
در نسخههای بعدی، از جمله مراحل غیرآزمایشی، دستورات پرسوجو ممکن است در هر زمان تغییر کنند. ماهیت clang-query تجزیهٔ رشتهٔ پرسوجو و تولید پویای تطبیقدهندهٔ AST متناظر است. بنابراین، عملکرد آن کاملاً به توابع ارائهشده توسط کتابخانهٔ AST matcher وابسته است. تطبیقدهندهٔ ast همراه با تغییرات در clang AST تغییر خواهد کرد. لطفاً به مرجع ast matcher در https://clang.llvm.org/docs/LibASTMatchersReference.html مراجعه کنید.
یکپارچهسازی Clang-tidy با IDEها و ویرایشگرها
علاوه بر یک ابزار مستقل، clang-tidy با محیطهای توسعه یکپارچه (IDE)، تحلیلگرهای کد و ویرایشگرهای مختلف یکپارچه شده است. پیشنهاد ما استفاده از clangd http://clangd.llvm.org است که clang-tidy را درون خود دارد و در بیشتر ویرایشگرهای اصلی https://clangd.llvm.org/installation.html#editor-plugins از طریق پلاگینها در دسترس است (Vim، Emacs، Visual Studio Code، Sublime Text و موارد بیشتر https://langserver.org/#implementations-client).
جدول زیر شناختهشدهترین یکپارچهسازیهای clang-tidy را با جزئیات نشان میدهد.
| ویژگی | |||||
| ابزار | بازرسی بلادرنگ (On-the-fly) | پیکربندی فهرست بررسیها (GUI) | گزینههای مربوط به بررسیها (GUI) | پیکربندی از طریق فایلهای .clang-tidy | باینری سفارشی clang-tidy |
| A.L.E. برای Vim | + | - | - | - | + |
| Clang Power Tools برای Visual Studio | - | + | - | + | - |
| Clangd | + | - | - | + | - |
| CLion IDE | + | + | + | + | + |
| CodeChecker | - | - | - | - | + |
| CPPCheck | - | - | - | - | - |
| CPPDepend | - | - | - | - | - |
| Flycheck برای Emacs | + | - | - | + | + |
| KDevelop IDE | - | + | + | + | + |
| Qt Creator IDE | + | + | - | + | + |
| ReSharper C++ برای Visual Studio | + | + | - | + | + |
| Syntastic برای Vim | + | - | - | - | + |
| Visual Assist برای Visual Studio | + | + | - | - | - |
IDEها
محیط CLion https://www.jetbrains.com/clion نسخهٔ 2017.2 و بالاتر، clang-tidy https://www.jetbrains.com/help/clion/clang-tidy-checks-support.html را به عنوان افزونهای برای تحلیلگر کد توکار خود یکپارچه کرده است. از نسخهٔ 2018.2 EAP به بعد، CLion امکان استفاده از clang-tidy را از طریق Clangd فراهم میکند. بازرسیها و اصلاحات سریعِ قابل اعمال بهصورت بلادرنگ انجام میشوند و بررسیها را میتوان در قالب استاندارد خط فرمان پیکربندی کرد. در این یکپارچهسازی، میتوانید از یک باینری clang-tidy متفاوت با نسخهٔ همراه برنامه استفاده کنید، پیکربندی را به جای استفاده از تنظیمات IDE از طریق فایلهای .clang-tidy اعمال نمایید و گزینههای مربوط به بررسیهای خاص را پیکربندی کنید.
محیط KDevelop https://www.kdevelop.org همراه با افزونهٔ kdev-clang-tidy https://github.com/KDE/kdev-clang-tidy، از نسخهٔ 5.1 به بعد، تحلیل ایستا را با استفاده از clang-tidy انجام میدهد. این افزونه باینری clang-tidy را از مسیر مشخصشده اجرا کرده و خروجی آن را برای ارائهٔ فهرستی از اشکالات تجزیه میکند.
محیط QtCreator https://www.qt.io نگارش 4.6 هشدارهای clang-tidy را ذیل Clang Code Model https://doc.qt.io/qtcreator/creator-clang-codemodel.html درون عیبیابیهای ویرایشگر یکپارچهسازی میکند. برای بهکارگیری بازبینی clang-tidy در QtCreator، باید یک کپی از یکی از پیشتنظیمها ایجاد کرده و بررسیهای مورد نظر برای اجرا را برگزینید. از نگارش 4.7 به بعد QtCreator، تحلیل در سطح کل پروژه بهوسیله تحلیلگر Clang Tools https://doc.qt.io/qtcreator/creator-clang-tools.html امکانپذیر است.
محیط MS Visual Studio https://visualstudio.microsoft.com میتواند clang-tidy را از طریق سه ابزار مختلف یکپارچهسازی کند. افزونه ReSharper C++ https://www.jetbrains.com/help/resharper/Clang_Tidy_Integration.html، نگارش 2017.3 و پس از آن، یکپارچهسازی بینقصی از clang-tidy ارائه میدهد: بررسیها و اصلاحات سریع (quick-fixes) در کنار بازبینیهای بومی اجرا میشوند. جدای از آن، ReSharper C++ ابزار clang-tidy را بهعنوان گامی مجزا در فرآیند پاکسازی کد خود میگنجاند. ابزار Visual Assist https://docs.wholetomato.com/default.asp?W761 ساخت 2210 شامل زیرمجموعهای از چکلیست clang-tidy است تا کد را همگام با ویرایش بازبینی کند. روش دیگر برای افزودن قابلیتهای clang-tidy به Visual Studio، پلاگین Clang Power Tools https://marketplace.visualstudio.com/items?itemName=caphyon.ClangPowerTools است که بیشتر بررسیهای clang-tidy را شامل شده و آنها را حین کامپایل یا بهعنوان گامی مجزا در تحلیل کد اجرا میکند.
ویرایشگرها
ویرایشگر Emacs24 https://www.gnu.org/s/emacs، هنگام توسعه با پلاگین Flycheck https://github.com/ch1bo/flycheck-clang-tidy، بازبینی clang-tidy را در تحلیلگر نحو (syntax analyzer) خود میگنجاند. برای Vim https://www.vim.org، میتوانید از Syntastic https://github.com/vim-syntastic/syntastic که شامل clang-tidy است، یا A.L.E. https://github.com/w0rp/ale، یک موتور لینت که clang-tidy را در کنار سایر لینترها اعمال میکند، بهره ببرید.
تحلیلگرها
ابزار clang-tidy از نگارش 2018.1 در CPPDepend https://www.cppdepend.com/cppdependv2018 و نگارش 1.82 در CPPCheck https://sourceforge.net/p/cppcheck/news یکپارچهسازی شده است. یکپارچهسازی با CPPCheck به شما اجازه میدهد سلوشنهای Visual Studio را وارد کرده و بازبینی clang-tidy را روی آنها اجرا کنید. برنامه CodeChecker https://github.com/Ericsson/codechecker نگارش 5.3 به بعد، که بهصورت یک پلاگین https://github.com/Ericsson/CodeCheckerEclipsePlugin برای Eclipse نیز عرضه میشود، از clang-tidy بهعنوان یک ابزار تحلیل ایستا پشتیبانی میکند و امکان استفاده از یک باینری سفارشی clang-tidy را فراهم میسازد.
مشارکت
ابزار clang-tidy چندین بررسی اختصاصی خود را دارد و میتواند بررسیهای تحلیلگر ایستای Clang را نیز اجرا کند، اما توانمندی اصلی آن در قابلیت نوشتن آسان بررسیهای سفارشی نهفته است.
بررسیها درون ماژولها سازماندهی میشوند؛ این ماژولها میتوانند با حداقل تغییرات در کد clang-tidy (یا بدون هیچ تغییری) به آن پیوند (link) شوند.
بررسیها میتوانند در سطح پیشپردازنده با استفاده از PPCallbacks https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html یا در سطح AST با بهکارگیری AST Matchers https://clang.llvm.org/docs/LibASTMatchers.html به تحلیل متصل شوند. در صورت یافتن خطا، بررسیها میتوانند آنها را مشابه با سازوکار عیبیابیهای Clang گزارش کنند. همچنین میتوان یک راهنمای اصلاح (fix-it hint) را به پیام عیبیابی ضمیمه کرد.
رابط کاربری ارائهشده توسط clang-tidy نوشتن بررسیهای مفید و دقیق را تنها با چند خط کد ساده میسازد. اگر ایدهای برای یک بررسی خوب دارید، ادامه این سند نحوه انجام آن را شرح میدهد.
- چند ابزار وجود دارند که بهویژه هنگام توسعه بررسیهای clang-tidy سودمند هستند:
- اسکریپت add_new_check.py برای خودکارسازی فرآیند افزودن یک بررسی جدید؛ این اسکریپت بررسی را ایجاد کرده، فایل CMake را بهروزرسانی میکند و یک تست میسازد.
- اسکریپت rename_check.py همانطور که از نام آن پیداست، نام یک بررسی موجود را تغییر میدهد.
- ابزار pp-trace فراخوانی متدها روی PPCallbacks را برای یک فایل سورس لاگ میکند و برای درک سازوکار پیشپردازنده فوقالعاده ارزشمند است.
- ابزار clang-query برای نمونهسازی تعاملیِ تطبیقدهندههای AST و کاوش در AST مربوط به Clang بسیار سودمند است؛
- ابزار clang-check https://clang.llvm.org/docs/ClangCheck.html همراه با گزینه -ast-dump (و در صورت تمایل -ast-dump-filter) روشی آسان برای دامپ گرفتن از AST یک برنامه C++ فراهم میکند.
اگر CMake با CLANG_TIDY_ENABLE_STATIC_ANALYZER=NO پیکربندی شود، clang-tidy بدون پشتیبانی از بررسیهای clang-analyzer-* یا بررسیهای mpi-* ساخته خواهد شد.
اگر CMake با CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS=NO پیکربندی شود، clang-tidy بدون پشتیبانی از بررسیهای مبتنی بر پرسوجو (query-based) ساخته خواهد شد.
انتخاب جایگاه مناسب برای بررسی شما
اگر ایدهای برای یک بررسی دارید، باید تصمیم بگیرید که آیا این بررسی باید بهصورت زیر پیادهسازی شود:
- عیبیابی Clang (یا Clang diagnostic): اگر بررسی بهاندازه کافی عمومی باشد، الگوهایی از کد را هدف قرار دهد که به احتمال بسیار زیاد باگ هستند (نه صرفاً مسائل مربوط به سبک کدنویسی یا خوانایی)، و بتوان آن را بهطور کارآمد و با نرخ مثبت کاذب بسیار پایین پیادهسازی کرد، میتواند یک Clang diagnostic مناسب باشد.
- بررسی تحلیلگر ایستای Clang: اگر بررسی به نوعی از تحلیل جریان کنترل (control flow analysis) نیاز داشته باشد، احتمالاً باید بهعنوان یک بررسی تحلیلگر ایستا پیادهسازی شود.
- بررسی clang-tidy: انتخابی مناسب برای بررسیهایی به سبک لینتر، بررسیهای مرتبط با یک سبک کدنویسی خاص، بررسیهایی با محوریت خوانایی کد و مواردی از این دست است.
آمادهسازی محیط کاری شما
اگر در توسعه LLVM تازهکار هستید، باید اسناد Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html، Using Clang Tools https://clang.llvm.org/docs/ClangTools.html و How To Setup Clang Tooling For LLVM https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html را بخوانید تا LLVM، Clang و ابزارهای جانبی Clang را دریافت کرده و با استفاده از CMake بسازید.
پس از پایان کار، به دایرکتوری llvm/clang-tools-extra بروید و کار را آغاز کنید!
هنگام پیکربندی ساخت CMake https://llvm.org/docs/GettingStarted.html#local-llvm-configuration، مطمئن شوید که پروژههای clang و clang-tools-extra را فعال کردهاید تا clang-tidy ساخته شود. از آنجا که بررسی جدید شما مستندات مربوط به خود را خواهد داشت، ممکن است بخواهید Sphinx https://www.sphinx-doc.org/en/master را نیز نصب کرده و آن را در پیکربندی CMake فعال کنید. برای صرفهجویی در زمان ساخت کتابخانههای اصلی Clang، ممکن است ترجیح دهید تنها هدف X86 را در پیکربندی CMake فعال سازید.
ساختار دایرکتوری
کد منبع clang-tidy در دایرکتوری llvm/clang-tools-extra قرار دارد و ساختار آن به شرح زیر است:
clang-tidy/ # هسته اصلی Clang-tidy.
|-- ClangTidy.h # رابطهای کاربری برای کاربران.
|-- ClangTidyCheck.h # رابطهای کاربری برای بررسیها.
|-- ClangTidyModule.h # رابط کاربری برای ماژولهای clang-tidy.
...
|-- google/ # ماژول Google برای clang-tidy.
|-+
|-- GoogleTidyModule.cpp
|-- GoogleTidyModule.h
...
|-- llvm/ # ماژول LLVM برای clang-tidy.
|-+
|-- LLVMTidyModule.cpp
|-- LLVMTidyModule.h
...
|-- objc/ # ماژول Objective-C برای clang-tidy.
|-+
|-- ObjCTidyModule.cpp
|-- ObjCTidyModule.h
...
|-- tool/ # کدهای منبع باینری clang-tidy.
...
test/clang-tidy/ # تستهای یکپارچهسازی.
...
unittests/clang-tidy/ # تستهای واحد (Unit tests).
|-- ClangTidyTest.h
|-- GoogleModuleTest.cpp
|-- LLVMModuleTest.cpp
|-- ObjCModuleTest.cpp
...
نوشتن یک بررسی (Check) برای clang-tidy
فرض کنید ایدهای برای یک بررسی مفید در clang-tidy دارید.
ابتدا، اگر با توسعهٔ LLVM آشنا نیستید، سند Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html را برای آشنایی با دستورالعملهای راهاندازی گردش کاری خود، و سند LLVM Coding Standards https://llvm.org/docs/CodingStandards.html را برای آشنایی با سبک کدنویسی استفادهشده در پروژه مطالعه کنید. برای بازبینی کدها (Code Review)، در حال حاضر از LLVM Github https://github.com/llvm/llvm-project استفاده میکنیم، هرچند در گذشته از Phabricator استفاده میشد.
در مرحله بعد، باید مشخص کنید که بررسی به کدام ماژول تعلق دارد. ماژولها در زیردایرکتوریهای clang-tidy/ https://github.com/llvm/llvm-project/tree/main/clang-tools-extra/clang-tidy قرار دارند و شامل بررسیهایی هستند که جنبهٔ خاصی از کیفیت کد (کارایی، خوانایی و غیره)، یک سبک کدنویسی یا استاندارد خاص (Google، LLVM، CERT و غیره) یا یک API پرکاربرد (مانند MPI) را هدف قرار میدهند. نام آنها مشابه نامهای گروههای بررسی کاربرمحور است که در بخشهای بالا توصیف شد <#checks-groups-table>.
پس از انتخاب ماژول و نام برای بررسی، اسکریپت clang-tidy/add_new_check.py را اجرا کنید تا ساختار اولیه (اسکلت) بررسی ایجاد شده و به clang-tidy متصل شود. این روش، شیوهٔ توصیهشده برای افزودن بررسیهای جدید است.
بهطور پیشفرض، بررسی جدید تنها برای کدهای C++ اعمال خواهد شد. اگر باید روی گزینههای زبانی متفاوتی اعمال شود، از پارامتر --language اسکریپت استفاده کنید.
اگر بخواهیم بررسی readability-awesome-function-names را ایجاد کنیم، دستور زیر را اجرا میکنیم:
$ clang-tidy/add_new_check.py readability awesome-function-names
- اسکریپت add_new_check.py اقدامات زیر را انجام میدهد:
- کلاس مربوط به بررسی شما را داخل دایرکتوری ماژول مشخصشده ایجاد کرده و آن را در ماژول و در سامانهٔ ساخت ثبت میکند؛
- یک فایل آزمون lit در دایرکتوری test/clang-tidy/ ایجاد میکند؛
- یک فایل مستندات ایجاد کرده و آن را در docs/clang-tidy/checks/list.rst درج میکند.
بیایید نگاه دقیقتری به تعریف کلاس بررسی بیندازیم:
...
#include "../ClangTidyCheck.h"
namespace clang::tidy::readability {
...
class AwesomeFunctionNamesCheck : public ClangTidyCheck {
public:
AwesomeFunctionNamesCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
};
} // namespace clang::tidy::readability
...
سازندهٔ (Constructor) بررسی، پارامترهای Name و Context را دریافت میکند و باید آنها را به سازندهٔ ClangTidyCheck ارسال نماید.
در مورد ما، بررسی باید در سطح AST کار کند و متدهای registerMatchers و check را بازنویسی (override) میکند. اگر میخواستیم کد را در سطح پیشپردازنده تجزیهوتحلیل کنیم، در عوض باید متد registerPPCallbacks را بازنویسی میکردیم.
در متد registerMatchers، یک AST Matcher (برای اطلاعات بیشتر به AST Matchers https://clang.llvm.org/docs/LibASTMatchers.html مراجعه کنید) ایجاد میکنیم که الگوی مد نظر ما را در AST پیدا میکند. نتایج تطبیق به متد check فرستاده میشوند، که میتواند آنها را بیشتر بررسی کرده و گزارشهای عیبیابی (diagnostics) را ارسال کند.
using namespace ast_matchers;
void AwesomeFunctionNamesCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(functionDecl().bind("x"), this);
}
void AwesomeFunctionNamesCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_"))
return;
diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome")
<< MatchedDecl
<< FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
}
(اگر میخواهید نمونهای از یک بررسی کاربردی را ببینید، به clang-tidy/google/ExplicitConstructorCheck.h https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.h و clang-tidy/google/ExplicitConstructorCheck.cpp https://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/ExplicitConstructorCheck.cpp نگاه کنید).
اگر نیاز به تعامل با ماکروها یا دستورات پیشپردازنده دارید، باید متد registerPPCallbacks را بازنویسی کنید. اسکریپت add_new_check.py در نقطهٔ شروع بررسی جدید، بازنویسی این متد را ایجاد نمیکند.
نکات توسعهٔ بررسی
نوشتن اولین بررسی میتواند کاری دشوار و دلهرهآور باشد، بهویژه اگر با پایگاههای کد LLVM و Clang آشنا نباشید. در اینجا پیشنهادهایی برای آشنایی با پایگاه کد و کار روی بررسی بهصورت گامبهگام آورده شده است.
راهنمای مستندات کاربردی
بسیاری از کلاسهای پشتیبان که برای LLVM ایجاد شدهاند، توسط Clang نیز استفاده میشوند؛ مانند StringRef https://llvm.org/docs/ProgrammersManual.html#the-stringref-class و SmallVector https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h. این کلاسها و سایر کلاسهای پرکاربرد در بخشهای Important and useful LLVM APIs https://llvm.org/docs/ProgrammersManual.html#important-and-useful-llvm-apis و Picking the Right Data Structure for the Task https://llvm.org/docs/ProgrammersManual.html#picking-the-right-data-structure-for-a-task از LLVM Programmer's Manual https://llvm.org/docs/ProgrammersManual.html توضیح داده شدهاند. نیازی نیست تمام جزئیات این کلاسها را به خاطر بسپارید؛ مستندات doxygen تولیدشده https://llvm.org/doxygen در صورت نیاز همه چیز را در خود دارند. در سربرگ LLVM/ADT/STLExtras.h https://llvm.org/doxygen/STLExtras_8h.html نگارشهای مفیدی از الگوریتمهای STL را خواهید یافت که روی نگهدارندههای LLVM عمل میکنند؛ مانند llvm::all_of https://llvm.org/doxygen/STLExtras_8h.html#func-members.
ابزار Clang بر روی LLVM پیادهسازی شده و مجموعهای از کلاسهای اختصاصی خود را معرفی میکند که هنگام نوشتن بررسی خود با آنها تعامل خواهید داشت. وقتی یک بررسی، گزارشهای عیبیابی و اصلاحات (fix-it) صادر میکند، این موارد با مکانهایی در کد منبع مرتبط هستند. مکانهای کد منبع، فایلهای منبع، محدودههای مکان منبع و کلاس SourceManager https://clang.llvm.org/doxygen/classclang_1_1SourceManager.html سازوکارهای توصیف چنین مکانهایی را فراهم میکنند. این موضوعات و موارد دیگر در "Clang" CFE Internals Manual https://clang.llvm.org/docs/InternalsManual.html شرح داده شدهاند. در حالی که مستندات تولیدشدهٔ doxygen بهعنوان مرجعی برای ساختار درونی Clang عمل میکند، این سند بهعنوان راهنمایی برای توسعهدهندگان دیگر است. موضوعات موجود در آن کتابچه که برای یک توسعهدهندهٔ بررسی جالب هستند عبارتند از:
- کتابخانهٔ The Clang "Basic" Library https://clang.llvm.org/docs/InternalsManual.html#the-clang-basic-library برای اطلاعات دربارهٔ عیبیابیها، راهنماییهای fix-it و مکانهای منبع.
- کتابخانهٔ The Lexer and Preprocessor Library https://clang.llvm.org/docs/InternalsManual.html#the-lexer-and-preprocessor-library برای اطلاعات دربارهٔ توکنها، تحلیل واژگانی (تبدیل نویسهها به توکنها) و پیشپردازنده.
- کتابخانهٔ The AST Library https://clang.llvm.org/docs/InternalsManual.html#the-ast-library برای اطلاعات در مورد نحوهٔ نمایش عبارات منبع C++ بهعنوان یک درخت نحو انتزاعی (AST).
بیشتر بررسیها از طریق AST با کد منبع C++ تعامل دارند. برخی از بررسیها با پیشپردازنده تعامل خواهند داشت. فایل منبع ورودی ابتدا تحلیل واژگانی و پیشپردازش شده و سپس به AST تجزیه میشود. پس از ساخت کامل AST، بررسی از طریق اعمال تطبیقدهندههای ثبتشدهٔ AST بررسی بر روی AST و فراخوانی بررسی با مجموعهای از گرههای تطبیقیافته از AST اجرا میشود. نظارت بر عملکرد پیشپردازنده مستقل از ساخت AST است، اما یک بررسی میتواند در طول پیشپردازش اطلاعاتی را برای استفادهٔ بعدی در زمان تطبیق گرهها توسط AST جمعآوری کند.
هر عنصر نحوی (و گاهی معنایی) از کد منبع C++ با کلاسهای متفاوتی در AST نمایش داده میشود. شما بخشهای مورد نظر خود از AST را با ترکیب توابع تطبیقدهندهٔ AST انتخاب میکنید. توصیه میشود AST Matcher Reference https://clang.llvm.org/docs/LibASTMatchersReference.html را بهدقت مطالعه کنید تا ارتباط بین توابع تطبیقدهندهٔ مختلف را درک نمایید.
استفاده از کتابخانه Transformer
کتابخانه Transformer به شما اجازه میدهد تا بررسیای بنویسید که کد منبع را از طریق بیان دگرگونی بهصورت یک RewriteRule بازنویسی کند. کتابخانه Transformer توابعی را برای ترکیب ویرایشها روی کد منبع فراهم میآورد تا قوانین بازنویسی ایجاد شوند. مگر اینکه نیاز به دستکاری موقعیت منبع در سطح پایین داشته باشید، بهتر است نوشتن بررسی خود را با کتابخانه Transformer در نظر بگیرید. راهنمای Clang Transformer https://clang.llvm.org/docs/ClangTransformerTutorial.html کتابخانه Transformer را با جزئیات شرح میدهد.
برای استفاده از کتابخانه Transformer، تغییرات زیر را روی کد تولیدشده توسط اسکریپت add_new_check.py اعمال کنید:
- گنجاندن ../utils/TransformerClangTidyCheck.h بهجای ../ClangTidyCheck.h
- تغییر کلاس پایه بررسی خود از ClangTidyCheck به TransformerClangTidyCheck
- حذف بازنویسی (override) متدهای registerMatchers و check در کلاس بررسی خود.
- نوشتن تابعی که RewriteRule را برای بررسی شما ایجاد میکند.
- فراخوانی این تابع در سازنده بررسی خود جهت ارسال قانون بازنویسی به سازنده TransformerClangTidyCheck.
توسعه گامبهگام بررسی خود
بهترین راه برای توسعه بررسی، شروع با موارد آزمایشی ساده و افزایش تدریجی پیچیدگی است. فایل آزمایشی ایجادشده توسط اسکریپت add_new_check.py یک نقطه شروع برای موارد آزمایشی شماست. نمای کلی این فرآیند به این صورت است:
- یک مورد آزمایشی برای بررسی خود بنویسید.
- با استفاده از clang-query روی فایل آزمایشی، تطبیقدهندهها (matchers) را نمونهسازی اولیه کنید.
- تطبیقدهندههای کارآمد را در متد registerMatchers ثبت کنید.
- پیامهای عیبیابی و اصلاحات (fix-its) لازم را در متد check صادر کنید.
- یادداشتهای ضروری CHECK-MESSAGES و CHECK-FIXES را برای اعتبارسنجی پیامهای عیبیابی و اصلاحات به مورد آزمایشی خود اضافه کنید.
- تارگت check-clang-tools را کامپایل کنید تا از قبولی در آزمون مطمئن شوید.
- این فرآیند را تکرار کنید تا تمام جنبههای بررسی شما با آزمونها پوشش داده شوند.
سریعترین راه برای نمونهسازی اولیه تطبیقدهنده، استفاده از clang-query جهت ساخت تعاملی تطبیقدهنده شما است. برای تطبیقدهندههای پیچیده، عبارت تطبیق را بهصورت افزایشی بسازید و از دستور let در clang-query برای ذخیره عبارات تطبیق نامگذاریشده جهت سادهسازی تطبیقدهنده استفاده کنید.
clang-query> let c1 cxxRecordDecl() clang-query> match c1
همچنین، فشردن کلید tab پس از پرانتز بازِ یک تطبیقدهنده قبلی نشان میدهد که چه تطبیقدهندههایی میتوانند با تطبیقدهنده قبلی زنجیر شوند، اگرچه ممکن است برخی از تطبیقدهندههای معتبر در فهرست ظاهر نشوند. توجه داشته باشید که تکمیل با کلید Tab در حال حاضر روی ویندوز کار نمیکند.
درست همانطور که تقسیم یک تابع بزرگ به بخشهای کوچکتر با نامهای بیانگرِ هدف میتواند به درک یک الگوریتم پیچیده کمک کند، تقسیم یک تطبیقدهنده به تطبیقدهندههای کوچکتر با نامهای مشخص و خوانا نیز میتواند به درک یک تطبیقدهنده پیچیده کمک کند.
پس از دستیابی به یک تطبیقدهنده کارآمد در clang-query، تطبیقدهندههای C++ API همان یا شبیه به تطبیقدهنده ساختهشده بهصورت تعاملی شما خواهند بود (ممکن است مواردی با تفاوتهای جزئی وجود داشته باشد). میتوانید از متغیرهای محلی برای حفظ نامهای هدفمحور خود که روی تطبیقدهندههای تودرتو اعمال کردهاید، استفاده کنید.
ایجاد تطبیقدهندههای خصوصی
گاهی اوقات میخواهید جنبه خاصی از AST را تطبیق دهید که توسط تطبیقدهندههای موجود در AST ارائه نشده است. میتوانید تطبیقدهنده خصوصی خود را با استفاده از همان زیرساخت تطبیقدهندههای عمومی ایجاد کنید. یک تطبیقدهنده خصوصی میتواند پردازش در متد check شما را با حذف پیمایشهای دستی و پیچیده گرههای تطبیقیافته در AST ساده کند. استفاده از تطبیقدهنده خصوصی به شما امکان میدهد بخشهای موردنظر AST را مستقیماً در تطبیقدهنده انتخاب کرده و از طریق یک نام متصلشده در متد check به آن ارجاع دهید.
آزمون واحد کدهای کمکی
تطبیقدهندههای سفارشی خصوصی نمونهای عالی از کدهای کمکی جانبی برای بررسی شما هستند که میتوان آنها را با آزمون واحد تست کرد. آزمایش تطبیقدهندهها یا سایر کلاسهای پشتیبان با نوشتن آزمون واحد بسیار آسانتر از نوشتن آزمون یکپارچگی FileCheck خواهد بود. تارگت ASTMatchersTests شامل آزمونهای واحد برای کلاسهای عمومی تطبیقدهنده AST است و منبعی عالی از الگوهای آزمایشی برای تطبیقدهندهها بهشمار میرود.
میتوانید آزمونهای واحد Clang-Tidy را با ساخت تارگت ClangTidyTests کامپایل کنید. تارگتهای آزمایشی در LLVM و Clang از عملیات سبک "build all" در تولیدکنندههای مبتنی بر محیط توسعه CMake کنار گذاشته شدهاند؛ بنابراین باید این تارگت را بهطور صریح کامپایل کنید تا آزمونهای واحد ساخته شوند.
مقاومسازی بررسی خود
پس از اینکه بررسی خود را با سناریوهای پایه "مسیر ایدهآل" (happy path) پوشش دادید، باید بررسی خود را با حداکثر حالتهای خاص (edge cases) ممکن بسنجید تا از مقاومت و پایداری آن اطمینان حاصل کنید. اجرای بررسی روی یک پایگاه کد بزرگ، مانند Clang/LLVM، روش مناسبی برای شناسایی مواردی است که در تطبیقدهندههای خود از قلم انداختهاید. با این حال، پایگاه کد LLVM ممکن است برای مقاصد آزمایشی کافی نباشد، چرا که بر اساس مجموعه خاصی از سبکهای کدنویسی و معیارهای کیفی توسعه یافته است. هرچه بدنه کدی که بررسی در برابر آن تست میشود بزرگتر باشد، جامعه کاربری اطمینان بیشتری به کارآمدی و نرخ خطای مثبت کاذب (false-positive rate) بررسی خواهد داشت.
چند پیشنهاد برای اطمینان از مقاوم بودن بررسی شما:
- فایلهای سرایندی بسازید که حاوی کدهای تطبیقیافته توسط بررسی شما باشند.
- بررسی کنید که اصلاحات (fix-its) بهدرستی با clang-tidy روی فایلهای هدر آزمایشی اعمال شوند. تا زمانی که پشتیبانی خودکار برای بررسی پیامها و اصلاحات به اسکریپت check_clang_tidy.py اضافه نشود، باید این آزمایش را بهصورت دستی انجام دهید.
- ماکروهایی تعریف کنید که شامل کدهای تطبیقیافته با بررسی شما باشند.
- کلاسهای قالبی (template classes) تعریف کنید که شامل کدهای تطبیقیافته با بررسی شما باشند.
- تخصیصهای قالب (template specializations) تعریف کنید که شامل کدهای تطبیقیافته با بررسی شما باشند.
- بررسی خود را در هر دو محیط ویندوز و لینوکس آزمایش کنید.
- مراقب نرخ بالای مثبت کاذب باشید. در حالت ایدهآل، یک بررسی نباید هیچ مثبت کاذبی داشته باشد، اما از آنجا که تطبیق با AST به جریان کنترل یا جریان داده حساس نیست، تعدادی مثبت کاذب مورد انتظار است. هرچه نرخ مثبت کاذب بالاتر باشد، احتمال بهکارگیری این بررسی در عمل کمتر خواهد بود. باید سازوکارهایی تعبیه شود تا به کاربر در مدیریت موارد مثبت کاذب کمک کند.
- دو سازوکار اصلی برای مدیریت موارد مثبت کاذب وجود دارد: پشتیبانی از یک الگوی کد که به برنامهنویس اجازه میدهد پیام عیبیابی را به شکل موردی خاموش کند، و گزینههای پیکربندی بررسی برای کنترل رفتار آن.
- پشتیبانی از یک الگوی کد را مد نظر قرار دهید تا هر زمان که چنین الگویی بتواند قصد برنامهنویس را بهوضوح بیان کند، امکان خاموش کردن پیام عیبیابی را به او بدهد؛ بهعنوان مثال، اجازه دادن به تبدیل نوع صریح به void برای خاموش کردن پیام عیبیابی متغیر استفادهنشده.
- افزودن گزینههای پیکربندی بررسی را در نظر بگیرید تا به کاربر اجازه دهد بدون تحمیل بار به کاربران در سناریوهای رایج و با اطمینان بالا، رفتارهای تهاجمیتر بررسی را فعال کند.
مستندسازی بررسی شما
اسکریپت add_new_check.py ورودیهایی را در یادداشتهای انتشار https://clang.llvm.org/extra/ReleaseNotes.html، فهرست
بررسیها و یک فایل جدید برای خودِ مستندات بررسی ایجاد میکند. توصیه میشود خلاصهای موجز از عملکرد بررسی خود را در یک تکجمله آماده کنید که در یادداشتهای انتشار، به عنوان نخستین جمله در کامنتهای doxygen فایل هدر کلاس بررسی شما، و نیز نخستین جمله در مستندات بررسی تکرار شود. از به کار بردن عبارت "this check" در خلاصه و مستندات بررسی خود خودداری کنید.
اگر بررسی شما به یک شیوهنامه یا راهنمای کدنویسی منتشرشده (مانند C++ Core Guidelines، SEI CERT و غیره) مربوط است، پیوندهایی به بخشهای مرتبط آن راهنما یا شیوهنامه را در مستندات بررسی خود بگنجانید.
نمونههای کافی از عیبیابیها و اصلاحات خودکار (fix-itها) ارائه دهید تا کاربر به آسانی درک کند هنگام اجرای بررسی، چه اتفاقی برای کد او رخ خواهد داد. اگر استثناها یا محدودیتهایی در بررسی شما وجود دارد، آنها را بهطور کامل مستند کنید. این کار به کاربران کمک میکند تا دامنه و حدود عیبیابیها و اصلاحات ارائهشده توسط بررسی را دریابند.
ساخت هدف (target) با نام docs-clang-tools-html، تولیدکننده مستندات Sphinx را اجرا کرده و فایلهای مستندات HTML را در دایرکتوری tools/clang/tools/extra/docs/html در درخت ساخت (build tree) شما ایجاد میکند. اطمینان حاصل کنید که بررسی شما بهدرستی در یادداشتهای انتشار و فهرست بررسیها نمایش داده میشود. مطمئن شوید که قالببندی و ساختار مستندات بررسی شما صحیح است: هیچ فضای خالی در انتهای خطوط (trailing whitespace) وجود نداشته باشد و طول خطوط از ۸۰ نویسه بیشتر نشود.
برای اعتبارسنجی فایلهای خود، لطفاً از doc8 به روشی که در ادامه شرح داده شده استفاده کنید.
ابزار Clang-Tidy از doc8 https://pypi.org/project/doc8 برای بررسی سازگاری قالببندی فایلهای .rst استفاده میکند. میتوانید doc8 را از طریق pip نصب کنید:
$ pip install doc8
برای اجرای doc8 روی مستندات تغییریافته:
$ git diff --name-only HEAD -- clang-tools-extra/docs/clang-tidy/ | grep "\.rst$" | xargs -r doc8
ثبت بررسی شما
(اسکریپت add_new_check.py وظیفه ثبت بررسی در یک ماژول موجود را بر عهده میگیرد. اگر میخواهید ماژول جدیدی بسازید یا از جزئیات آن آگاه شوید، ادامه را مطالعه کنید.)
بررسی باید در ماژول مربوطه با یک نام متمایز و یکتا ثبت شود:
class MyModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<ExplicitConstructorCheck>(
"my-explicit-constructor");
}
};
اکنون باید ماژول را با استفاده از یک متغیر با مقداردهی اولیه ایستا (statically initialized) در ClangTidyModuleRegistry ثبت کنیم:
static ClangTidyModuleRegistry::Add<MyModule> X("my-module",
"Adds my lint checks.");
هنگام استفاده از سیستم ساخت LLVM، باید از ترفند زیر استفاده کنیم تا مطمئن شویم ماژول حتماً در فایل باینری clang-tidy پیوند (link) میخورد:
این قطعه کد را در نزدیکی متغیر ClangTidyModuleRegistry::Add<MyModule> اضافه کنید:
// This anchor is used to force the linker to link in the generated object file // and thus register the MyModule. volatile int MyModuleAnchorSource = 0;
و این قطعه کد را به واحد ترجمه اصلی فایل باینری clang-tidy (یا فایلی که کتابخانه clang-tidy را در آن پیوند میدهید) در مسیر clang-tidy/ClangTidyForceLinker.h اضافه کنید:
// This anchor is used to force the linker to link the MyModule. extern volatile int MyModuleAnchorSource; static int MyModuleAnchorDestination = MyModuleAnchorSource;
پیکربندی بررسیها
اگر یک بررسی نیاز به گزینههای پیکربندی داشته باشد، میتواند از طریق فراخوانی Options.get<Type>("SomeOption", DefaultValue) در سازنده (constructor) بررسی، به گزینههای اختصاصی خود دسترسی پیدا کند. در این صورت، بررسی باید متد ClangTidyCheck::storeOptions را نیز بازنویسی (override) کند تا گزینههای ارائهشده توسط بررسی قابل شناسایی و کشف باشند. این متد به clang-tidy اطلاع میدهد که بررسی چه گزینههایی را پیادهسازی کرده و مقادیر فعلی آنها چیست (برای نمونه، جهت استفاده در گزینه خط فرمان -dump-config).
class MyCheck : public ClangTidyCheck {
const unsigned SomeOption1;
const std::string SomeOption2;
public:
MyCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
SomeOption1(Options.get("SomeOption1", -1U)),
SomeOption2(Options.get("SomeOption2", "some default")) {}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
Options.store(Opts, "SomeOption1", SomeOption1);
Options.store(Opts, "SomeOption2", SomeOption2);
}
...
با فرض اینکه بررسی با نام "my-check" ثبت شده باشد، گزینه را میتوان درون یک فایل .clang-tidy به صورت زیر تنظیم کرد:
CheckOptions: my-check.SomeOption1: 123 my-check.SomeOption2: 'some other value'
اگر نیاز به تعیین گزینههای بررسی در خط فرمان دارید، میتوانید از قالب YAML درونخطی (inline) استفاده کنید:
$ clang-tidy -config="{CheckOptions: {a: b, x: y}}" ...
بررسیهای آزمایشی
برای اجرای آزمونهای clang-tidy، هدف ساخت check-clang-tools را بسازید. برای نمونه، اگر پیکربندی ساخت CMake خود را با تولیدکننده پروژه ninja انجام دادهاید، از این دستور استفاده کنید:
$ ninja check-clang-tools
بررسیهای clang-tidy را میتوان با استفاده از آزمونهای واحد (unit tests) یا آزمونهای lit https://llvm.org/docs/CommandGuide/lit.html سنجید. آزمونهای واحد ممکن است برای سنجش جایگزینیهای پیچیده همراه با بررسیهای سختگیرانه مناسبتر باشند. آزمونهای Lit https://llvm.org/docs/CommandGuide/lit.html امکان تطبیق جزئی متن و عبارات باقاعده (regular expressions) را فراهم میکنند که آنها را برای نوشتن آزمونهای فشرده برای پیامهای تشخیصی مناسبتر میسازد.
اسکریپت check_clang_tidy.py راهکاری آسان برای آزمودن پیامهای تشخیصی و اصلاحات خودکار (fix-its) فراهم میکند. این اسکریپت خطوط CHECK را از فایل آزمون پالایش کرده، clang-tidy را اجرا میکند و پیامها و اصلاحات را با دو بار فراخوانی مجزای FileCheck https://llvm.org/docs/CommandGuide/FileCheck.html اعتبارسنجی مینماید: یک بار با پیشوند دستور FileCheck تنظیمشده روی CHECK-MESSAGES جهت اعتبارسنجی پیامهای تشخیصی، و یک بار با پیشوند دستور تنظیمشده روی CHECK-FIXES، که در برابر کد اصلاحشده (یعنی کدی که پس از اعمال fix-itهای تولیدشده بهدست آمده) اجرا میشود. بهویژه، CHECK-FIXES: را میتوان برای بررسی این موضوع به کار برد که کد توسط fix-itها دستخوش تغییر نشده است، این کار از طریق بررسی وجود بدون تغییر آن در کد اصلاحشده انجام میشود. مجموعه کامل دستورالعملهای FileCheck https://llvm.org/docs/CommandGuide/FileCheck.html در دسترس است (مانند CHECK-MESSAGES-SAME:، CHECK-MESSAGES-NOT:)، اگرچه معمولاً اشکال پایهای CHECK (یعنی CHECK-MESSAGES و CHECK-FIXES) برای آزمونهای clang-tidy کفایت میکنند. توجه داشته باشید که مستندات FileCheck https://llvm.org/docs/CommandGuide/FileCheck.html غالباً پیشفرض را بر پیشوند پیشفرض (CHECK) میگذارد، و از این رو دستورالعمل را بهصورت CHECK:، CHECK-SAME:، CHECK-NOT: و غیره توصیف میکند. برای آزمونهای clang-tidy، مقدار CHECK را با CHECK-FIXES یا CHECK-MESSAGES جایگزین کنید.
یک بررسی اضافی که توسط check_clang_tidy.py فعال میشود اطمینان حاصل میکند که اگر CHECK-MESSAGES: در فایلی استفاده شده باشد، باید به ازای هر هشدار یا خطا یک CHECK متناظر در آن فایل وجود داشته باشد. یا اینکه، اگر میخواهید اطمینان حاصل کنید که تمام یادداشتها (notes) نیز بررسی میشوند، میتوانید به جای آن از CHECK-NOTES: استفاده کنید.
برای استفاده از اسکریپت check_clang_tidy.py، یک فایل .cpp با خط RUN مناسب در دایرکتوری test/clang-tidy قرار دهید. از خطوط CHECK-MESSAGES: و CHECK-FIXES: برای نوشتن بررسیها در برابر پیامهای تشخیصی و کد اصلاحشده استفاده کنید.
توصیه میشود بررسیها تا حد ممکن دقیق و مشخص باشند تا از تطابق اشتباه بررسیها با بخشهای نادرست ورودی جلوگیری شود. در کد آزمون از جایگزینیهای [[@LINE+X]]/[[@LINE-X]] و نامهای متمایز برای توابع و متغیرها استفاده کنید.
در اینجا نمونهای از یک آزمون با استفاده از اسکریپت check_clang_tidy.py آمده است (کد منبع کامل در test/clang-tidy/checkers/google/readability-casting.cpp https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/test/clang-tidy/checkers/google/readability-casting.cpp موجود است):
// RUN: %check_clang_tidy %s google-readability-casting %t
void f(int a) {
int b = (int)a;
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type [google-readability-casting]
// CHECK-FIXES: int b = a;
}
برای بررسی بیش از یک سناریو در همان فایل آزمون، از -check-suffix=SUFFIX-NAME در خط فرمان check_clang_tidy.py یا از -check-suffixes=SUFFIX-NAME-1,SUFFIX-NAME-2,... استفاده کنید. با -check-suffix[es]=SUFFIX-NAME باید دستورالعملهای CHECK-* خود را با CHECK-MESSAGES-SUFFIX-NAME و CHECK-FIXES-SUFFIX-NAME جایگزین نمایید.
در اینجا یک مثال آمده است:
// RUN: %check_clang_tidy -check-suffix=USING-A %s misc-unused-using-decls %t -- -- -DUSING_A
// RUN: %check_clang_tidy -check-suffix=USING-B %s misc-unused-using-decls %t -- -- -DUSING_B
// RUN: %check_clang_tidy %s misc-unused-using-decls %t
...
// CHECK-MESSAGES-USING-A: :[[@LINE-8]]:10: warning: using decl 'A' {{.*}}
// CHECK-MESSAGES-USING-B: :[[@LINE-7]]:10: warning: using decl 'B' {{.*}}
// CHECK-MESSAGES: :[[@LINE-6]]:10: warning: using decl 'C' {{.*}}
// CHECK-FIXES-USING-A-NOT: using a::A;$
// CHECK-FIXES-USING-B-NOT: using a::B;$
// CHECK-FIXES-NOT: using a::C;$
گوشههای تاریک فراوانی در زبان ++C وجود دارد، و ممکن است کارکرد بینقص بررسی شما در تمامی حالات دشوار باشد، بهویژه اگر توصیههای fix-it تولید کند. رایجترین چالشها و دامها ماکروها و قالبها (templates) هستند:
- 1.
- کد نوشتهشده در بدنه یک ماکرو یا تعریف یک قالب ممکن است بسته به گسترش ماکرو (macro expansion) یا نمونهسازی قالب (template instantiation) معنای متفاوتی داشته باشد.
- 2.
- گسترشهای متعدد ماکرو یا نمونهسازیهای مکرر قالب ممکن است سبب شود تا همان کد چندین بار توسط بررسی، بازبینی شود (احتمالاً با معانی متفاوت، مورد ۱ را ببینید)، و همان هشدار (یا هشداری با اندکی تفاوت) بارها از سوی بررسی صادر گردد؛ clang-tidy هشدارهای _کاملاً یکسان_ را یکتاسازی (deduplicate) میکند، اما اگر هشدارها اندکی با هم تفاوت داشته باشند، تمامی آنها به کاربر نمایش داده خواهند شد (و در صورت وجود، برای اعمال اصلاحات استفاده میشوند).
- 3.
- اعمال جایگزینیها در بدنه ماکرو یا تعریف قالب ممکن است برای برخی از گسترشهای ماکرو یا نمونهسازیهای قالب درست کار کند، اما بهسادگی باعث شکست در سایر گسترشها یا نمونهسازیها شود.
اگر برای سنجش همه جنبههای بررسی خود به چندین فایل نیاز دارید، پیشنهاد میشود آنها را در زیردایرکتوری با نام همان بررسی، در دایرکتوری Inputs مربوط به ماژول دربردارنده بررسی خود قرار دهید. این کار مانع از شلوغی دایرکتوری آزمون میشود.
اگر نیاز به اعتبارسنجی نحوه تعامل بررسی خود با فایلهای هدر سیستمی دارید، مجموعهای از فایلهای هدر سیستمی شبیهسازیشده در دایرکتوری checkers/Inputs/Headers قرار دارد. مسیر این دایرکتوری در آزمون lit با متغیر %clang_tidy_headers در دسترس است.
ارسال Pull Request
پیش از ارسال یک pull request، به مشارکتکنندگان توصیه میشود که clang-tidy و clang-format را روی تغییرات خود اجرا کنند تا از کیفیت کد اطمینان حاصل کرده و مشکلات احتمالی را بیابند. گرچه clang-tidy در حال حاضر در CI اجباری نشده است، اما رعایت این شیوه به حفظ یکپارچگی کد و جلوگیری از خطاهای رایج کمک میکند.
در اینجا دستور مفیدی برای بررسی تغییرات مرحلهبندیشده (staged) شما آمده است:
$ git diff --staged -U0 | ./clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py \
-j $(nproc) -path build/ -p1 -only-check-in-db
$ git clang-format
توجه داشته باشید که برخی هشدارها ممکن است مثبت کاذب (false positive) باشند یا پیش از اصلاح نیاز به بررسی دقیق داشته باشند. از قضاوت مهندسی خود استفاده کنید و چنانچه درباره یک هشدار خاص نامطمئن هستید، در pull request مطرح نمایید.
افزونههای بررسی خارج از ساختار درختی (Out-of-tree)
توسعه یک بررسی خارج از درخت ساخت بهصورت افزونه تا حد زیادی از مراحل شرحدادهشده در بالا پیروی میکند، از جمله ایجاد ماژول جدید و انجام ترفندهای لازم برای ثبت ماژول. این افزونه یک کتابخانه مشترک (shared library) است که کد آن بیرون از سیستم ساخت clang-tidy قرار دارد. این کتابخانه مشترک را همانطور که برای سایر انواع افزونههای Clang انجام میشود، کامپایل کرده و به LLVM پیوند (link) دهید. در صورت استفاده از CMake، هنگام فراخوانی add_library یا llvm_add_library از کلیدواژه MODULE استفاده کنید.
افزونه را میتوان با ارسال آرگومان -load به clang-tidy علاوه بر نامهای بررسیهای مورد نظر جهت فعالسازی، بارگذاری کرد.
$ clang-tidy --checks=-*,my-explicit-constructor -list-checks -load myplugin.so
هیچ تضمینی در رابطه با پایداری ABI و API وجود ندارد، بنابراین افزونه باید دقیقاً با همان نگارش از clang-tidy کامپایل شود که قرار است افزونه را بارگذاری نماید.
این افزونهها میتوانند از ریسهها (threads)، متغیرهای TLS، یا هر امکانات دیگری که برای کدهای دروندرختی فراهم بوده و از طریق هدرهای خارجی در دسترس است، استفاده کنند.
توجه داشته باشید که آزمودن بررسیهای out-of-tree ممکن است نیازمند دریافت llvm-lit از یک نسخه نصبشده LLVM باشد که از منبع کامپایل شده است. برای روشهای انجام این کار، بخش Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html را ببینید.
همچنین میتوانید ابزار lit https://llvm.org/docs/CommandGuide/lit.html را با پیروی از راهنمای test-suite https://llvm.org/docs/TestSuiteGuide.html و فایل اجرایی FileCheck https://llvm.org/docs/CommandGuide/FileCheck.html به دست آورید، و نگارشی از check_clang_tidy.py https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/test/clang-tidy/check_clang_tidy.py را متناسب با نیاز خود بنویسید.
اجرای clang-tidy روی LLVM
برای آزمایش یک بررسی، بهتر است آن را روی یک پایگاه کد بزرگتر امتحان کنید. LLVM و Clang اهداف طبیعی هستند، چرا که از پیش کد منبع آنها را در اختیار دارید. راحتترین روش برای اجرای clang-tidy با استفاده از پایگاهداده دستورات کامپایل (compile command database) است؛ CMake میتواند بهطور خودکار آن را تولید کند؛ برای مشاهده توضیح چگونگی فعالسازی آن، به How To Setup Clang Tooling For LLVM https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html مراجعه کنید. هنگامی که compile_commands.json در محل قرار گرفت و یک نسخه کاربردی از clang-tidy در PATH وجود داشت، کل پایگاه کد میتواند با clang-tidy/tool/run-clang-tidy.py تحلیل شود. این اسکریپت clang-tidy را با مجموعه پیشفرض بررسیها روی تمام واحدهای ترجمه در پایگاهداده دستورات کامپایل اجرا کرده و هشدارها و خطاهای حاصل را نمایش میدهد. این اسکریپت فلگهای پیکربندی متعددی را فراهم میکند.
- مجموعه پیشفرض بررسیها را میتوان با استفاده از آرگومان -checks لغو و بازنویسی کرد، که قالبی دقیقاً مشابه با قالب clang-tidy میپذیرد. برای نمونه، -checks=-*,modernize-use-override فقط بررسی modernize-use-override را اجرا خواهد کرد.
- برای محدود کردن فایلهای مورد بررسی، میتوانید یک یا چند آرگومان regex ارائه دهید که نام فایلها با آنها تطبیق داده میشوند. دستور run-clang-tidy.py clang-tidy/.*Check\.cpp تنها بررسیهای clang-tidy را تحلیل خواهد کرد. همچنین ممکن است نیاز باشد فایلهای سرآیندی که هشدارها از آنها نمایش داده میشوند را با استفاده از فلگهای -header-filter و -exclude-header-filter محدود کنید. عملکرد آنها با فلگهای متناظر در clang-tidy یکسان است.
- برای اعمال اصلاحات پیشنهادی، میتوان -fix را به عنوان یک آرگومان ارسال کرد. این کار تمام تغییرات را در یک دایرکتوری موقت جمعآوری کرده و آنها را اعمال میکند. ارسال -format باعث اجرای clang-format روی خطوط تغییریافته خواهد شد.
پیرامون پروفایلبندی بررسیها
ابزار clang-tidy میتواند اطلاعات پروفایلبندی را به تفکیک بررسیها جمعآوری کرده و آن را به ازای هر فایل منبع پردازششده (واحد ترجمه) در خروجی نمایش دهد.
برای فعالسازی جمعآوری اطلاعات پروفایلبندی، از آرگومان -enable-check-profile استفاده کنید. زمانبندیها در قالب یک جدول در stderr نمایش داده خواهند شد. نمونه خروجی:
$ clang-tidy -enable-check-profile -checks=-*,readability-function-size source.cpp
===-------------------------------------------------------------------------===
clang-tidy checks profiling
===-------------------------------------------------------------------------===
Total Execution Time: 1.0282 seconds (1.0258 wall clock)
---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) readability-function-size
0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) Total
همچنین میتواند این دادهها را به عنوان فایلهای JSON برای پردازشهای بعدی ذخیره کند. نمونه خروجی:
$ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp
$ # Note that there won't be timings table printed to the console.
$ ls /tmp/out/
20180516161318717446360-source.cpp.json
$ cat 20180516161318717446360-source.cpp.json
{
"file": "/path/to/source.cpp",
"timestamp": "2018-05-16 16:13:18.717446360",
"profile": {
"time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00,
"time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01,
"time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01
}
}
تنها یک آرگومان برای کنترل ذخیرهسازی پروفایل وجود دارد:
- •
- -store-check-profile=<prefix>
بهطور پیشفرض، گزارشها در قالب جدولبندیشده در stderr چاپ میشوند. هنگامی که این گزینه ارسال شود، این پروفایلهای به ازای هر TU به جای آن بهصورت JSON ذخیره میشوند. اگر پیشوند یک مسیر مطلق نباشد، نسبتی از دایرکتوری در نظر گرفته میشود که clang-tidy را از آنجا اجرا کردهاید. تمام الگوهای . و .. در مسیر باز شده و پیوندهای نمادین (symlinks) حل میشوند.
مثال: فرض کنید یک فایل منبع به نام example.cpp دارید که در دایرکتوری /source قرار دارد. تنها نام فایل ورودی استفاده میشود، نه مسیر کامل به فایل منبع. علاوه بر این، برچسب زمانی (timestamp) فعلی به عنوان پیشوند به آن اضافه میشود.
- اگر -store-check-profile=/tmp را مشخص کنید، پروفایل در /tmp/<ISO8601-like timestamp>-example.cpp.json ذخیره خواهد شد.
- اگر clang-tidy را از داخل دایرکتوری /foo اجرا کنید و -store-check-profile=. را مشخص نمایید، پروفایل همچنان در /foo/<ISO8601-like timestamp>-example.cpp.json ذخیره خواهد شد.
نمونههای خارجی Clang-Tidy
مقدمه
این صفحه نمونههایی از کارهایی که افراد با clang-tidy انجام دادهاند را فراهم میکند که میتواند به عنوان راهنماهای مفید (یا نقاط شروع) برای توسعه بررسیهای خودتان به کار رود. آنها ممکن است برای موارد ضروری مانند نحوه نوشتن CMakeLists.txt برای یک پلاگین خارج از درخت (out-of-tree) از بررسیهای clang-tidy مفید باشند.
اگر ابزار یا پروژهای میشناسید (یا نوشتهاید!) که از clang-tidy استفاده میکند، لطفاً آن را در تالارهای گفتگوی Discourse (دستهبندی Clang Frontend) https://discourse.llvm.org/c/clang/6 برای دیدهشدن بیشتر به اشتراک بگذارید و یک درخواست pull در گیتهاب LLVM https://github.com/llvm/llvm-project باز کنید تا در اینجا اضافه شود. از آنجا که هدف اصلی این صفحه ارائه نمونههایی است که بتواند به توسعهدهندگان کمک کند، پروژههای فهرستشده باید کد دردسترس داشته باشند.
از آنجا که clang-tidy برای مثال از AST Matchers و عیبیابیهای Clang استفاده میکند، مشاهده External Clang Examples https://clang.llvm.org/docs/ExternalClangExamples.html نیز ممکن است برای چنین نمونههایی مفید باشد.
فهرست پروژهها و ابزارها
- https://github.com/coveooss/clang-tidy-plugin-examples
- "این پوشه شامل پلاگینهای clang-tidy است."
ابزار clang-tidy یک ابزار «linter» برای C++ بر پایه clang است. هدف آن فراهم کردن چارچوبی با قابلیت گسترش برای عیبیابی و برطرف کردن خطاهای معمول برنامهنویسی است، مانند نقض شیوهنامهها، استفاده نادرست از رابطها، یا باگهایی که از طریق تحلیل ایستاوستا قابل استنتاج هستند. ابزار clang-tidy ماژولار بوده و رابطی راحت برای نوشتن بررسیهای جدید فراهم میسازد.
استفاده از Clang-Tidy
ابزار clang-tidy ابزاری مبتنی بر LibTooling https://clang.llvm.org/docs/LibTooling.html است، و اگر یک پایگاهداده دستورات کامپایل برای پروژه خود تنظیم کنید کار با آن آسانتر است (برای نمونهای از نحوه انجام این کار، به How To Setup Tooling For LLVM https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html مراجعه کنید). همچنین میتوانید گزینههای کامپایل را در خط فرمان پس از -- مشخص کنید:
$ clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...
اگر گزینهها یا فایلهای منبع بسیار زیادی برای تعیین در خط فرمان وجود دارد، میتوانید آنها را در یک فایل پارامتر ذخیره کرده و clang-tidy را با آن فایل پارامترها به کار ببرید:
$ clang-tidy @parameters_file
ابزار clang-tidy بررسیهای اختصاصی خود را داشته و همچنین میتواند بررسیهای Clang Static Analyzer را اجرا کند. هر بررسی دارای یک نام است و بررسیها برای اجرا میتوانند با استفاده از گزینه -checks= انتخاب شوند، که فهرستی جداشده با کاما از الگوهای تطبیق مثبت و منفی (با پیشوند -) را مشخص میکند. الگوهای مثبت زیرمجموعههایی از بررسیها را اضافه کرده و الگوهای منفی آنها را حذف میکنند. برای مثال،
$ clang-tidy test.cpp -checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus*
تمام بررسیهای پیشفرض (-*) را غیرفعال کرده و همه بررسیهای clang-analyzer-* را به جز موارد clang-analyzer-cplusplus* فعال خواهد کرد.
گزینه -list-checks تمام بررسیهای فعالشده را فهرست میکند. هنگامی که بدون -checks= استفاده شود، بررسیهای فعال بهطور پیشفرض را نمایش میدهد. از -checks=* برای مشاهده تمام بررسیهای موجود یا از هر مقدار دیگری برای -checks= برای مشاهده اینکه چه بررسیهایی توسط آن مقدار فعال میشوند استفاده کنید.
در حال حاضر گروههای زیر از بررسیها وجود دارند:
| پیشوند نام | توضیحات |
| abseil- | بررسیهای مرتبط با کتابخانه Abseil. |
| altera- | بررسیهای مرتبط با برنامهنویسی OpenCL برای FPGAها. |
| android- | بررسیهای مرتبط با Android. |
| boost- | بررسیهای مرتبط با کتابخانه Boost. |
| bugprone- | بررسیهایی که ساختارهای مستعد باگ را هدف قرار میدهند. |
| cert- | بررسیهای مرتبط با رهنمودهای کدنویسی امن CERT. |
| clang-analyzer- | بررسیهای Clang Static Analyzer. |
| concurrency- | بررسیهای مرتبط با برنامهنویسی همگام (شامل رشتهها، fiberها، coroutineها و غیره). |
| cppcoreguidelines- | بررسیهای مرتبط با C++ Core Guidelines. |
| darwin- | بررسیهای مرتبط با قواعد کدنویسی Darwin. |
| fuchsia- | بررسیهای مرتبط با قواعد کدنویسی Fuchsia. |
| google- | بررسیهای مرتبط با قواعد کدنویسی Google. |
| hicpp- | بررسیهای مرتبط با استاندارد کدنویسی High Integrity C++. |
| linuxkernel- | بررسیهای مرتبط با قواعد کدنویسی Linux Kernel. |
| llvm- | بررسیهای مرتبط با قواعد کدنویسی LLVM. |
| llvmlibc- | بررسیهای مرتبط با استانداردهای کدنویسی LLVM-libc. |
| misc- | بررسیهایی که دستهبندی بهتری برای آنها نداشتیم. |
| modernize- | بررسیهایی که استفاده از ساختارهای مدرن زبان (در حال حاضر «مدرن» به معنای «C++11» است) را توصیه میکنند. |
| mpi- | بررسیهای مرتبط با MPI (Message Passing Interface). |
| objc- | بررسیهای مرتبط با قواعد کدنویسی Objective-C. |
| openmp- | بررسیهای مرتبط با OpenMP API. |
| performance- | بررسیهایی که مسائل مرتبط با کارایی و عملکرد را هدف قرار میدهند. |
| portability- | بررسیهایی که مسائل مرتبط با قابلیت حمل را هدف قرار میدهند که به هیچ شیوه کدنویسی خاصی وابسته نیستند. |
| readability- | بررسیهایی که مسائل مرتبط با خوانایی را هدف قرار میدهند که به هیچ شیوه کدنویسی خاصی وابسته نیستند. |
| zircon- | بررسیهای مرتبط با قواعد کدنویسی هسته Zircon. |
پیامهای عیبیابی Clang مشابهبا عیبیابیهای بررسیها مدیریت میشوند. پیامهای عیبیابی Clang توسط clang-tidy نمایش داده میشوند و میتوان آنها را با استفاده از گزینهٔ -checks= فیلتر کرد. با این حال، گزینهٔ -checks= آرگومانهای کامپایل را تغییر نمیدهد، بنابراین نمیتواند هشدارهای Clang را که در پیکربندی ساخت فعال نشدهاند روشن کند. گزینهٔ -warnings-as-errors= هرگونه هشدار صادرشده تحت فلگ -checks= را به خطا ارتقا میدهد (اما خودش هیچ بررسی جدیدی را فعال نمیکند).
پیامهای عیبیابی Clang دارای نام بررسی هستند که با clang-diagnostic- آغاز میشود. عیبیابیهایی که دارای گزینهٔ هشدار متناظر هستند، بهصورت clang-diagnostic-<warning-option> نامگذاری میشوند، برای نمونه هشدار Clang که با -Wliteral-conversion کنترل میشود با نام بررسی clang-diagnostic-literal-conversion گزارش خواهد شد.
خطاهای کامپایلر Clang (مانند خطاهای نحوی، خطاهای معنایی یا سایر خرابیهایی که مانع از کامپایل کد توسط Clang میشوند) با نام بررسی clang-diagnostic-error گزارش میگردند. این موارد نشاندهندهٔ خطاهای بنیادین کامپایل هستند که باید پیش از اینکه clang-tidy بتواند تحلیل خود را انجام دهد، برطرف شوند. بر خلاف سایر عیبیابیها، clang-diagnostic-error را نمیتوان غیرفعال کرد، زیرا clang-tidy برای کارکردن به کدی معتبر نیاز دارد.
فلگ -fix به clang-tidy دستور میدهد در صورت پشتیبانی توسط بررسیهای متناظر، خطاهای یافتشده را اصلاح کند.
نمای کلی تمام گزینههای خط فرمان:
$ clang-tidy --help
USAGE: clang-tidy [options] <source0> [... <sourceN>]
OPTIONS:
Generic Options:
--help - Display available options (--help-hidden for more)
--help-list - Display list of available options (--help-list-hidden for more)
--version - Display the version of this program
clang-tidy options:
--checks=<string> - Comma-separated list of globs with optional '-'
prefix. Globs are processed in order of
appearance in the list. Globs without '-'
prefix add checks with matching names to the
set, globs with the '-' prefix remove checks
with matching names from the set of enabled
checks. This option's value is appended to the
value of the 'Checks' option in .clang-tidy
file, if any.
--config=<string> - Specifies a configuration in YAML/JSON format:
-config="{Checks: '*',
CheckOptions: {x: y}}"
When the value is empty, clang-tidy will
attempt to find a file named .clang-tidy for
each source file in its parent directories.
--config-file=<string> - Specify the path of .clang-tidy or custom config file:
e.g. --config-file=/some/path/myTidyConfigFile
This option internally works exactly the same way as
--config option after reading specified config file.
Use either --config-file or --config, not both.
--dump-config - Dumps configuration in the YAML format to
stdout. This option can be used along with a
file name (and '--' if the file is outside of a
project with configured compilation database).
The configuration used for this file will be
printed.
Use along with -checks=* to include
configuration of all checks.
--enable-check-profile - Enable per-check timing profiles, and print a
report to stderr.
--enable-module-headers-parsing - Enables preprocessor-level module header parsing
for C++20 and above, empowering specific checks
to detect macro definitions within modules. This
feature may cause performance and parsing issues
and is therefore considered experimental.
--exclude-header-filter=<string> - Regular expression matching the names of the
headers to exclude diagnostics from. Diagnostics
from the main file of each translation unit are
always displayed.
Must be used together with --header-filter.
Can be used together with -line-filter.
This option overrides the 'ExcludeHeaderFilterRegex'
option in .clang-tidy file, if any.
--explain-config - For each enabled check explains, where it is
enabled, i.e. in clang-tidy binary, command
line or a specific configuration file.
--export-fixes=<filename> - YAML file to store suggested fixes in. The
stored fixes can be applied to the input source
code with clang-apply-replacements.
--extra-arg=<string> - Additional argument to append to the compiler command line
--extra-arg-before=<string> - Additional argument to prepend to the compiler command line
--fix - Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
--fix-errors - Apply suggested fixes even if compilation
errors were found. If compiler errors have
attached fix-its, clang-tidy will apply them as
well.
--fix-notes - If a warning has no fix, but a single fix can
be found through an associated diagnostic note,
apply the fix.
Specifying this flag will implicitly enable the
'--fix' flag.
--format-style=<string> - Style for formatting code around applied fixes:
- 'none' (default) turns off formatting
- 'file' (literally 'file', not a placeholder)
uses .clang-format file in the closest parent
directory
- '{ <json> }' specifies options inline, e.g.
-format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
- 'llvm', 'google', 'webkit', 'mozilla'
See clang-format documentation for the up-to-date
information about formatting styles and options.
This option overrides the 'FormatStyle` option in
.clang-tidy file, if any.
--header-filter=<string> - Regular expression matching the names of the
headers to output diagnostics from. The default
value is '.*', i.e. diagnostics from all non-system
headers are displayed by default. Diagnostics
from the main file of each translation unit are
always displayed.
Can be used together with -line-filter.
This option overrides the 'HeaderFilterRegex'
option in .clang-tidy file, if any.
--line-filter=<string> - List of files and line ranges to output diagnostics from.
The range is inclusive on both ends. Can be used together
with -header-filter. The format of the list is a JSON
array of objects. For example:
[
{"name":"file1.cpp","lines":[[1,3],[5,7]]},
{"name":"file2.h"}
]
This will output diagnostics from 'file1.cpp' only for
the line ranges [1,3] and [5,7], as well as all from the
entire 'file2.h'.
--list-checks - List all enabled checks and exit. Use with
-checks=* to list all available checks.
--load=<pluginfilename> - Load the specified plugin
-p <string> - Build path
--quiet - Run clang-tidy in quiet mode. This suppresses
printing statistics about ignored warnings and
warnings treated as errors if the respective
options are specified.
--store-check-profile=<prefix> - By default reports are printed in tabulated
format to stderr. When this option is passed,
these per-TU profiles are instead stored as JSON.
--system-headers - Display the errors from system headers.
This option overrides the 'SystemHeaders' option
in .clang-tidy file, if any.
--use-color - Use colors in diagnostics. If not set, colors
will be used if the terminal connected to
standard output supports colors.
This option overrides the 'UseColor' option in
.clang-tidy file, if any.
--verify-config - Check the config files to ensure each check and
option is recognized.
--vfsoverlay=<filename> - Overlay the virtual filesystem described by file
over the real file system.
--warnings-as-errors=<string> - Upgrades warnings to errors. Same format as
'-checks'.
This option's value is appended to the value of
the 'WarningsAsErrors' option in .clang-tidy
file, if any.
--allow-no-checks - Allow empty enabled checks. This suppresses
the "no checks enabled" error when disabling
all of the checks.
-p <build-path> is used to read a compile command database.
For example, it can be a CMake build directory in which a file named
compile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
CMake option to get this output). When no build path is specified,
a search for compile_commands.json will be attempted through all
parent paths of the first input file . See:
<https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html> for an
example of setting up Clang Tooling on a source tree.
<source0> ... specify the paths of source files. These paths are
looked up in the compile command database. If the path of a file is
absolute, it needs to point into CMake's source tree. If the path is
relative, the current working directory needs to be in the CMake
source tree and the file must be in a subdirectory of the current
working directory. "./" prefixes in the relative files will be
automatically removed, but the rest of a relative path must be a
suffix of a path in the compile command database.
Parameters files:
A large number of options or source files can be passed as parameter files
by use '@parameter-file' in the command line.
Configuration files:
clang-tidy attempts to read configuration for each source file from a
.clang-tidy file located in the closest parent directory of the source
file. The .clang-tidy file is specified in YAML format. If any configuration
options have a corresponding command-line option, command-line option takes
precedence.
The following configuration options may be used in a .clang-tidy file:
CheckOptions - List of key-value pairs defining check-specific
options. Example:
CheckOptions:
some-check.SomeOption: 'some value'
Checks - Same as '--checks'. Additionally, the list of
globs can be specified as a list instead of a
string.
CustomChecks - List of user defined checks based on
Clang-Query syntax.
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
ExtraArgs - Same as '--extra-arg'.
ExtraArgsBefore - Same as '--extra-arg-before'.
FormatStyle - Same as '--format-style'.
HeaderFileExtensions - File extensions to consider to determine if a
given diagnostic is located in a header file.
HeaderFilterRegex - Same as '--header-filter'.
ImplementationFileExtensions - File extensions to consider to determine if a
given diagnostic is located in an
implementation file.
InheritParentConfig - If this option is true in a config file, the
configuration file in the parent directory
(if any exists) will be taken and the current
config file will be applied on top of the
parent one.
SystemHeaders - Same as '--system-headers'.
UseColor - Same as '--use-color'.
User - Specifies the name or e-mail of the user
running clang-tidy. This option is used, for
example, to place the correct user name in
TODO() comments in the relevant check.
WarningsAsErrors - Same as '--warnings-as-errors'.
RemovedArgs - Same as '--removed-arg'
The effective configuration can be inspected using --dump-config:
$ clang-tidy --dump-config
---
Checks: '-*,some-check'
WarningsAsErrors: ''
HeaderFileExtensions: ['', 'h','hh','hpp','hxx']
ImplementationFileExtensions: ['c','cc','cpp','cxx']
HeaderFilterRegex: '.*'
FormatStyle: none
InheritParentConfig: true
User: user
CheckOptions:
some-check.SomeOption: 'some value'
...
خودکارسازی Clang-Tidy
clang-tidy میتواند چندین فایل سورس را با مشخص کردن آنها در خط فرمان تحلیل کند. برای پروژههای بزرگتر، اسکریپتهای خودکارسازی قابلیتهای بیشتری مانند اجرای موازی و یکپارچهسازی با سیستمهای کنترل نسخه فراهم میکنند.
اجرای موازی Clang-Tidy
clang-tidy میتواند چندین فایل را بهصورت متوالی پردازش کند، اما برای پروژههایی با فایلهای سورس پرتعداد، اسکریپت run-clang-tidy.py اجرای موازی را جهت کاهش چشمگیر زمان تحلیل ارائه میدهد. این اسکریپت همراه با clang-tidy عرضه میشود و clang-tidy را روی تمام فایلهای موجود در پایگاهداده کامپایل یا یک مسیر مشخص بهطور همروند اجرا میکند.
این اسکریپت به یک پایگاهداده کامپایل (compile_commands.json) نیاز دارد که میتواند توسط سیستمهای ساخت مانند CMake (با استفاده از -DCMAKE_EXPORT_COMPILE_COMMANDS=ON) یا توسط ابزارهایی مانند Bear https://github.com/rizsotto/Bear ایجاد شود.
این اسکریپت بیشتر گزینههای خود clang-tidy از جمله -checks=، -fix، -header-filter= و گزینههای پیکربندی را پشتیبانی میکند. برای مشاهده فهرست کامل گزینههای موجود، run-clang-tidy.py --help را اجرا کنید.
نمونه فراخوانیها:
# Run clang-tidy on all files in the compilation database in parallel $ run-clang-tidy.py -p=build/ # Run with specific checks and apply fixes $ run-clang-tidy.py -p=build/ -fix -checks=-*,readability-* # Run on specific files/directories with header filtering $ run-clang-tidy.py -p=build/ -header-filter=src/ src/ # Run with parallel execution (uses all CPU cores by default) $ run-clang-tidy.py -p=build/ -j 4
اجرای Clang-Tidy روی Diff
اسکریپت clang-tidy-diff.py به شما امکان میدهد clang-tidy را روی خطوطی که در دایرکتوری کاری یا در یک diff مشخص تغییر کردهاند، اجرا کنید. نکته مهم این است که clang-tidy-diff.py تنها عیبیابیها را برای خطوط تغییریافته گزارش میدهد؛ clang-tidy همچنان کل فایل را تحلیل میکند و خطوط تغییرنیافته را پس از تحلیل فیلتر میکند، بنابراین این کار عملکرد را بهبود نمیبخشد. این قابلیت بهویژه برای بازبینی کد و یکپارچهسازی مداوم (CI) مفید است، چرا که تحلیل را به جای کل پایگاه کد روی کد تغییریافته متمرکز میکند.
این اسکریپت میتواند با منابع مختلف diff کار کند:
- تغییرات دایرکتوری کاری Git
- خروجی دستور git diff
- خروجی دستور svn diff
- فایلهای پچ (Patch)
نمونه فراخوانیها:
# Run clang-tidy on all changes in the working directory $ git diff -U0 --no-color HEAD^ | clang-tidy-diff.py -p1 # Run with specific checks and apply fixes $ git diff -U0 --no-color HEAD^ | clang-tidy-diff.py -p1 -fix \ -checks=-*,readability-* # Run on staged changes $ git diff -U0 --no-color --cached | clang-tidy-diff.py -p1 # Run on changes between two commits $ git diff -U0 --no-color HEAD~2 HEAD | clang-tidy-diff.py -p1 # Run on a patch file $ clang-tidy-diff.py -p1 < changes.patch
گزینه -p1 به اسکریپت میگوید یک سطح از پیشوند مسیر را از diff حذف کند، که معمولاً برای diffهای Git مورد نیاز است. این اسکریپت بیشتر گزینههای خود clang-tidy از جمله -checks=، -fix، -header-filter= و گزینههای پیکربندی را پشتیبانی میکند.
اگرچه clang-tidy-diff.py برای تمرکز بر تغییرات اخیر مفید است، اتکای صرف به آن ممکن است منجر به تحلیلی ناقص شود. از آنجا که اسکریپت تنها هشدارهای مربوط به خطوط تغییریافته را گزارش میدهد، ممکن است مشکلاتی را که ناشی از تغییرات هستند اما در جای دیگری از کد بروز میکنند از دست بدهد. برای مثال، تغییراتی که تنها خطوطی را به یک تابع اضافه میکنند ممکن است باعث نقض محدودیتهای اندازه آن شوند (مثلاً readability-function-size <checks/readability/function-size.html>)، اما عیبیابی در محل اعلان تابع گزارش خواهد شد که ممکن است در diff نباشد و در نتیجه فیلتر شود. همچنین اصلاح فایلهای هدر ممکن است بسیاری از فایلهای پیادهسازی را تحت تأثیر قرار دهد، اما تنها هشدارهای موجود در خطوط تغییریافته هدر گزارش خواهند شد.
برای تحلیل جامع، بهویژه پیش از ادغام (merge) تغییرات مهم، اجرای clang-tidy روی کل فایلهای متأثر یا کل پروژه را با استفاده از run-clang-tidy.py در نظر بگیرید.
فرونشاندن عیبیابیهای ناخواسته
عیبیابیهای clang-tidy با این هدف در نظر گرفته شدهاند که کدهایی را که از یک استاندارد کدنویسی پیروی نمیکنند یا به هر شکلی مشکلساز هستند، مشخص کنند. با این حال، اگر از صحت کد اطمینان وجود دارد، بیصدا کردن هشدار میتواند مفید باشد. برخی از بررسیهای clang-tidy روشی مخصوص به خود را برای بیصدا کردن عیبیابیها ارائه میدهند؛ برای نمونه bugprone-use-after-move <checks/bugprone/use-after-move.html> میتواند با مقداردهی اولیه مجدد متغیر پس از انتقال (move) بیصدا شود، bugprone-string-integer-assignment <checks/bugprone/string-integer-assignment.html> میتواند با تبدیل نوع صریح مقدار صحیح به char فرونشانده شود، readability-implicit-bool-conversion <checks/readability/implicit-bool-conversion.html> نیز با استفاده از تبدیلهای نوع صریح قابل فرونشاندن است، و غیره.
اگر سازوکار فرونشانی مشخصی برای یک هشدار خاص در دسترس نباشد، یا استفاده از آن به هر دلیلی مطلوب نباشد، clang-tidy سازوکاری عمومی برای فرونشاندن عیبیابیها با استفاده از کامنتهای NOLINT، NOLINTNEXTLINE و NOLINTBEGIN ... NOLINTEND دارد.
کامنت NOLINT به clang-tidy دستور میدهد هشدارهای مربوط به همان خط را نادیده بگیرد (این کامنت به یک تابع، یک بلوک کد یا هیچ ساختار زبانی دیگری اعمال نمیشود؛ بلکه به همان خط کدی که در آن قرار دارد اعمال میشود). اگر درج کامنت در همان خط قالببندی را به شکل ناخواستهای تغییر دهد، کامنت NOLINTNEXTLINE امکان فرونشاندن هشدارهای clang-tidy را در خط بعدی فراهم میکند. کامنتهای NOLINTBEGIN و NOLINTEND امکان فرونشاندن هشدارهای clang-tidy را روی چندین خط (تأثیرگذار روی تمام خطوط بین دو کامنت) ممکن میسازند.
پس از تمام کامنتها میتواند یک فهرست اختیاری از نامهای بررسی در داخل پرانتز بیاید (سینتکس رسمی را در زیر مشاهده کنید). فهرست نامهای بررسی از globbing پشتیبانی میکند، با همان فرمت و معنایی که برای فعالسازی بررسیها به کار میرود. توجه: الگوهای منفی (negative globs) در اینجا نادیده گرفته میشوند، زیرا عملاً موجب فعالسازی مجدد هشدار خواهند شد.
برای مثال:
class Foo {
// Suppress all the diagnostics for the line
Foo(int param); // NOLINT
// Consider explaining the motivation to suppress the warning
Foo(char param); // NOLINT: Allow implicit conversion from `char`, because <some valid reason>
// Silence only the specified checks for the line
Foo(double param); // NOLINT(google-explicit-constructor, google-runtime-int)
// Silence all checks from the `google` module
Foo(bool param); // NOLINT(google*)
// Silence all checks ending with `-avoid-c-arrays`
int array[10]; // NOLINT(*-avoid-c-arrays)
// Silence only the specified diagnostics for the next line
// NOLINTNEXTLINE(google-explicit-constructor, google-runtime-int)
Foo(bool param);
// Silence all checks from the `google` module for the next line
// NOLINTNEXTLINE(google*)
Foo(bool param);
// Silence all checks ending with `-avoid-c-arrays` for the next line
// NOLINTNEXTLINE(*-avoid-c-arrays)
int array[10];
// Silence only the specified checks for all lines between the BEGIN and END
// NOLINTBEGIN(google-explicit-constructor, google-runtime-int)
Foo(short param);
Foo(long param);
// NOLINTEND(google-explicit-constructor, google-runtime-int)
// Silence all checks from the `google` module for all lines between the BEGIN and END
// NOLINTBEGIN(google*)
Foo(bool param);
// NOLINTEND(google*)
// Silence all checks ending with `-avoid-c-arrays` for all lines between the BEGIN and END
// NOLINTBEGIN(*-avoid-c-arrays)
int array[10];
// NOLINTEND(*-avoid-c-arrays)
};
سینتکس رسمی NOLINT، NOLINTNEXTLINE و NOLINTBEGIN ... NOLINTEND به صورت زیر است:
lint-comment: lint-command lint-command lint-args lint-args: ( check-name-list ) check-name-list: check-name check-name-list , check-name lint-command: NOLINT NOLINTNEXTLINE NOLINTBEGIN NOLINTEND
توجه داشته باشید که فاصلهها بین NOLINT/NOLINTNEXTLINE/NOLINTBEGIN/NOLINTEND و پرانتز باز مجاز نیستند (در این حالت با کامنت همانند NOLINT/NOLINTNEXTLINE/NOLINTBEGIN/NOLINTEND رفتار خواهد شد)، در حالی که در فهرست نام بررسیها (داخل پرانتز)، فاصلهها میتوانند استفاده شوند و نادیده گرفته خواهند شد.
تمام کامنتهای NOLINTBEGIN باید با تعداد برابری از کامنتهای NOLINTEND جفت شوند. علاوه بر این، یک جفت کامنت باید شناسهها (آرگومانهای) مطابقی داشته باشند – برای مثال، NOLINTBEGIN(check-name) میتواند با NOLINTEND(check-name) جفت شود اما با NOLINTEND (بدون شناسه) خیر. اگر هر یک از کامنتهای NOLINTBEGIN/NOLINTEND این نیازمندیها را نقض کند، clang-tidy یک خطای عیبیابی clang-tidy-nolint تولید خواهد کرد.
CLANG-INCLUDE-FIXER
فهرست مطالب
- •
- Clang-Include-Fixer
- •
- راهاندازی
- ایجاد نمایه نماد از پایگاهداده ترجمه
- یکپارچهسازی با Vim
- یکپارچهسازی با Emacs
- •
- نحوه کارکرد
یکی از دردسرهای بزرگ ++C در مقایسه با سایر زبانها، مدیریت دستی دایرکتیوهای #include در فایلها است. ابزار clang-include-fixer با فراهم آوردن روشی خودکار برای افزودن دایرکتیوهای #include برای نمادهای ناموجود در یک واحد ترجمه (translation unit)، به حل جنبهای از این مشکل میپردازد.
هنگام درج #include ناموجود، در صورتی که نماد فاقد برخی پیشوندهای namespace باشد، clang-include-fixer توصیفکنندههای namespace ناموجود را نیز به تمام نمونههای یک نماد ناشناخته اضافه میکند.
راهاندازی
برای استفاده از clang-include-fixer دو پایگاهداده مورد نیاز است. هر دو را میتوان با ابزارهای موجود تولید کرد.
- پایگاهداده ترجمه (Compilation database). شامل دستورهای کامپایلر برای هر فایل در پروژه است و میتواند توسط CMake تولید شود؛ به بخش نحوه راهاندازی ابزارها برای LLVM مراجعه کنید: https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html.
- نمایه نماد (Symbol index). شامل تمام اطلاعات نمادها در یک پروژه برای تطبیق یک شناسه مشخص با یک فایل هدر است.
در حالت ایدهآل هر دو پایگاهداده (compile_commands.json و find_all_symbols_db.yaml) به ریشه درخت منبع مربوطهشان لینک میشوند. در این صورت اگر clang-include-fixer با فایلی از آن درخت فراخوانی شود، میتواند بهطور خودکار آنها را شناسایی کند. توجه داشته باشید که بهطور پیشفرض، فایل compile_commands.json تولیدشده توسط CMake شامل فایلهای هدر نمیشود؛ بنابراین ابزارها فقط میتوانند فایلهای پیادهسازی را پردازش کنند.
ایجاد نمایه نماد از پایگاهداده ترجمه
بسته include fixer شامل find-all-symbols است؛ ابزاری برای ایجاد پایگاهداده نماد در قالب YAML از یک پایگاهداده ترجمه از طریق تجزیه تمام فایلهای منبع فهرستشده در آن. فهرست دستورهای زیر نحوه راهاندازی پایگاهداده را برای LLVM نشان میدهد؛ هر پروژهای که با CMake ساخته شده باشد باید مراحل مشابهی را طی کند.
$ cd path/to/llvm-build $ ninja find-all-symbols // build find-all-symbols tool. $ ninja clang-include-fixer // build clang-include-fixer tool. $ ls compile_commands.json # Make sure compile_commands.json exists. compile_commands.json $ path/to/llvm/source/clang-tools-extra/clang-include-fixer/find-all-symbols/tool/run-find-all-symbols.py ... wait as clang indexes the code base ... $ ln -s $PWD/find_all_symbols_db.yaml path/to/llvm/source/ # Link database into the source tree. $ ln -s $PWD/compile_commands.json path/to/llvm/source/ # Also link compilation database if it's not there already. $ cd path/to/llvm/source $ /path/to/clang-include-fixer -db=yaml path/to/file/with/missing/include.cpp Added #include "foo.h"
یکپارچهسازی با Vim
برای اجرای clang-include-fixer بر روی یک بافر احتمالاً ذخیرهنشده در Vim، کلید میانبر زیر را به فایل .vimrc خود اضافه کنید:
noremap <leader>cf :pyf path/to/llvm/source/clang-tools-extra/clang-include-fixer/tool/clang-include-fixer.py<cr>
این کار ابزار clang-include-fixer را برای حالتهای NORMAL و VISUAL فعال میکند. اگر به میانبر دیگری برای clang-include-fixer نیاز دارید، مقدار <leader>cf را تغییر دهید. کلید <leader> http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader اشاره به کلید خاصی دارد که با متغیر mapleader تعریف میشود و بهطور پیشفرض به بکاسلش اختصاص دارد.
اطمینان حاصل کنید که vim میتواند clang-include-fixer را پیدا کند:
- مسیر clang-include-fixer را به متغیر محیطی PATH اضافه کنید.
- یا متغیر g:clang_include_fixer_path را در vimrc تنظیم کنید: let g:clang_include_fixer_path=path/to/clang-include-fixer
میتوانید تعداد هدرهای پیشنهادی نمایش دادهشده را با این تنظیم سفارشی کنید: let g:clang_include_fixer_maximum_suggested_headers=5
تنظیمات سفارشی در .vimrc:
- let g:clang_include_fixer_path = "clang-include-fixer"
تنظیم مسیر فایل باینری clang-include-fixer.
- let g:clang_include_fixer_maximum_suggested_headers = 3
تنظیم حداکثر تعداد #includes برای نمایش. مقدار پیشفرض 3 است.
- let g:clang_include_fixer_increment_num = 5
تنظیم تعداد افزایش هدرهای include در هر بار فشردن کلید m. مقدار پیشفرض 5 است.
- let g:clang_include_fixer_jump_to_include = 0
اگر میخواهید نشانگر پس از درج به خط #include جدید بپرد، مقدار را روی 1 قرار دهید. مقدار پیشفرض 0 است.
- let g:clang_include_fixer_query_mode = 0
اگر میخواهید #include مربوط به نماد زیر مکاننما درج شود، مقدار را روی 1 قرار دهید. مقدار پیشفرض 0 است. در مقایسه با حالت عادی، این حالت فایل منبع را تجزیه نمیکند و فقط نماد را از پایگاهداده جستجو میکند که از حالت عادی سریعتر است.
برای جزئیات بیشتر به clang-include-fixer.py مراجعه کنید.
یکپارچهسازی با Emacs
برای اجرای clang-include-fixer بر روی یک بافر ذخیرهنشده در Emacs، با افزودن دایرکتوری حاوی فایل به load-path و require کردن clang-include-fixer در فایل .emacs، اطمینان حاصل کنید که Emacs میتواند clang-include-fixer.el را پیدا کند:
(add-to-list 'load-path "path/to/llvm/source/clang-tools-extra/clang-include-fixer/tool/" (require 'clang-include-fixer)
در Emacs، این ابزار را میتوان با دستور M-x clang-include-fixer فراخوانی کرد. این کار هدری را که اولین نماد تعریفنشده را تعریف میکند درج خواهد کرد؛ اگر بیش از یک هدر وجود داشته باشد که نماد را تعریف کند، از کاربر خواسته میشود تا یکی را انتخاب کند.
برای درج هدری که نماد واقع در مکان فعلی نشانگر را تعریف میکند، دستور M-x clang-include-fixer-at-point را اجرا کنید.
اطمینان حاصل کنید که Emacs میتواند clang-include-fixer را پیدا کند:
- •
- یا دایرکتوری والد clang-include-fixer را به متغیر محیطی PATH اضافه کنید، یا گزینه کاربری Emacs یعنی clang-include-fixer-executable را سفارشیسازی کرده تا به نام فایل برنامه اشاره کند.
نحوه کارکرد
برای دریافت بیشترین اطلاعات ممکن از Clang در زمان تجزیه (parse)، ابزار clang-include-fixer همگام با فرآیند تجزیه اجرا میشود و بازخوانیهایی (callbacks) از تحلیل معنایی Clang دریافت میکند. بهویژه، از پشتیبانی موجود برای تصحیح اشتباهات تایپی مجدداً استفاده میکند. هر زمان که Clang سعی میکند یک اشتباه تایپی بالقوه را تصحیح کند، یک بازخوانی به include fixer ارسال میکند که سپس به دنبال فایل متناظر میگردد. در این مرحله، اطلاعات غنی جستجو هنوز در دسترس است، اطلاعاتی که در مراحل بعدی در AST موجود نخواهد بود.
شناسهای که باید تصحیح تایپی شود به پایگاه داده ارسال میگردد؛ اگر یک فایل هدر بازگردانده شود، به عنوان یک دستور include در بالای فایل افزوده خواهد شد.
در حال حاضر clang-include-fixer تنها یک include را در هر بار درج میکند تا از گرفتار شدن در خطاهای متوالی بعدی جلوگیری کند. اگر اضافه شدن چند مورد #include مد نظر باشد، میتوان برنامه را تا رسیدن به یک نقطه ثبات (fix-point) دوباره اجرا کرد.
CLANG-CHANGE-NAMESPACE
محتویات
- •
- Clang-Change-Namespace
- نمونه استفاده
- نمونهای دیگر
- •
- هشدارها
- محتوا از قبل در فضای نام جدید وجود دارد
- فضای نام درونخطی (Inline namespace) کار نمیکند
- ارجاعات به نمادها بهروزرسانی نشدهاند
- •
- گزینههای خط فرمان clang-change-namespace
ابزار clang-change-namespace میتواند برای تغییر فضاهای نام احاطهکننده تعاریف کلاس/تابع استفاده شود.
کلاسها/توابع موجود در فضای نام منتقلشده، فضاهای نام جدیدی خواهند داشت، در حالی که ارجاعات به نمادهایی (مانند انواع داده، توابع) که در فضای نام تغییریافته تعریف نشدهاند، با افزودن پیشوند مشخصکنندههای فضای نام در ابتدای آنها، بهدرستی واجد شرایط (qualify) میشوند. این فرآیند تلاش میکند کوتاهترین مشخصکنندههای ممکن فضای نام را اضافه کند.
هنگامی که یک ارجاع نماد نیاز به صلاحیت کامل (fully-qualified) داشته باشد، پیشوند :: به مشخصکنندههای فضای نام اضافه میشود، مگر اینکه فضای نام جدید، فضای نام سراسری (global) باشد. برای کلاسها، تنها کلاسهایی که در فضای نام دادهشده در فایلهای مشخصشده اعلان/تعریف شده باشند منتقل خواهند شد: اعلانهای اولیه (forward declarations) در فضای نام قدیمی باقی خواهند ماند. این موضوع در مثال بعدی نشان داده خواهد شد.
نمونه استفاده
به عنوان مثال، این نمونه test.cc را با کلاس اعلان اولیهشده FWD و کلاس تعریفشده A در نظر بگیرید که هر دو در فضای نام a قرار دارند.
namespace a {
class FWD;
class A {
FWD *fwd;
};
} // namespace a
و اکنون بیایید فضای نام a را به x تغییر دهیم.
clang-change-namespace \ --old_namespace "a" \ --new_namespace "x" \ --file_pattern "test.cc" \ --i \ test.cc
توجه داشته باشید که در کد زیر، کلاس اعلان اولیهشده FWD همچنان در فضای نام a باقی مانده است. این کلاس به فضای نام جدید منتقل نشد زیرا در اینجا در a تعریف/اعلان نشده بود، بلکه فقط به صورت اولیه (forward declared) اعلان شده بود.
namespace a {
class FWD;
} // namespace a
namespace x {
class A {
a::FWD *fwd;
};
} // namespace x
نمونهای دیگر
این فایل test.cc را در نظر بگیرید:
namespace na {
class X {};
namespace nb {
class Y {
X x;
};
} // namespace nb
} // namespace na
برای انتقال تعریف کلاس Y از فضای نام na::nb به x::y، اجرا کنید:
clang-change-namespace \ --old_namespace "na::nb" \ --new_namespace "x::y" \ --file_pattern "test.cc" \ --i \ test.cc
این دستور فایل test.cc را بازنویسی میکند تا به شکل زیر درآید:
namespace na {
class X {};
} // namespace na
namespace x {
namespace y {
class Y {
na::X x;
};
} // namespace y
} // namespace x
توجه داشته باشید که ما با موفقیت کلاس Y را از فضای نام na::nb به فضای نام x::y منتقل کردیم.
هشدارها و محدودیتها
محتوا از قبل در فضای نام جدید وجود دارد
این مثال از test.cc را در نظر بگیرید که دو class A تعریف میکند، یکی درون فضای نام a و دیگری در فضای نام b:
namespace a {
class A {
int classAFromWithinNamespace_a;
};
} // namespace a
namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace b
بیایید همه چیز را از فضای نام a به فضای نام b منتقل کنیم:
clang-change-namespace \ --old_namespace "a" \ --new_namespace "b" \ --file_pattern test.cc \ test.cc
همانطور که انتظار میرود، اکنون دو تعریف از class A درون فضای نام b داریم:
namespace b {
class A {
int classAFromWithinNamespace_a;
};
} // namespace b
namespace b {
class A {
int classAFromWithinNamespace_b;
};
} //namespace b
بازسازی کد (refactoring) به ظاهر درست انجام شده است اما کد به دلیل تکرار نام کامپایل نخواهد شد. تضمین قابلیت کامپایل از این حیث بر عهدهٔ این ابزار نیست، بلکه کاربر باید نسبت به آن آگاه باشد.
عدم پشتیبانی از inline namespace
این نمونه استفاده از دو نسخه از پیادهسازیهای یک تابع greet را در نظر بگیرید:
#include <cstdio>
namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
namespace Version2 {
const char* greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace Greeter
int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}
توجه داشته باشید که در حال حاضر Greeter::greet() منجر به فراخوانی Greeter::Version1::greet() میشود زیرا آن فضای نام به صورت inlined است.
فرض کنید میخواهید یکی را تغییر داده و Version2 را به عنوان پیشفرض قرار دهید و inline را از Version1 حذف کنید. ابتدا بیایید سعی کنیم namespace Version2 را به inline namespace Version2 تبدیل نماییم:
clang-change-namespace \ --old_namespace "Greeter::Version2" \ --new_namespace "inline Version2" \ --file_pattern main.cc main.cc
اما این کار کلمهٔ کلیدی inline را در جای نادرستی قرار میدهد که نتیجهٔ آن چنین خواهد بود:
#include <cstdio>
namespace Greeter {
inline namespace Version1 {
const char* greet() { return "Hello from version 1!"; }
} // namespace Version1
} // namespace Greeter
namespace inline Greeter {
namespace Version2 {
const char *greet() { return "Hello from version 2!"; }
} // namespace Version2
} // namespace inline Greeter
int main(int argc, char* argv[]) {
printf("%s\n", Greeter::greet());
return 0;
}
نمیتوان از :program:`clang-change-namespace برای inline کردن یک فضای نام استفاده کرد.
ارجاعات به نمادها بهروزرسانی نمیشوند
این فایل test.cc را در نظر بگیرید:
namespace old {
struct foo {};
} // namespace old
namespace b {
old::foo g_foo;
} // namespace b
دقت کنید که فضای نام b یک متغیر سراسری از نوع old::foo تعریف میکند. اگر ما اکنون نام فضای نام old را به modern تغییر دهیم، این ارجاع بهروزرسانی نخواهد شد:
clang-change-namespace \ --old_namespace "old" \ --new_namespace "modern" \ --file_pattern test.cc \ test.cc
namespace modern {
struct foo {};
} // namespace modern
namespace b {
old::foo g_foo;
} // namespace b
متغیر g_foo همچنان از نوع old::foo است که دیگر وجود ندارد، در حالی که در عوض باید از modern::foo استفاده میکرد.
تنها ارجاعات نمادها در فضای نام منتقلشده بهروزرسانی میشوند، نه موارد خارج از آن.
گزینههای خط فرمان clang-change-namespace
- --allowed_file=<string>
- فایلی شامل عبارات باقاعده (regex) از نام نمادهایی که انتظار نمیرود هنگام تغییر namespace در اطرافشان بهروزرسانی شوند.
- --dump_result
- در صورت تعیین، محتویات فایل جدید را با قالب YAML در خروجی میریزد.
- --extra-arg=<string>
- آرگومان اضافی برای الحاق به انتهای خط فرمان کامپایلر
- --extra-arg-before=<string>
- آرگومان اضافی برای درج در ابتدای خط فرمان کامپایلر
- --file_pattern=<string>
- فقط namespaceها را در فایلهایی تغییر نام میدهد که با الگوی عبارت باقاعده ارائهشده مطابقت داشته باشند.
- -i
- در صورت تعیین، فایلهای <file> را به صورت درجا (inplace) ویرایش میکند.
- --new_namespace=<string>
- فضای نام جدید. زمانی که فضای نام سراسری (global namespace) هدف است از "" استفاده کنید.
- --old_namespace=<string>
- فضای نام قدیمی.
- -p <string>
- مسیر ساخت (build)
- --style=<string>
- نام شیوهنامه (style) مورد استفاده برای قالببندی مجدد.
راهنمای کاربری MODULARIZE
نحوه استفاده از Modularize
modularize [<modularize-options>] [<module-map>|<include-files-list>]* [<front-end-options>...]
عبارت <modularize-options> جایبانی برای گزینههای مخصوص به modularize است که در زیر در بخش گزینههای خط فرمان Modularize شرح داده شدهاند.
عبارت <module-map> مسیر نام یک فایل را برای یک module map موجود مشخص میکند. module map باید از لحاظ نحو (syntax) خوشساخت باشد. Modularize نام فایلهای هدر را از این map استخراج خواهد کرد. تنها هدرهای معمولی بررسی میشوند، با این فرض که هدرهای علامتگذاریشده با عنوان "private"، "textual"، یا "exclude" نباید به عنوان یک include سطحبالا بررسی شوند، چرا که فرض میشود آنها یا توسط هدرهای دیگری که بررسی میشوند فراخوانی شدهاند، یا برای ماژولها مناسب نیستند.
عبارت <include-files-list> مسیر نام فایلی را مشخص میکند که شامل فهرستی از هدرهای جداشده با خط جدید است تا نسبت به یکدیگر بررسی شوند. خطوطی که با '#' شروع میشوند و خطوط خالی نادیده گرفته میشوند. نامهای فایل هدر که پس از آنها دو نقطه و نامهای فایل دیگرِ جداشده با فاصله میآید، آن فایلهای اضافه را به عنوان وابستگی در بر میگیرند. نام فایلها میتوانند مسیرهای نسبی یا کامل باشند، اما باید در همان سطر قرار گیرند. برای مثال:
header1.h header2.h header3.h: header1.h header2.h
توجه داشته باشید که مگر در صورت تعیین گزینه -prefix (header path)، مسیرهای فایل غیرمطلق در فایل فهرست هدرها، نسبت به دایرکتوری فایل فهرست هدر در نظر گرفته میشوند. برای مشخص کردن دایرکتوری دیگر از -prefix استفاده کنید.
عبارت <front-end-options> جایبانی برای آرگومانهای معمولی فرانتاند Clang است که باید بعد از <include-files-list> بیایند. توجه داشته باشید که به طور پیشفرض، modularize فرض میکند فایلهای .h حاوی کد منبع C++ هستند، بنابراین اگر از زبان دیگری استفاده میکنید، ممکن است لازم باشد از گزینه -x استفاده کنید تا به Clang بگویید که هدر حاوی زبان دیگری است، مانند: -x c
همچنین توجه داشته باشید که چون modularize از درایور clang استفاده نمیکند، به احتمال زیاد نیاز خواهید داشت که آرگومانهای فرانتاند کامپایلر اضافی ارسال کنید تا با مواردی که درایور به صورت پیشفرض ارسال میکند، مطابقت داشته باشد.
گزینههای خط فرمان Modularize
- -prefix=<header-path>
- مسیر دادهشده را به ابتدای مسیرهای فایل غیرمطلق در فایل فهرست هدر اضافه میکند. به طور پیشفرض، فرض میشود که هدرها نسبت به دایرکتوریِ فایل فهرست هدرها هستند. از -prefix برای مشخص کردن یک دایرکتوری متفاوت استفاده کنید.
- -module-map-path=<module-map-path>
- یک module map تولید کرده و آن را در فایل دادهشده خروجی میدهد. توضیحات را در Module Map Generation <#module-map-generation> مشاهده کنید.
- -problem-files-list=<problem-files-list-file-name>
- تنها برای استفاده با دستیار module map است. فهرست ورودی فایلهایی که با ماژولها مشکل دارند. این فایلها همچنان در module map تولیدشده گنجانده میشوند، اما به عنوان هدرهای "excluded" علامتگذاری خواهند شد.
- -root-module=<root-name>
- ماژولهای تولیدشده توسط گزینه -module-map-path را درون یک ماژول دربرگیرنده با نام دادهشده قرار میدهد. توضیحات را در Module Map Generation <#module-map-generation> مشاهده کنید.
- -block-check-header-list-only
- بررسیِ #include-inside-extern-or-namespace-block را تنها به آن دسته از هدرهایی محدود میکند که صراحتاً در فهرست هدرها لیست شدهاند. این یک راهکار موقت برای جلوگیری از پیامهای خطا برای includeهای خصوصی است که عمداً در داخل بلوکها گنجانده میشوند.
- -no-coverage-check
- بررسی پوشش (coverage check) را برای یک module map انجام نمیدهد.
- -coverage-check-only
- تنها بررسی پوشش (coverage check) را برای یک module map انجام میدهد.
- -display-file-lists
- فهرست فایلهای سالم (بدون خطای کامپایل)، فایلهای مشکلدار، و یک فهرست ترکیبی را که فایلهای مشکلدار با یک '#' در ابتدای آنها مشخص شدهاند، نمایش میدهد. از این گزینه میتوان برای تعیین سریع فایلهای دارای مشکل استفاده کرد. فهرست ترکیبی اخیر میتواند برای شروع ماژولار کردن مجموعهای از هدرها مفید باشد. میتوانید با فهرست کاملی از هدرها شروع کنید، از گزینه -display-file-lists استفاده کنید و سپس از فهرست ترکیبی به عنوان فهرست میانی خود بهره ببرید و هدرها را به محض رفع اشکال، از حالت کامنت خارج کنید.
ابزار modularize یک ابزار مستقل است که بررسی میکند آیا مجموعهای از هدرها تعاریف سازگارِ موردنیاز برای استفاده از ماژولها را ارائه میدهند یا خیر. به عنوان مثال، این ابزار تشخیص میدهد که آیا یک موجودیت یکسان (مانند یک ماکروی NULL یا typedef مربوط به size_t) در چندین هدر تعریف شده است یا اینکه آیا یک هدر تحت شرایط مختلف تعاریف متفاوتی تولید میکند یا خیر. این شرایط باعث میشوند ماژولهای ساختهشده از هدرها عملکرد ضعیفی داشته باشند و باید قبل از معرفی module map برطرف شوند.
ابزار modularize همچنین دارای یک گزینه در حالت دستیار (assistant mode) برای تولید فایل module map بر اساس فهرست هدرهای ارائهشده است. فایل تولیدشده یک module map کاربردی است که میتواند به عنوان یک نقطه شروع برای یک فایل module.modulemap استفاده شود.
شروع به کار
برای ساخت از سورسکد:
- 1.
- برای کسب اطلاعات درباره نحوه دریافت سورسکدهای LLVM، Clang و Clang Extra Tools، مستندات Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html و Clang Tools Documentation https://clang.llvm.org/docs/ClangTools.html را مطالعه کنید.
- 2.
- مستندات Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html و Building LLVM with CMake https://llvm.org/docs/CMake.html دستورالعملهای نحوه ساخت را ارائه میدهند. در صورتی که تمام سورسکدها در مسیر صحیح دریافت (check out) شده باشند، فرآیند ساخت LLVM ابزارهای Clang Extra Tools و وابستگیهای آنها را به طور خودکار خواهد ساخت.
- •
- در صورت استفاده از CMake، میتوانید از تارگت modularize نیز برای ساخت اختصاصی ابزار modularize و وابستگیهای آن استفاده کنید.
پیش از ادامه، نگاهی به Modularize Usage <> بیندازید تا با نحوه اجرای modularize آشنا شوید.
مواردی که Modularize بررسی میکند
ابزار Modularize موارد زیر را بررسی خواهد کرد:
- تعریفهای تکراری نوعها و متغیرهای سراسری (global)
- تعریفهای تکراری ماکروها
- نمونههای ماکرو، عبارتهای 'defined(macro)'، یا شروط #if، #elif، #ifdef و #ifndef که در یک هدر مقادیر متفاوتی ارزیابی میشوند
- دستورهای #include درون بلوکهای 'extern "C/C++" {}' یا 'namespace (name) {}'
- کامل بودن پوشش هدرهای module map (تنها در صورت ارائه ورودی module map)
ابزار Modularize فرآیند پارس معمول C/C++ را انجام داده و خطاها و هشدارهای استاندارد را گزارش میدهد، اما پیامهای خطای ویژهای مانند نمونههای زیر را نیز گزارش خواهد کرد:
error: '(symbol)' defined at multiple locations: (file):(row):(column) (file):(row):(column) error: header '(file)' has different contents depending on how it was included
مورد دوم ممکن است با پیامهایی نظیر زیر همراه باشد:
note: '(symbol)' in (file) at (row):(column) not always provided
همچنین بررسیهایی برای بسط ماکروها، عبارتهای defined(macro)، و دستورهای شرطی پیشپردازنده که به طور ناسازگار ارزیابی میشوند صورت میگیرد و میتواند پیامهای خطایی مانند زیر تولید کند:
(...)/SubHeader.h:11:5:
#if SYMBOL == 1
^
error: Macro instance 'SYMBOL' has different values in this header,
depending on how it was included.
'SYMBOL' expanded to: '1' with respect to these inclusion paths:
(...)/Header1.h
(...)/SubHeader.h
(...)/SubHeader.h:3:9:
#define SYMBOL 1
^
Macro defined here.
'SYMBOL' expanded to: '2' with respect to these inclusion paths:
(...)/Header2.h
(...)/SubHeader.h
(...)/SubHeader.h:7:9:
#define SYMBOL 2
^
Macro defined here.
بررسیهایی نیز برای دستورهای '#include' که درون بلوکهای 'extern "C/C++" {}' یا 'namespace (name) {}' تو در تو شدهاند انجام میشود و میتواند پیام خطایی مانند نمونه زیر ایجاد کند:
IncludeInExtern.h:2:3:
#include "Empty.h"
^
error: Include directive within extern "C" {}.
IncludeInExtern.h:1:1:
extern "C" {
^
The "extern "C" {}" block is here.
بررسی پوشش Module Map
بررسی پوشش (coverage check) از کتابخانه Clang برای خواندن و پارس فایل module map استفاده میکند. این ابزار از دایرکتوری فایل module map یا صرفاً مسیرهای include (در صورت مشخص شدن) شروع کرده و نام تمام فایلهایی را که به عنوان هدر در نظر میگیرد جمعآوری میکند (فایلهای بدون پسوند، .h یا .inc--اگر به موارد بیشتری نیاز دارید، تابع isHeader را اصلاح کنید). سپس این هدرها را با مواردی که در module map به آنها ارجاع داده شده است مقایسه میکند؛ چه به طور صریح نام برده شده باشند و چه به طور ضمنی از طریق یک دایرکتوری umbrella یا فایل umbrella، همانطور که توسط شیء ModuleMap پارس شده است. اگر هدرهایی یافت شوند که در module map مورد ارجاع قرار نگرفته یا توسط دایرکتوری یا فایل umbrella پوشش داده نشده باشند، پیامهای هشدار تولید شده و برنامه با کد خطای 1 خارج میشود. در صورتی که مشکلی یافت نشود، کد خروجی 0 بازگردانده میشود.
توجه داشته باشید که در مورد هدرهای umbrella، این ابزار کامپایلر را برای پیشپردازش فایل فراخوانی میکند و از یک callback برای جمعآوری فایلهای هدر include شده توسط هدر umbrella یا هر یک از includeهای تو در توی آن استفاده میکند. در صورتی که برای این فراخوانیهای کامپایلر به گزینههای front end نیاز باشد، میتوان آنها را در خط فرمان پس از آرگومان فایل module map مشخص کرد.
پیامهای هشدار ساختاری به این صورت دارند:
دقت داشته باشید در صورتی که module map به فایلی ارجاع دهد که وجود خارجی ندارد، پارسر module map در Clang (در زمان نگارش این متن) یک پیام خطا نمایش خواهد داد.
برای محدود کردن بررسیهای modularize صرفاً به بررسی پوشش module map، از گزینه -coverage-check-only استفاده کنید.
به عنوان مثال:
modularize -coverage-check-only module.modulemap
اگر -module-map-path=<module map file> را مشخص کنید، modularize بر اساس فهرست هدرهای ورودی، یک نقشه ماژول (module map) ایجاد میکند. برای هر هدر یک ماژول ساخته خواهد شد. همچنین، اگر هدر موجود در فهرست هدرها یک مسیر نسبی باشد، یک سلسلهمراتب ماژول تودرتو ایجاد میشود که در آن برای هر مؤلفه زیرپوشه در مسیر هدر یک ماژول تعریف شده و خود هدر با داخلیترین ماژول نشان داده میشود. اگر هدرهای دیگر نیز از همان زیرپوشهها استفاده کنند، درون همین ماژولها قرار خواهند گرفت.
به عنوان مثال، برای فهرست هدرهای زیر:
SomeTypes.h SomeDecls.h SubModule1/Header1.h SubModule1/Header2.h SubModule2/Header3.h SubModule2/Header4.h SubModule2.h
نقشه ماژول زیر تولید خواهد شد:
// Output/NoProblemsAssistant.txt
// Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
-root-module=Root NoProblemsAssistant.modularize
module SomeTypes {
header "SomeTypes.h"
export *
}
module SomeDecls {
header "SomeDecls.h"
export *
}
module SubModule1 {
module Header1 {
header "SubModule1/Header1.h"
export *
}
module Header2 {
header "SubModule1/Header2.h"
export *
}
}
module SubModule2 {
module Header3 {
header "SubModule2/Header3.h"
export *
}
module Header4 {
header "SubModule2/Header4.h"
export *
}
header "SubModule2.h"
export *
}
گزینه اختیاری -root-module=<root-name> میتواند برای ایجاد یک ماژول ریشه بهکار رود که تمام ماژولهای دیگر را در بر میگیرد.
گزینه اختیاری -problem-files-list=<problem-file-name> میتواند برای وارد کردن فهرستی از فایلهایی که باید نادیده گرفته شوند استفاده شود، شاید به عنوان یک راهکار موقت تا زمانی که هدرهای مشکلدار اصلاح شوند.
به عنوان مثال، با همان فهرست هدرهای بالا:
// Output/NoProblemsAssistant.txt
// Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
-root-module=Root NoProblemsAssistant.modularize
module Root {
module SomeTypes {
header "SomeTypes.h"
export *
}
module SomeDecls {
header "SomeDecls.h"
export *
}
module SubModule1 {
module Header1 {
header "SubModule1/Header1.h"
export *
}
module Header2 {
header "SubModule1/Header2.h"
export *
}
}
module SubModule2 {
module Header3 {
header "SubModule2/Header3.h"
export *
}
module Header4 {
header "SubModule2/Header4.h"
export *
}
header "SubModule2.h"
export *
}
}
توجه داشته باشید که هدرهای دارای وابستگی با یک هشدار نادیده گرفته میشوند، زیرا سازوکار ماژول Clang از هدرهایی که وابسته به include شدن قبلی هدرهای دیگر هستند پشتیبانی نمیکند.
قالب نقشه ماژول کلمات کلیدی خاصی را تعریف میکند که نمیتوانند در نام ماژولها استفاده شوند. اگر نام یک هدر یکی از این کلمات باشد، یک زیرخط ('_') به ابتدای نام اضافه خواهد شد. برای مثال، اگر نام هدر header.h باشد، از آنجا که header یک کلمه کلیدی است، نام ماژول _header خواهد بود. برای مشاهده فهرست کلمات کلیدی نقشه ماژول، لطفاً این پیوند را بررسی کنید: ساختار واژگانی https://clang.llvm.org/docs/Modules.html#lexical-structure
راهنمای کاربری PP-TRACE
ابزار pp-trace یک ابزار مستقل است که فعالیتهای پیشپردازنده را ردگیری میکند. همچنین به عنوان آزمونی برای رابط PPCallbacks در Clang استفاده میشود. این ابزار فایل منبع ارائهشده را از طریق پیشپردازنده Clang پردازش کرده و اطلاعات منتخب را از توابع بازخوانی (callback functions) بازنویسیشده در یک مشتق PPCallbacks https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html نمایش میدهد. خروجی در قالب سطح بالای YAML است که در pp-trace Output Format شرح داده شده است.
نحوه استفاده از pp-trace
قالب خط فرمان
pp-trace [<pp-trace-options>] <source-file> [-- <front-end-options>]
عبارت <pp-trace-options> جایبانی برای گزینههای مخصوص pp-trace است که در ادامه در بخش Command Line Options شرح داده شدهاند.
عبارت <source-file> فایل منبعی را مشخص میکند که باید توسط پیشپردازنده پردازش شود.
عبارت <front-end-options> جایبانی برای گزینههای معمولی Clang Compiler Options https://clang.llvm.org/docs/UsersManual.html#command-line-options است که باید پس از <source-file> قرار گیرند.
گزینههای خط فرمان
- -callbacks <comma-separated-globs>
- این گزینه فهرستی از الگوهای انطباق (globs) جداشده با کاما را مشخص میکند که فهرست کالبکهای مورد نیاز برای ردگیری را تعیین مینمایند. الگوها به ترتیب حضورشان پردازش میشوند. الگوهای مثبت کالبکهای منطبق را به مجموعه اضافه کرده و الگوهای منفی (آنهایی که پیشوند '-' دارند) کالبکها را از مجموعه حذف میکنند.
- FileChanged
- FileSkipped
- InclusionDirective
- moduleImport
- EndOfMainFile
- Ident
- PragmaDirective
- PragmaComment
- PragmaDetectMismatch
- PragmaDebug
- PragmaMessage
- PragmaDiagnosticPush
- PragmaDiagnosticPop
- PragmaDiagnostic
- PragmaOpenCLExtension
- PragmaWarning
- PragmaWarningPush
- PragmaWarningPop
- MacroExpands
- MacroDefined
- MacroUndefined
- Defined
- SourceRangeSkipped
- If
- Elif
- Ifdef
- Ifndef
- Else
- Endif
- -output <output-file>
- بهطور پیشفرض، pp-trace اطلاعات ردگیری را در خروجی استاندارد (stdout) مینویسد. از این گزینه برای ذخیره اطلاعات ردگیری در یک فایل استفاده کنید.
قالب خروجی pp-trace
خروجی pp-trace به قالب YAML است. برای اطلاعات کلی در مورد YAML به https://yaml.org مراجعه کنید. این خروجی بهصورت توالیای از اطلاعات مربوط به فراخوانی کالبک، شامل نام کالبک و اطلاعات آرگومانها مرتب شده است؛ برای مثال::
--- - Callback: Name Argument1: Value1 Argument2: Value2 (etc.) ...
با دادههای واقعی::
--- - Callback: FileChanged Loc: "c:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:1:1" Reason: EnterFile FileType: C_User PrevFID: (invalid) (etc.) - Callback: FileChanged Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:5:1" Reason: ExitFile FileType: C_User PrevFID: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/Input/Level1B.h" - Callback: EndOfMainFile ...
در همه موارد به جز یک مورد (MacroDirective)، مقادیر اسکالر "Argument" نامی یکسان با آرگومان موجود در تابع کالبک متناظر در PPCallbacks دارند.
جزئیات کالبکها
بخشهای زیر هدف و قالب خروجی هر کالبک را شرح میدهند.
برای مشاهده مستندات Doxygen مربوط به هر کالبک، روی نام آن در عنوان بخش کلیک کنید.
جدول توضیحات آرگومانها، اطلاعات نمایشدادهشده برای آرگومان کالبک را شرح میدهد.
فیلد Argument Name در بیشتر موارد (اما نه همه) نامی یکسان با پارامتر تابع کالبک دارد.
فیلد Argument Value Syntax مقادیری را که برای مقدار آرگومان نمایش داده خواهند شد شرح میدهد. این بخش از یک نمایش موردی (ad hoc) استفاده میکند که ترکیبی از نمایشهای تحتاللفظی و نمادین است. اعضای برشماری (enum) بهصورت عضو واقعی برشماری در قالب (member1|member2|...) نمایش داده میشوند. یک نام درون پرانتز میتواند نشاندهنده یک جانگهدار برای مقدار توصیفشده باشد، یا برای ابهامزدایی، ممکن است یک مقدار لفظی مانند (null) برای یک اشارهگر null باشد. مکانها فقط برای جلوگیری از سردرگمی مولد مستندات داخل کوتیشن قرار گرفتهاند.
فیلد Clang C++ Type همان نوع داده برگرفته از اعلان تابع کالبک است.
ستون Description آرگومان یا آنچه را که برای آن نمایش داده میشود توصیف میکند.
توجه داشته باشید که در برخی موارد، مانند زمانی که مقدار یک آرگومان اشارهگری به یک ساختار (struct) است، بهجای نمایش تمام اعضای ساختار، تنها برخی از اعضای کلیدی برای نمایش آن مقدار آورده میشوند.
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a7cc8cfaf34114fc65e92af621cd6464e'>کالبک FileChanged https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a7cc8cfaf34114fc65e92af621cd6464e
کالبک FileChanged زمانی فراخوانی میشود که پیشپردازنده وارد یک فایل شده یا از آن خارج گردد؛ چه فایل سطح بالا که در حال کامپایل است، و چه هرگونه دستور #include. این کالبک همچنین در نتیجه یک عملگر عملگر پراگما مربوط به هدرهای سیستمی (system header pragma) یا در تغییر نام داخلی فایل فراخوانی میشود.
توضیحات آرگومانها:
| Argument Name | Argument Value Syntax | Clang C++ Type | Description |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت دایرکتیو. |
| Reason | (EnterFile|ExitFile|SystemHeaderPragma|RenameFile) | PPCallbacks::FileChangeReason | دلیل تغییر. |
| FileType | (C_User|C_System|C_ExternCSystem) | SrcMgr::CharacteristicKind | نوع Include. |
| PrevFID | ((file)|(invalid)) | FileID | فایل قبلی، در صورت وجود. |
مثال::
- Callback: FileChanged Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:1:1" Reason: EnterFile FileType: C_User PrevFID: (invalid)
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab5b338a0670188eb05fa7685bbfb5128'>فراخوانی FileSkipped https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab5b338a0670188eb05fa7685bbfb5128
هنگامی که یک فایل مبدأ به دلیل بهینهسازی گارد هدر (header guard) نادیده گرفته شود، FileSkipped فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| ParentFile | ("(file)" or (null)) | const FileEntry | فایلی که فایل نادیدهگرفتهشده را #include کرده است. |
| FilenameTok | (token) | توکن موجود در ParentFile که نشاندهنده فایل نادیدهگرفتهشده است. | |
| FileType | (C_User|C_System|C_ExternCSystem) | SrcMgr::CharacteristicKind | نوع فایل. |
مثال::
- Callback: FileSkipped ParentFile: "/path/filename.h" FilenameTok: "filename.h" FileType: C_User
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a557d9738c329793513a6f57d6b60de52'>فراخوانی InclusionDirective https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a557d9738c329793513a6f57d6b60de52
هنگامی که یک دایرکتیو شمول از هر نوعی (#include</code>، #import</code> و غیره) پردازش شود، فارغ از اینکه این شمول در عمل منجر به وارد کردن فایل شود یا خیر، InclusionDirective فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| HashLoc | "(file):(line):(col)" | SourceLocation | موقعیت کاراکتر '#' که دایرکتیو شمول را آغاز میکند. |
| IncludeTok | (token) | توکنی که نوع دایرکتیو شمول را مشخص میکند، مانند 'include' یا 'import'. | |
| FileName | "(file)" | StringRef | نام فایلی که قرار است گنجانده شود، همانطور که در کد مبدأ نوشته شده است. |
| IsAngled | (true|false) | bool | مشخص میکند که نام فایل درون براکتهای زاویهدار قرار گرفته است یا نه؛ در غیر این صورت، درون کوتیشن قرار داشته است. |
| FilenameRange | "(file)" | CharSourceRange | محدوده نویسههای کوتیشن یا براکتهای زاویهدار برای نام فایلِ نوشتهشده. |
| File | "(file)" | const FileEntry | فایل واقعی که ممکن است توسط این دایرکتیو شمول گنجانده شود. |
| SearchPath | "(path)" | StringRef | شامل مسیر جستجویی است که برای یافتن فایل در سیستم فایل استفاده شده است. |
| RelativePath | "(path)" | StringRef | مسیرِ نسبی به SearchPath که فایلِ مورد شمول در آن پیدا شده است. |
| Imported | ((module name)|(null)) | const Module | ماژول مربوطه، در صورتی که دایرکتیو شمول بهطور خودکار به یک واردات ماژول (module import) تبدیل شده باشد، وگرنه null است. |
مثال::
- Callback: InclusionDirective HashLoc: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-include.cpp:4:1" IncludeTok: include FileName: "Input/Level1B.h" IsAngled: false FilenameRange: "Input/Level1B.h" File: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/Input/Level1B.h" SearchPath: "D:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace" RelativePath: "Input/Level1B.h" Imported: (null)
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#af32dcf1b8b7c179c7fcd3e24e89830fe'>بازخوانی moduleImport https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#af32dcf1b8b7c179c7fcd3e24e89830fe
بازخوانی moduleImport هنگامی فراخوانی میشود که یک نحو صریح وارد کردن ماژول (module-import) وجود داشته باشد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| ImportLoc | "(file):(line):(col)" | SourceLocation | مکان توکن دستور import. |
| Path | "(path)" | ModuleIdPath | شناسهها (و مکانهای آنها) مربوط به "path" ماژول. |
| Imported | ((module name)|(null)) | const Module | ماژول وارد شده؛ در صورت شکست فرآیند وارد کردن میتواند null باشد. |
مثال::
- Callback: moduleImport
ImportLoc: "d:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:2"
Path: [{Name: Level1B, Loc: "d:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:9"}, {Name: Level2B, Loc: "d:/Clang/llvmnewmod/clang-tools-extra/test/pp-trace/pp-trace-modules.cpp:4:17"}]
Imported: Level2B
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a63e170d069e99bc1c9c7ea0f3bed8bcc'>بازخوانی EndOfMainFile https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a63e170d069e99bc1c9c7ea0f3bed8bcc
بازخوانی EndOfMainFile هنگامی که به پایان فایل اصلی برسد فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| (بدون آرگومان) |
مثال::
- Callback: EndOfMainFile
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3683f1d1fa513e9b6193d446a5cc2b66'>بازخوانی Ident https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3683f1d1fa513e9b6193d446a5cc2b66
بازخوانی Ident هنگامی که یک دستور #ident یا #sccs خوانده شود، فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | مکان دستور. |
| str | (name) | const std::string | متن دستور. |
مثال::
- Callback: Ident Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-ident.cpp:3:1" str: "$Id$"
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0a2d7a72c62184b3cbde31fb62c6f2f7'>بازخوانی PragmaDirective https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0a2d7a72c62184b3cbde31fb62c6f2f7
بازخوانی PragmaDirective با شروع خواندن هر دستور pragma فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | مکان دستور. |
| Introducer | (PIK_HashPragma|PIK__Pragma|PIK___pragma) | PragmaIntroducerKind | نوع دستور pragma. |
مثال::
- Callback: PragmaDirective Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Introducer: PIK_HashPragma
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ace0d940fc2c12ab76441466aab58dc37'>فراخوان PragmaComment https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ace0d940fc2c12ab76441466aab58dc37
هنگامی که یک دستور مستقیم #pragma comment خوانده میشود، PragmaComment فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت مکانی دستورالعمل. |
| Kind | ((name)|(null)) | const IdentifierInfo | نماد نوع کامنت (دیدگاه). |
| Str | (message directive) | const std::string | دستورالعمل پیام کامنت. |
مثال::
- Callback: PragmaComment Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Kind: library Str: kernel32.lib
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab11158c9149fb8ad8af1903f4a6cd65d'>فراخوان PragmaDetectMismatch https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab11158c9149fb8ad8af1903f4a6cd65d
هنگامی که یک دستور مستقیم #pragma detect_mismatch خوانده میشود، PragmaDetectMismatch فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت مکانی دستورالعمل. |
| Name | "(name)" | const std::string | نام. |
| Value | (string) | const std::string | مقدار. |
مثال::
- Callback: PragmaDetectMismatch Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Name: name Value: value
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a57cdccb6dcc07e926513ac3d5b121466'>فراخوان PragmaDebug https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a57cdccb6dcc07e926513ac3d5b121466
هنگامی که یک دستور مستقیم #pragma clang __debug خوانده میشود، PragmaDebug فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت مکانی دستورالعمل. |
| DebugType | (string) | StringRef | نوع پیام اشکالزدایی (دیباگ) را مشخص میکند. |
مثال::
- Callback: PragmaDebug Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" DebugType: warning
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abb42935d9a9fd8e2c4f51cfdc4ea2ae1'>فراخوان PragmaMessage https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abb42935d9a9fd8e2c4f51cfdc4ea2ae1
هنگامی که یک دستور #pragma message خوانده میشود، PragmaMessage فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | ساختار مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستور. |
| Namespace | (name) | StringRef | فضاینام (Namespace) دستور پیام. |
| Kind | (PMK_Message|PMK_Warning|PMK_Error) | PPCallbacks::PragmaMessageKind | نوع دستور پیام. |
| Str | (string) | StringRef | متن دستور پیام. |
مثال::
- Callback: PragmaMessage Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC" Kind: PMK_Message Str: The message text.
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0f3ff19762baa38fe6c5c58022d32979'>فراخوان PragmaDiagnosticPush https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0f3ff19762baa38fe6c5c58022d32979
هنگامی که یک دستور #pragma gcc diagnostic push خوانده میشود، PragmaDiagnosticPush فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | ساختار مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستور. |
| Namespace | (name) | StringRef | نام فضاینام (Namespace). |
مثال::
- Callback: PragmaDiagnosticPush Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC"
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac94d789873122221fba8d76f6c5ea45e'>فراخوان PragmaDiagnosticPop https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac94d789873122221fba8d76f6c5ea45e
هنگامی که یک دستور #pragma gcc diagnostic pop خوانده میشود، PragmaDiagnosticPop فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | ساختار مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستور. |
| Namespace | (name) | StringRef | نام فضاینام (Namespace). |
مثال::
- Callback: PragmaDiagnosticPop Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC"
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afe7938f38a83cb7b4b25a13edfdd7bdd'>فراخوانی بازگشتی PragmaDiagnostic https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afe7938f38a83cb7b4b25a13edfdd7bdd
هنگامی که یک دستور مستقیم #pragma gcc diagnostic خوانده میشود، PragmaDiagnostic فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | سینتکس مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستور مستقیم. |
| Namespace | (name) | StringRef | نام فضای نام (Namespace). |
| mapping | (0|MAP_IGNORE|MAP_WARNING|MAP_ERROR|MAP_FATAL) | diag::Severity | نوع نگاشت (Mapping). |
| Str | (string) | StringRef | نام هشدار/خطا. |
مثال:
- Callback: PragmaDiagnostic Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Namespace: "GCC" mapping: MAP_WARNING Str: WarningName
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a92a20a21fadbab4e2c788f4e27fe07e7'>فراخوانی بازگشتی PragmaOpenCLExtension https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a92a20a21fadbab4e2c788f4e27fe07e7
هنگامی که افزونه OpenCL توسط یک pragma فعال یا غیرفعال میشود، PragmaOpenCLExtension فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | سینتکس مقدار آرگومان | نوع C++ در Clang | توضیحات |
| NameLoc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان نام. |
| Name | (name) | const IdentifierInfo | نماد نام (Symbol). |
| StateLoc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان وضعیت. |
| State | (1|0) | unsigned | وضعیت فعال/غیرفعال. |
مثال:
- Callback: PragmaOpenCLExtension NameLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:10" Name: Name StateLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:18" State: 1
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#aa17169d25fa1cf0a6992fc944d1d8730'>فراخوانی بازگشتی PragmaWarning https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#aa17169d25fa1cf0a6992fc944d1d8730
هنگامی که یک دستور مستقیم #pragma warning خوانده میشود، PragmaWarning فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | سینتکس مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستور مستقیم. |
| WarningSpec | (string) | StringRef | مشخصکننده هشدار (Warning specifier). |
| Ids | [(number)[, ...]] | ArrayRef<int> | شمارههای هشدار. |
مثال:
- Callback: PragmaWarning Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" WarningSpec: disable Ids: 1,2,3
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ae5626ef70502687a859f323a809ed0b6'>فراخوان PragmaWarningPush https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ae5626ef70502687a859f323a809ed0b6
هنگامی که یک دستورالعمل #pragma warning(push) خوانده میشود، PragmaWarningPush فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستورالعمل. |
| Level | (number) | int | سطح هشدار. |
مثال::
- Callback: PragmaWarningPush Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1" Level: 1
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac98d502af8811b8a6e7342d7cd2b3b95'>فراخوان PragmaWarningPop https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac98d502af8811b8a6e7342d7cd2b3b95
هنگامی که یک دستورالعمل #pragma warning(pop) خوانده میشود، PragmaWarningPop فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت و مکان دستورالعمل. |
مثال::
- Callback: PragmaWarningPop Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a9bc725209d3a071ea649144ab996d515'>فراخوان MacroExpands https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a9bc725209d3a071ea649144ab996d515
فراخوان MacroExpands در زمان اجرای ::HandleMacroExpandedIdentifier هنگامی که فراخوانی یک ماکرو پیدا میشود، فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| MacroNameTok | (token) | const Token | توکن نام ماکرو. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | نوع دستورالعمل ماکرو از ساختار MacroDirective. |
| Range | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | محدوده منبع (source range) برای بسط ماکرو. |
| Args | [(name)|(number)|<(token name)>[, ...]] | const MacroArgs | توکنهای آرگومان. نامها و اعداد بهصورت لفظی (literal) هستند، بقیه موارد به فرم '<' tokenName '>' میباشند. |
مثال::
- Callback: MacroExpands MacroNameTok: X_IMPL MacroDirective: MD_Define Range: [(nonfile), (nonfile)] Args: [a <plus> y, b]
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a8448fc9f96f22ad1b93ff393cffc5a76'>فراخوانی بازگشتی (Callback) MacroDefined https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a8448fc9f96f22ad1b93ff393cffc5a76
هنگامی که یک تعریف ماکرو دیده میشود، MacroDefined فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | سینتکس مقدار آرگومان | نوع C++ در Clang | توضیحات |
| MacroNameTok | (token) | const Token | توکن نام ماکرو. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | نوع دایرکتیو ماکرو از ساختار MacroDirective. |
مثال::
- Callback: MacroDefined MacroNameTok: X_IMPL MacroDirective: MD_Define
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#acb80fc6171a839db8e290945bf2c9d7a'>فراخوانی بازگشتی (Callback) MacroUndefined https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#acb80fc6171a839db8e290945bf2c9d7a
هنگامی که دستور ماکروی #undef دیده میشود، MacroUndefined فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | سینتکس مقدار آرگومان | نوع C++ در Clang | توضیحات |
| MacroNameTok | (token) | const Token | توکن نام ماکرو. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | نوع دایرکتیو ماکرو از ساختار MacroDirective. |
مثال::
- Callback: MacroUndefined MacroNameTok: X_IMPL MacroDirective: MD_Define
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3cc2a644533d0e4088a13d2baf90db94'>فراخوانی بازگشتی (Callback) Defined https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3cc2a644533d0e4088a13d2baf90db94
هنگامی که عملگر 'defined' دیده میشود، Defined فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | سینتکس مقدار آرگومان | نوع C++ در Clang | توضیحات |
| MacroNameTok | (token) | const Token | توکن نام ماکرو. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | نوع دایرکتیو ماکرو از ساختار MacroDirective. |
| Range | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | محدوده مبدأ (Source Range) برای دایرکتیو. |
مثال::
- Callback: Defined MacroNameTok: MACRO MacroDirective: (null) Range: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:5", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:19"]
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abdb4ebe11610f079ac33515965794b46_SourceRangeSkipped'>فراخوانی بازگشتی (Callback) https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abdb4ebe11610f079ac33515965794b46 SourceRangeSkipped
هنگامی که یک محدوده سورس کد نادیده گرفته میشود، SourceRangeSkipped فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Range | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | محدوده سورس کدی که نادیده گرفته شده است. |
مثال::
- Callback: SourceRangeSkipped Range: [":/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2", ":/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:9:2"]
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a645edcb0d6becbc6f256f02fd1287778_If'>فراخوانی بازگشتی (Callback) https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a645edcb0d6becbc6f256f02fd1287778 If
هنگامی که یک #if دیده میشود، If فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت این دستورالعمل (directive). |
| ConditionRange | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | محدوده سورس کد برای شرط. |
| ConditionValue | (true|false) | bool | مقدار شرط. |
مثال::
- Callback: If Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2" ConditionRange: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:4", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:9:1"] ConditionValue: false
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a180c9e106a28d60a6112e16b1bb8302a_Elif'>فراخوانی بازگشتی (Callback) https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a180c9e106a28d60a6112e16b1bb8302a Elif
هنگامی که یک #elif دیده میشود، Elif فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت این دستورالعمل (directive). |
| ConditionRange | ["(file):(line):(col)", "(file):(line):(col)"] | SourceRange | محدوده سورس کد برای شرط. |
| ConditionValue | (true|false) | bool | مقدار شرط. |
| IfLoc | "(file):(line):(col)" | SourceLocation | موقعیت این دستورالعمل (directive). |
مثال::
- Callback: Elif Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2" ConditionRange: ["D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:4", "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:11:1"] ConditionValue: false IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0ce79575dda307784fd51a6dd4eec33d'>فراخوانی بازگشتی (Callback) Ifdef https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0ce79575dda307784fd51a6dd4eec33d
هنگامی که یک #ifdef دیده شود، Ifdef فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | ساختار مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت پیشپردازنده (directive). |
| MacroNameTok | (token) | const Token | توکن نام ماکرو. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | نوع پیشپردازندهٔ ماکرو از ساختار MacroDirective. |
مثال::
- Callback: Ifdef Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-conditional.cpp:3:1" MacroNameTok: MACRO MacroDirective: MD_Define
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a767af69f1cdcc4cd880fa2ebf77ad3ad'>فراخوانی بازگشتی (Callback) Ifndef https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a767af69f1cdcc4cd880fa2ebf77ad3ad
هنگامی که یک #ifndef دیده شود، Ifndef فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | ساختار مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت پیشپردازنده (directive). |
| MacroNameTok | (token) | const Token | توکن نام ماکرو. |
| MacroDirective | (MD_Define|MD_Undefine|MD_Visibility) | const MacroDirective | نوع پیشپردازندهٔ ماکرو از ساختار MacroDirective. |
مثال::
- Callback: Ifndef Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-conditional.cpp:3:1" MacroNameTok: MACRO MacroDirective: MD_Define
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ad57f91b6d9c3cbcca326a2bfb49e0314'>فراخوانی بازگشتی (Callback) Else https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ad57f91b6d9c3cbcca326a2bfb49e0314
هنگامی که یک #else دیده شود، Else فراخوانی میشود.
توضیحات آرگومانها:
| نام آرگومان | ساختار مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت پیشپردازندهٔ else. |
| IfLoc | "(file):(line):(col)" | SourceLocation | موقعیت پیشپردازندهٔ if مربوطه. |
مثال::
- Callback: Else Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2" IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"
https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afc62ca1401125f516d58b1629a2093ce_Endif'>فراخوان https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afc62ca1401125f516d58b1629a2093ce Endif
هنگامی که یک #endif دیده میشود، Endif فراخوانی میگردد.
توضیحات آرگومانها:
| نام آرگومان | نحو مقدار آرگومان | نوع C++ در Clang | توضیحات |
| Loc | "(file):(line):(col)" | SourceLocation | موقعیت دایرکتیو endif. |
| IfLoc | "(file):(line):(col)" | SourceLocation | موقعیت دایرکتیو if مربوطه. |
مثال::
- Callback: Endif Loc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:10:2" IfLoc: "D:/Clang/llvm/clang-tools-extra/test/pp-trace/pp-trace-macro.cpp:8:2"
ساخت pp-trace
برای ساخت از روی کد منبع:
- 1.
- برای دریافت اطلاعات درباره نحوه دریافت کدهای منبع LLVM، Clang و Clang Extra Tools، بخش Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html و مستندات Clang Tools Documentation https://clang.llvm.org/docs/ClangTools.html را مطالعه کنید.
- 2.
- بخشهای Getting Started with the LLVM System https://llvm.org/docs/GettingStarted.html و Building LLVM with CMake https://llvm.org/docs/CMake.html دستورالعملهای مربوط به نحوه ساخت را ارائه میدهند. با دریافت و قرارگیری تمام سورسکدها در جای مناسب، فرایند ساخت LLVM به طور خودکار ابزارهای Clang Extra Tools و وابستگیهای آنها را خواهد ساخت.
- •
- در صورت استفاده از CMake، میتوانید از تارگت pp-trace نیز برای ساخت انحصاری ابزار pp-trace و وابستگیهای آن استفاده کنید.
CLANG-DOC
فهرست مطالب
- •
- Clang-Doc
- استفاده
- خروجی
- پیکربندی
- •
- گزینهها
ابزار clang-doc ابزاری برای تولید مستندات C و C++ از روی کد منبع و کامنتها است.
این ابزار در مراحل اولیه توسعه قرار دارد، بنابراین ممکن است با باگها و کرشها مواجه شوید. ارسال گزارشها به همراه اطلاعات نحوه بازتولید مشکل به سامانه ردیابی باگ LLVM https://github.com/llvm/llvm-project/issues قطعاً به پروژه کمک خواهد کرد. در صورت داشتن هرگونه ایده یا پیشنهاد، لطفاً درخواست قابلیت جدید (feature request) خود را در آنجا ثبت کنید.
استفاده
ابزار clang-doc ابزاری مبتنی بر LibTooling https://clang.llvm.org/docs/LibTooling.html است و بنابراین به یک پایگاهداده دستورات کامپایل برای پروژه شما نیاز دارد (برای مشاهده نمونهای از نحوه راهاندازی این مورد، به How To Setup Tooling For LLVM https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html مراجعه فرمایید).
این ابزار به طور پیشفرض فهرستی از فایلها را پردازش میکند:
$ clang-doc File1.cpp File2.cpp ... FileN.cpp
این ابزار همچنین میتواند همراه با پایگاهداده دستورات کامپایل به کار گرفته شود:
$ clang-doc --executor=all-TUs compile_commands.json
جهت انتخاب تنها زیرمجموعهای از فایلها از پایگاهداده، از سوییچ --filter استفاده کنید:
$ clang-doc --executor=all-TUs --filter=File[0-9]+.cpp compile_commands.json
خروجی
ابزار clang-doc یک پوشه حاوی مستندات ایجاد میکند. برای هر فضاینام (namespace) و رکورد موجود در کد منبع پروژه، یک فایل تولید میشود که شامل تمام مستندات مربوط به آن مورد (شامل توابع، متدها و enumهای درون آن) خواهد بود.
دایرکتوری سطح بالا از طریق سوییچ output قابل پیکربندی است:
$ clang-doc --output=output/directory/ compile_commands.json
پیکربندی
پیکربندی برای clang-doc در حال حاضر به گزینههای خط فرمان محدود است. در آینده ممکن است قابلیت استفاده از فایل پیکربندی برای آن توسعه یابد، اما در حال حاضر هیچ تلاشی در این جهت در جریان نیست.
گزینهها
ابزار clang-doc گزینههای زیر را ارائه میدهد:
$ clang-doc --help
OVERVIEW: Generates documentation from source code and comments.
Example usage for files without flags (default):
$ clang-doc File1.cpp File2.cpp ... FileN.cpp
Example usage for a project using a compile commands database:
$ clang-doc --executor=all-TUs compile_commands.json
USAGE: clang-doc [options] <source0> [... <sourceN>]
OPTIONS:
Generic Options:
-help - Display available options (-help-hidden for more)
-help-list - Display list of available options (-help-list-hidden for more)
-version - Display the version of this program
clang-doc options:
--doxygen - Use only doxygen-style comments to generate docs.
--extra-arg=<string> - Additional argument to append to the compiler command line
Can be used several times.
--extra-arg-before=<string> - Additional argument to prepend to the compiler command line
Can be used several times.
--format=<value> - Format for outputted docs.
=yaml - Documentation in YAML format.
=md - Documentation in MD format.
=html - Documentation in HTML format.
--ignore-map-errors - Continue if files are not mapped correctly.
--output=<string> - Directory for outputting generated files.
-p <string> - Build path
--project-name=<string> - Name of project.
--public - Document only public declarations.
--repository=<string> -
URL of repository that hosts code.
Used for links to definition locations.
--source-root=<string> -
Directory where processed files are stored.
Links to definition locations will only be
generated if the file is in this dir.
--stylesheets=<string> - CSS stylesheets to extend the default styles.
فلگهای زیر تنها زمانی باید استفاده شوند که مقدار format روی html تنظیم شده باشد: - repository - source-root - stylesheets
نگهدارندگان ابزارهای اضافی CLANG
این فایل فهرستی از نگهدارندگان https://llvm.org/docs/DeveloperPolicy.html#maintainers برای پروژهٔ Extra Clang Tools https://clang.llvm.org/extra/index.html است.
- •
- نگهدارندگان فعال
- نگهدارندهٔ ارشد
- clang-tidy
- clang-query
- clang-doc
- clangd
- •
- نگهدارندگان غیرفعال
- نگهدارندگان ارشد بازنشسته (Emeritus)
- نگهدارندگان غیرفعال مؤلفهها
نگهدارندگان فعال
افراد زیر نگهدارندگان فعال پروژه هستند. لطفاً برای بازبینی کدها (Code Review)، پرسشها درباره حوزه تخصصیشان، یا سایر کمکها با آنها در ارتباط باشید.
نگهدارندهٔ ارشد
Aaron Ballman <aaron@aaronballman.com> (ایمیل), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord)
clang-tidy
Congcong Cai <congcongcai0907@163.com> (ایمیل), HerrCai0907 (GitHub), HerrCai0907 (Discourse)
Julian Schmidt <git.julian.schmidt@gmail.com> (ایمیل), 5chmidti (GitHub), 5chmidti (Discourse), 5chmidti (Discord)
Piotr Zegar <me@piotrzegar.pl> (ایمیل), PiotrZSL (GitHub), PiotrZSL (Discourse), PiotrZSL (Discord)
Victor Baranov <bar.victor.2002@gmail.com> (ایمیل), vbvictor (GitHub), vbvictor (Discourse), vbvictor (Discord)
clang-query
Aaron Ballman <aaron@aaronballman.com> (ایمیل), aaron.ballman (Phabricator), AaronBallman (GitHub), AaronBallman (Discourse), aaronballman (Discord)
clang-doc
Paul Kirth <paulkirth@google.com> (ایمیل), ilovepi (GitHub), ilovepi (Discourse)
Peter Chou <peterchou411@gmail.com> (ایمیل), PeterChou1 (GitHub), PeterChou1 (Discourse), .peterchou (Discord)
clangd
Nathan Ridge <zeratul976@hotmail.com> (ایمیل), HighCommander4 (GitHub), HighCommander4 (Discourse), nridge (Discord)
Chris Bieneman <chris.bieneman@gmail.com> (ایمیل), llvm-beanz (GitHub), beanz (Discord), beanz (Discourse)
Kadir Çetinkaya <kadircet@google.com> (ایمیل), kadircet (GitHub) kadircet (Discourse), kadircet (Discord)
نگهدارندگان غیرفعال
افراد زیر با بزرگواری زمان خود را صرف انجام مسئولیتهای نگهداری این پروژه کردهاند، اما دیگر در این نقش فعال نیستند. از تمام کمکهای شما در موفقیت این پروژه سپاسگزاریم!
راهبران نگهدارنده افتخاری
Manuel Klimek (<klimek@google.com> (email), r4nt (GitHub))
نگهدارندگان غیرفعال مؤلفهها
Nathan James (<n.james93@hotmail.co.uk>) -- clang-tidy Julie Hockett (<juliehockett@google.com>) -- clang-doc Sam McCall (<sammccall@google.com> (email), sam-mccall (GitHub, Discourse, Discord)) -- clangd
مستندات Doxygen ساختار نرمافزاری داخلی که ابزارهای clang-tools-extra را تشکیل میدهند توصیف میکند، نه کاربرد خارجی این ابزارها را. مستندات Doxygen حاوی هیچ دستورالعملی درباره نحوه استفاده از ابزارها نیست، بلکه تنها شامل APIهایی است که این نرمافزار را میسازند. برای دستورالعملهای استفاده، لطفاً به راهنمای کاربر یا راهنمای مرجع هر ابزار مراجعه کنید.
- •
- مستندات Doxygen <doxygen/annotated.html>
نکته:
- فهرست <>
- صفحه جستجو <>
نویسنده (AUTHOR)
The Clang Team
حق نشر (COPYRIGHT)
2007-2026, The Clang Team
| June 21, 2026 | 22 |