Я пытаюсь написать проверку для определенных сценариев, связанных с потоковой передачей. Рассмотрим эту простую функцию:
#include <iostream>
#include <stdint.h>
void foo(std::ostream& os, uint8_t i) {
os << i;
os << 4 << i;
}
Если я бегу clang-query
протестировать некоторые совпадения:
$ clang-query foo.cxx --
clang-query> match cxxOperatorCallExpr(hasOverloadedOperatorName("<<"))
// [ ... snip some std library matches ... ]
Match #7:
foo.cxx:5:5: note: "root" binds here
os << i;
^~~~~~~
Match #8:
foo.cxx:6:5: note: "root" binds here
os << 4 << i;
^~~~~~~~~~~~
Match #9:
foo.cxx:6:5: note: "root" binds here
os << 4 << i;
^~~~~~~
Это соответствует всем 3 применениям operator<<
что у меня есть в этой программе. Отлично. Однако, если я попытаюсь добавить фильтр для того, как выглядит первый аргумент:
clang-query> match cxxOperatorCallExpr(hasOverloadedOperatorName("<<"), hasArgument(0, expr(hasType(asString("std::ostream")))))
Match #1:
foo.cxx:5:5: note: "root" binds here
os << i;
^~~~~~~
Match #2:
foo.cxx:6:5: note: "root" binds here
os << 4 << i;
^~~~~~~
Вот и все. Это не соответствует всему выражению os << 4 << i
, Почему бы и нет? Это выражение имеет тип std::ostream
, Если я сваливаю AST напрямую, я вижу:
|-CXXOperatorCallExpr 0x953f618 <line:6:5, col:16> 'basic_ostream<char, struct std::char_traits<char> >':'class std::basic_ostream<char>' lvalue
| |-ImplicitCastExpr 0x953f600 <col:13> 'basic_ostream<char, struct std::char_traits<char> > &(*)(basic_ostream<char, struct std::char_traits<char> > &, unsigned char)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x953f5d8 <col:13> 'basic_ostream<char, struct std::char_traits<char> > &(basic_ostream<char, struct std::char_traits<char> > &, unsigned char)' lvalue Function 0x94bcd40 'operator<<' 'basic_ostream<char, struct std::char_traits<char> > &(basic_ostream<char, struct std::char_traits<char> > &, unsigned char)'
| |-CXXOperatorCallExpr 0x953f160 <col:5, col:11> 'std::basic_ostream<char, struct std::char_traits<char> >::__ostream_type':'class std::basic_ostream<char>' lvalue
| | |-ImplicitCastExpr 0x953f148 <col:8> 'std::basic_ostream<char, struct std::char_traits<char> >::__ostream_type &(*)(int)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x953f0c0 <col:8> 'std::basic_ostream<char, struct std::char_traits<char> >::__ostream_type &(int)' lvalue CXXMethod 0x94b7740 'operator<<' 'std::basic_ostream<char, struct std::char_traits<char> >::__ostream_type &(int)'
| | |-DeclRefExpr 0x953ec88 <col:5> 'std::ostream':'class std::basic_ostream<char>' lvalue ParmVar 0x953e470 'os' 'std::ostream &'
| | `-IntegerLiteral 0x953ecb0 <col:11> 'int' 4
| `-ImplicitCastExpr 0x953f5c0 <col:16> 'uint8_t':'unsigned char' <LValueToRValue>
| `-DeclRefExpr 0x953f1a8 <col:16> 'uint8_t':'unsigned char' lvalue ParmVar 0x953e500 'i' 'uint8_t':'unsigned char'
Как внешнее, так и внутреннее CXXOperatorCallExpr
говорят, что их выражение class std::basic_ostream<char> lvalue
, Как правильно правильно сопоставить этот аргумент?
Это определенно проблема с типом возврата первого дополнительного вызова <<
см. следующее сопоставление, которое также совпадает с "class std::__1::basic_ostream<char>"
и с этим пропущенный вызов:
clang-query> match callExpr(hasArgument(0, expr(hasType(anyOf(asString("class std::__1::basic_ostream<char>"), asString("std::ostream"))))))
Match #1:
/Users/ug/clangtest/main.cc:5:5: note: "root" binds here
os << i;
^~~~~~~
Match #2:
/Users/ug/clangtest/main.cc:6:5: note: "root" binds here
os << 4 << i;
^~~~~~~~~~~~
Match #3:
/Users/ug/clangtest/main.cc:6:5: note: "root" binds here
os << 4 << i;
^~~~~~~
3 matches.
Других решений пока нет …