How can I get macro name from CStyleCastExpr matcher in clang-tidy? - c++

I using clang-tidy for a while and creating some own checks. But now I stuck in this issue. I have a cstyle cast expression from which I want to get a macro name as string.
#define H_KEY 5;
float *a;
a = (float *)H_KEY; // I want to print out "H_KEY" from this expression
So I registered a matcher like this
void PointersCastCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(cStyleCastExpr().bind("cStyleCastExpr"), this);
}
I'm able to catch every cstyle cast and get a subExpr from it like this.
const Expr * subExpr = cStyleCast->getSubExpr();
So the clang tidy now give me information that I have "int" type sub-expression which is correct but I don't know how can I get the name of it.
What I tried was dynamic cast to DeclRefExpr, but this not pass. Also tried dynamic cast to BuiltinType, then I want to get a declaration but with no luck.
So please help. I think this should not be difficult.
Thank you!

If someone run in this issue, I resolve it like this.
if (subExpr->getExprLoc().isMacroID()) {
SourceManager &SM = *Result.SourceManager;
LangOptions LangOpts = getLangOpts();
StringRef subExprText = Lexer::getSourceText(CharSourceRange::getTokenRange(subExpr->getSourceRange()), SM, LangOpts);}
Maybe there is better approach but this one fits my needs.

Related

Writing a Clang-Tidy check, how do I find if a pointer is initialized before it is called?

