2016-04-27 28 views
12

Trong đợt kiểm tra mã, tôi tìm thấy mã nguồn như thế này:Tự động phát hiện std giống hệt nhau liên tiếp :: string :: tìm() gọi

void f_odd(std::string &className, std::string &testName) 
{ 
    if (className.find("::") != std::string::npos) 
    { 
    testName = className.substr(className.find("::") + 2); 
    (void)className.erase(className.find("::"), std::string::npos); 
    } 
} 

Trong chức năng này, std :: string :: tìm() được được gọi là ba lần với cùng một mẫu (ở đây "::").

Mã này tất nhiên có thể được refactored để

void f(std::string &className, std::string &testName) 
{ 
    const size_t endOfClassNamePos = className.find("::"); 
    if (endOfClassNamePos != std::string::npos) 
    { 
    testName = className.substr(endOfClassNamePos + 2); 
    (void)className.erase(endOfClassNamePos, std::string::npos); 
    } 
} 

nơi tìm được gọi là chỉ một lần.

Câu hỏi

Không ai biết một chiến lược để phát hiện như một mô hình như thế này? Tôi đang có một cơ sở mã rất lớn, nơi tôi có ý định phát hiện ra mẫu hình này. Tôi định sử dụng môi trường Windows hoặc Linux.

chiến lược tiềm năng

  1. Sử dụng/thích ứng một công cụ phân tích mã tĩnh, giống như cppcheck để phát hiện các loại hợp kỳ dị.
  2. Tìm kiếm trong cơ sở mã với cụm từ thông dụng.
  3. Sử dụng/điều chỉnh clang-gọn gàng để phát hiện mẫu này.
  4. Viết trình kiểm tra tùy chỉnh bằng một số ngôn ngữ (ví dụ: Python) phát hiện những vấn đề này. Trong trường hợp này, việc kiểm tra phải được thực hiện trên mã được xử lý trước.

Không Go

  • xét Manual

Cập nhật 1

tôi quyết định bắt đầu với chiến lược tiềm năng 1). Tôi có kế hoạch để thích ứng với cppcheck để nắm bắt vấn đề này.

Cppcheck cung cấp khả năng viết các quy tắc tùy chỉnh, dựa trên biểu thức thông thường PCRE. Đối với điều này, cppcheck phải được biên dịch với hỗ trợ PCRE được kích hoạt. Kể từ môi trường thử nghiệm hiện tại được dựa trên Linux, các lệnh sau đây có thể được sử dụng để tải về phiên bản mới nhất của cppcheck:

git clone https://github.com/danmar/cppcheck.git && cd cppcheck

Sau đó, biên dịch và cài đặt công cụ này như sau:

sudo make install HAVE_RULES=yes

Bây giờ, việc thiết lập công cụ cơ bản được thực hiện. Để phát triển một quy tắc cppcheck, tôi đã chuẩn bị một trường hợp thử nghiệm đơn giản (tệp: test.cpp), tương tự như mã mẫu trong phần đầu tiên của bài viết này. Tệp này chứa ba hàm và quy tắc cppcheck sẽ phát ra cảnh báo trên f_oddf_odd1 về các lệnh gọi std::string::find giống hệt nhau liên tiếp.

kiểm tra.cpp:

#include <string> 
void f(std::string &className, std::string &testName) 
{ 
    const size_t endOfClassNamePos = className.find("::"); 
    if (endOfClassNamePos != std::string::npos) 
    { 
    testName = className.substr(endOfClassNamePos + 2); 
    (void)className.erase(endOfClassNamePos, std::string::npos); 
    } 
} 

void f_odd(std::string &className, std::string &testName) 
{ 
    if (className.find("::") != std::string::npos) 
    { 
     testName = className.substr(className.find("::") + 2); 
     (void)className.erase(className.find("::"), std::string::npos); 
    } 
} 

