Может ли Klocwork (или другие инструменты) знать о типах, директивах typedefs и #define? - PullRequest
6 голосов
/ 22 июня 2011

Я искал инструментов для помощи в обнаружении ошибок, которые мешают правильной работе программы в виде 64-битного кода.Совсем недавно я играл с Klocwork и его пользовательской функцией проверки, которая позволяет мне перемещаться по исходному коду в виде дерева с помощью XPath.Это полезно в качестве «более разумной» альтернативы регулярным выражениям, но я не смог заставить ее узнать о типах.

Например, допустим, я хотел бы найти каждый экземпляр forцикл, который использует либо int, либо long для подсчета.Следующий код легко найти.

for (int i = 0; i < 10; i++)
    // ...

Поиск этого кода тривиален, поскольку определение переменной находится прямо внутри цикла.Однако рассмотрим следующий пример.

int i;
// ...
for (i = 0; i < 10; i++)
    // ...

Это сложно найти, поскольку определение переменной отделено от цикла, а необходимое выражение XPath будет либо громоздким, либо подверженным ошибкам.

Итак, могут ли пользовательские правила Klocwork найти выражения, подобные этому, где необходима осведомленность о типах, включая разрешение операторов typedef и #define?Существуют ли другие инструменты, которые могут это сделать?

РЕДАКТИРОВАТЬ 1: Рассмотрим следующий пример.

typedef int myint;

void Foo() {
    int i;
    for (i = 0; i < 10; i++) {
        Bar();
    }

    myint j;
    for (j = 0; j < 10; j++) {
        Bar();
    }
}

Решение , предоставленное ahmeddirie , находитпервый цикл, потому что тип i явно определен как int.Однако второй цикл не найден, потому что typedef скрыл базовый тип.Какие инструменты отслеживают типы таким образом, чтобы идентифицировать вторую переменную цикла j как действительно int?

Ответы [ 3 ]

2 голосов
/ 23 июня 2011

Вы можете использовать Clang (http://clang.llvm.org) или даже Elsa (https://github.com/dsw/oink-stack/)) для генерации AST после распространения типов и создания шаблонов. Оба предоставляют приличный C ++ API и некоторые средства для выгрузки ASTв читаемый текст. И оба варианта бесплатно .

1 голос
/ 23 июня 2011

Не совсем уверен, что это именно то, что вам нужно, но вы всегда можете легко разрешить типы с помощью встроенных функций.Например, отвечая на ваш вопрос (хотя, возможно, не ваша основная потребность):

//ForStmt / Init::ExprStmt / Expr::BinaryExpr [ $type := Left.getTypeName() ] [ $type = 'int' | $type.contains('long') ]

Это найдет циклы 'for', которые довольно легко используют типы счетчиков 'int' или 'long int', и, очевидно, может бытьприменяется к любому элементу выражения на основе выражений.

Определения типов поддаются такому манипулированию, как программисту, так и языку.Однако определения препроцессора приведут только к их типу на родном языке (т. Е. Сам макрос недоступен для манипулирования через KAST, только к тому, что он расширяет).

1 голос
/ 23 июня 2011

Компания, в которой я работаю, Semantic Designs Inc. предоставляет инструменты, включающие общая инфраструктура для анализа и преобразования программ и специфические компоненты анализа для различных языков программирования. Вместе они известны как DMS. В случае C ++ DMS включает интегрированные лексеры, препроцессоры, парсеры, а также имя и тип компоненты разрешения для каждого из GCC3, GCC4, ISO14882c1998 (ANSI), Visual C ++ 6.0 и неуправляемая Visual Studio 2005 C ++. Для различных диалекты С, также существуют анализ потока управления, побочные эффекты анализатор и анализатор символьной зависимости, с помощью которого такие инструменты, как средство проверки указателя, средство удаления неактивного кода, средство профилирования функций и программа слайсер были реализованы.

Компоненты разрешения имени и типа предоставляют полную таблицу символов информация и возможности поиска, так что ссылки на идентификаторы могут быть легко связаны с их типами и другими декларативная информация. Информация, как это захвачено и используется компилятором, но сохраняется вместе с абстрактными синтаксическими деревьями в форме, пригодной для адаптивного повторного использования любым инструментом, включающим компонент.

Semantic Designs недавно создал специальный инструмент, который связанные конкретно с типами индексных переменных в цикле объявления, такие как в вашем примере. В этом случае проблема была обновить код GCC2, который использовал переключатель компилятора -fno-for-scope, который предоставил правило разрешения области для переменных цикла, которое не было поддерживается в более поздних диалектах GCC. Инструмент должен был преобразовать петли, перемещая объявления своих переменных цикла во внешний контекст это сохранило правило области действия -fno-for-scope. Где такие изменения не были необходимы, никаких изменений не было.

Таким образом, инструмент должен был различать тип, связанный с каждой ссылкой к переменной цикла, дифференцируя в случае маскирующих областей, и восстановить код так, чтобы разрешение имен GCC3 и GCC4 привести к той же семантической интерпретации, что и GCC2 с -fno-для-сферы. Это требовало возможности доступа к таблице символов информация, связанная с каждой ссылкой на переменную, и в случае где код был перемещен, чтобы восстановить правильный синтаксис для объявления типа для любой переменной, объявление которой было перемещено. Таблица символов и таблица идентификаторов, предоставленная DMS Компонент разрешения имен и типов в C ++ содержит все необходимое информация, и модуль для восстановления синтаксиса предписанного типа допускается обобщение правильных объявлений новых типов.

Например, рассмотрим пример:

// loop variable hides variable in global scope
// will change meaning without -fno-for-scope
// fix: move decl. of cnt before for-loop
//   optionally rename globcnt loop variable

float globcnt = 0.0;

int Foo::foo3() {
    for (int globcnt = 0; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

GCC2-семантика -fno-for-scope указывает, что ссылки на globcnt вне цикла находятся в переменной цикла, хотя GCC3 будет вывести переменную цикла из области видимости и разрешить ссылки на глобальная переменная. Инструмент преобразовал этот код в:

float globcnt = 0.0;

int Foo::foo3() {
    int globcnt = 0;
    for (; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

Если бы код не был преобразован, GCC4 всегда возвращал бы значение 1 из Foo: foo3. Преобразованный, однако, значение будет иметь были подвержены влиянию итераций цикла, как изначально Gcc2. Инструмент должен был признать, что окончательная ссылка на globcnt для локальной переменной типа int, а не для глобальной переменной введите float, что можно сделать с помощью поиска в таблице символов, и действовать соответственно.

С другой стороны, инструмент распознает в следующем коде, что не было ссылок на i вне цикла, так что это было приемлемо (и желательно) оставить объявление переменной цикла без изменений.

int Foo::foo0() {
    for (int i = 0; i < 10; i++) {
        globalInt += i*i;
    }
    return 0;
}
...