I am trying to port some static checks from an old in-house C++ static checker to clang-tidy.
Since I am really new to this tool, I am having a really hard time doing it and I am starting to think that it's not the proper tool to do what I want.
So basically what I am currently trying to implement, is a check on pointer initialization. I want to verify that a local pointer is properly initialized before being used.
For example if I take this sample of code:
void method(const char *);
int main(int argc, char **argv){
const char * ptNotInit;
const char * ptInit = "hello";
method(ptNotInit);
method(ptInit);
return 0;
}
I want to get an error on method(ptNotInit) because I am passing a nullptr to method.
At first I try a very simple matcher:
Finder->addMatcher(varDecl(hasType(pointerType())).bind("pointerDeclaration"),this);
// and
const auto *MatchedPtDecl = Result.Nodes.getNodeAs<VarDecl>("pointerDeclaration");
if ( MatchedPtDecl->hasInit() == false )
// Do an error
So i get an error on ptNotInit and argv, so I add MatchedPtDecl->isLocalVarDecl() and all seems fine.
Except that in my code sample I add:
ptNotInit = "Hello again";
method(ptNotInit);
I still get an error on ptNotInit when I abviously initialized it just before the call to method.
I suppose that the VarDecl method hasInit() just apply for the declaration of the variable, explaining why it return false?
So my question is, how can I know when calling method(ptNotInit) if ptNotInit was initialized?
Clang-tidy seems powerful to find something, but I don't know how to find the lack of something, if you see what I mean...
I try to write more complex matcher to find init like this one
Finder->addMatcher(binaryOperator(hasOperatorName("="),hasLHS(declRefExpr(hasType(pointerType()),hasDeclaration(varDecl().bind("pointerDeclaration"))).bind("affectation")))
If my pointer is on the left of an = operator, that should be an initialization... Ok why not, but at the end I want to know that there are no initialization, I don't want to match initialization syntax... Maybe I am taking the problem backward.
Any tips would help, or if you can point me to an already implemented checker doing something similar, that would be a great help!

C++ Code Smell: Incorrect Initializer Braces Placement

I am trying to fix the following code smell within my C++ code generated from Klocwork(KW):
MISRA.INIT.BRACES: Incorrect initializer braces placement
Below is a snippet of the code I am attempting to clean this up on.
typedef char charString[10];
enum SomeEnum
{
BLAH1_e,
BLAH2_e,
BLAH3_e
};
struct ParentStruct
{
SomeEnum myEnumValue;
charString myCharStringValue;
};
// This is the the part that KW is not happy about
// KW complaining about initializer bracer placement
const ParentStruct myParent[3] =
{
{BLAH1_e, "String1"},
{BLAH2_e, "String2"},
{BLAH3_e, "String3"}
}
I've attempted many variations of bracer placement and can't seem to figure out the exact issue with bracer placement I currently have. This doesn't generate any compile errors nor does this have a negative outcome on the code. Maybe it's just KW but just wanted to get some thoughts before I give up completely.
Below is an alternative bracer placement I attempted as well in case someone throws it out as an answer:
// compiles but KW does not like this as well
const ParentStruct myParent[3] =
{
{BLAH1_e, {"String1"}},
{BLAH2_e, {"String2"}},
{BLAH3_e, {"String3"}}
}
Please do run the analysis with the latest version of Klocwork and check if this issue has been reported by Klocwork at your end.
I am using Klocwork 2021.3 and it seems that MISRA.INIT.BRACES checker is not reporting any issue on your code as expected.

Preferred way of extracting and matching protobuf message typename from an Any package

I've been using Any to package dynamic messages for protobuf.
On the receiver end, I use Any.type_url() to match the enclosed message types.
Correct me if I'm wrong, knowing that .GetDescriptor() is unavailable with Any, I still want to make the matching less of a clutter. I tried to extract the message type with brute force like this:
MyAny pouch;
// unpacking pouch
// Assume the message is "type.googleapis.com/MyTypeName"
....
const char* msgPrefix = "type.googleapis.com/";
auto lenPrefix = strlen(msgPrefix);
const std::string& msgURL = pouch.msg().type_url();
MamStr msgName = msgURL.substr(lenPrefix, msgURL.size());
if (msgName == "MyTypeName") {
// do stuff ...
}
But I'm still wondering if there are cleaner way of skipping the prefix to get the "basename" of the type URL.
Thanks!
You could try
std::string getBaseName(std::string const & url) {
return url.substr(url.find_last_of("/\\") + 1);
}
if it suits you well.
Although there are some cases, it may not explode it correctly.
Let's say you have two params as basename: http://url.com/example/2
This will get the latest, which is 2...
If you are not looking for cross-platform support, you can always go for https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/splitpath-wsplitpath?view=vs-2019

Why this Clang ASTMatcher cause wrong polymorphic conversion?

I am Writing a tool using clang as frontend and matching some AST nodes.
I create ASTMatcher as follow:
void Rule_1_2_1::registerMatchers(MatchFinder *Finder)
{
DeclarationMatcher Matcher = decl(hasType(builtinType().bind("non-typedef"))).bind("non-typedef-decl");
Finder->addMatcher(Matcher, this);
}
void Rule_1_2_1::run(const MatchFinder::MatchResult &Result)
{
if (const BuiltinType *type = Result.Nodes.getNodeAs<BuiltinType>("non-typedef")) {
if (!type->isFloatingPoint() && !type->isInteger())
return;
if (const Decl *decl = Result.Nodes.getNodeAs<Decl>("non-typedef-decl")) {
DiagnosticsEngine &DE = Result.Context->getDiagnostics();
Context->report(this->CheckerName, this->ReportMsg, DE, decl->getLocStart(), DiagnosticIDs::Note);
}
}
}
But compiler gives me following errors:
/usr/include/clang/ASTMatchers/ASTMatchersInternal.h: In instantiation of ‘clang::ast_matchers::internal::PolymorphicMatcherWithParam1<MatcherT, P1, ReturnTypesF>::operator clang::ast_matchers::internal::Matcher<From>() const [with T = clang::Decl; MatcherT = clang::ast_matchers::internal::matcher_hasType0Matcher; P1 = clang::ast_matchers::internal::Matcher<clang::QualType>; ReturnTypesF = void(clang::ast_matchers::internal::TypeList<clang::Expr, clang::TypedefNameDecl, clang::ValueDecl>)]’:
../src/modules/gjb/Rule_1_2_1.cpp:18:81: required from here
/usr/include/clang/ASTMatchers/ASTMatchersInternal.h:1104:5: Error:static assertion failed: right polymorphic conversion
static_assert(TypeListContainsSuperOf<ReturnTypes, T>::value,
^~~~~~~~~~~~~
I know i am not familiar with Clang ASTMatcher and the documentation may be not very detailed.
Why this error happened?
line 18 is the line of Matcher defined.
I'm posting this as an answer since it is too long for a comment, but it is only a guess at your problem, not a definite solution.
The error looks like it occurs when you compile your matcher, not when you apply it. Which means you misused the API, not that it doesn't match anything in your code. The AST matcher API checks that you don't do things that make no sense, like filtering on an attribute that may not even exist.
In your case, you are looking for declarations that have some type. But asking a declaration what its type is doesn't necessarily make sense. The Decl class in Clang is the root of the entire declaration hierarchy and includes things like EmptyDecl (which represents simply a single semicolon outside a statement context) and StaticAssertDecl (static_assert), neither of which have a type.
Every node matcher has type information on what nodes it produces. Every narrowing matcher has information on what nodes it applies to. It is checked at compile time that these are compatible.
They interesting parts of the error message are not the unfortunately vague message, but the static_assert condition itself and the listing of the active parameter substitutions.
TypeListContainsSuperOf<ReturnTypes, T>::value is the condition, i.e. "the type list must contain a type that is a supertype of T".
But what is T, and what does the type list contain? The error message says: "In instantiation of with " and then lists substitutions. There we learn that:
T = clang::Decl
MatcherT = clang::ast_matchers::internal::matcher_hasType0Matcher
ReturnTypesF = void(clang::ast_matchers::internal::TypeList<clang::Expr, clang::TypedefNameDecl, clang::ValueDecl>)
ReturnTypes is not directly listed, but it's pretty obvious that it refers to the parameter type of ReturnTypesF, i.e. the TypeList in there.
This tells us the following things:
The decl() matcher produces clang::Decl nodes.
The matcher we're currently validating is the hasType() matcher.
The hasType() matcher can work on any of clang::Expr, clang::TypedefNameDecl, and clang::ValueDecl.
But Decl is a supertype of TypedefNameDecl and ValueDecl, not the other way around, and unrelated to Expr. This means that the static assertion fails. The decl() matcher does not produce nodes that hasType() can work with.
Depending on your exact goals, using valueDecl() instead might work.

QScriptEngine: bad expression considered valid

I need to implement a simple math expression calculator like 1+2*(3.4 + 0.1)
I thought it'd be quite easy to implement the stuff using
QScriptEngine::evaluate()
but there is a problem: some invalid expressions is considered valid and evaluated to something instead of producing an error.
Example:
QString expression = "1 + 2*("; // <---- wrong expression
auto checkResult = QScriptEngine::checkSyntax(expression);
if (checkResult.state() == QScriptSyntaxCheckResult::Valid)
{
QScriptEngine engine;
auto scriptResult = engine.evaluate(expression);
if (scriptResult.isValid() && scriptResult.isNumber())
{
double value = scriptResult.toNumber(); // <---- the value is 3.0, instead of an error
}
}
So my question is: am I missing something and there is a way I can check the syntax of an expression before a QScriptEngine::evaluate() using Qt?
Another way is to use Lepton library (or similiar one), but I'd prefer to not include other 3rd party components.
Unfortunately there was a configuration/Qt issue. Updating to the latest Qt5.5 & rebuilding the project solved the problem. Thank you #hyde.