#define A "::" 
#define B "::" 
#define C "::" 
void f_odd1(std::string &className, std::string &testName) 
{ 
    if (className.find(A) != std::string::npos) 
    { 
     testName = className.substr(className.find(B) + 2); 
     (void)className.erase(className.find(C), std::string::npos); 
    } 
} 

Cho đến nay rất tốt. Bây giờ cppcheck phải được tinh chỉnh để bắt các cuộc gọi std::string::find giống hệt nhau liên tiếp. Đối với điều này tôi đã tạo ra một cppcheck_rule-file có chứa một biểu thức chính quy phù hợp với liên tiếp giống hệt std::string::find cuộc gọi:

<?xml version="1.0"?> 
<rule> 
<tokenlist>normal</tokenlist> 
<pattern><![CDATA[([a-zA-Z][a-zA-Z0-9]*)(\s*\.\s*find)(\s*\(\s*\"[ -~]*\"\s*\))[ -\{\n]*(\1\2\3)+[ -z\n]]]></pattern> 
<message> 
    <severity>style</severity> 
    <summary>Found identical consecutive std::string::find calls.</summary> 
</message> 

Tập tin này có thể được sử dụng để mở rộng cppcheck về một tấm séc mới. Cho phép thử:

cppcheck --rule-file=rules/rule.xml test/test.cpp

và đầu ra là

Checking test/test.cpp... 
[test/test.cpp:14]: (style) Found identical consecutive std::string::find calls. 
[test/test.cpp:26]: (style) Found identical consecutive std::string::find calls. 

Bây giờ, giống hệt nhau liên tiếp std::string::find cuộc gọi có thể được phát hiện trong mã C/C++. Có ai biết một giải pháp tốt hơn/hiệu quả hơn hoặc thông minh hơn không?

Tài liệu tham khảo:


+3

Bạn có thể viết [clang-tidy] của riêng bạn (http://clang.llvm.org/extra/clang-tidy/) kiểm tra để phát hiện điều này. – Jonas

+0

@ Jonas Cảm ơn bạn clang-tidy là một công cụ mạnh khác có thể thực hiện công việc. Tôi sẽ cập nhật phần giải pháp tiềm năng của mình. – orbitcowboy

+2

Bạn có thể đặt lại cụm từ cho câu hỏi của mình sao cho nó không xuất hiện như một yêu cầu đề xuất công cụ? Đây là [chủ đề không rõ ràng] (https://stackoverflow.com/help/on-topic) trên trang web này. – 5gon12eder

Trả lời

1

Vấn đề chính với một công cụ như vậy là một phân tích từ vựng chỉ có thể kiểm tra xem có văn bản lặp lại. Lấy ví dụ của bạn, gọi className.find("::") hai lần là một vấn đề tiềm năng nếu biến đề cập đến cùng một chuỗi hai lần. Nhưng hãy để tôi thêm một số nhỏ đổi thành mã của bạn: className = className.substr(className.find("::") + 2);. Đột nhiên ý nghĩa của className.find tiếp theo đã thay đổi đáng kể.

Bạn có thể tìm thấy những thay đổi như vậy không? Bạn cần một trình biên dịch đầy đủ cho điều đó, và thậm chí sau đó bạn phải bi quan. Gắn bó với ví dụ của bạn, có thể thay đổi className qua một trình lặp không? Nó không chỉ là thao tác trực tiếp mà bạn cần phải biết.

Không có tin tức tích cực? Vâng: các trình biên dịch hiện tại có một cơ chế tương tự. Nó được gọi là Loại bỏ biểu hiện phổ biến phụ, và nó hoạt động theo khái niệm như bạn muốn nó hoạt động trong ví dụ trên. Nhưng đó cũng là tin xấu theo một cách: Nếu tình hình có thể phát hiện được, nó không quan trọng vì nó đã được tối ưu hóa bởi trình biên dịch!

Các vấn đề liên quan