Kaydet (Commit) 4fd910a0 authored tarafından Luboš Luňák's avatar Luboš Luňák

tutorial examples for writing new Clang plugin actions

http://wiki.documentfoundation.org/Clang_plugins

Change-Id: Ieb4fc186490e81ab961c094ca0a7fcdabc0f348f
üst 18e61518
...@@ -40,4 +40,5 @@ Modifications will be written directly to the source files. ...@@ -40,4 +40,5 @@ Modifications will be written directly to the source files.
== Code documentation / howtos == == Code documentation / howtos ==
TBD http://wiki.documentfoundation.org/Clang_plugins
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#include "tutorial1.hxx"
/*
This is a compile check.
Checks all return statements and warns if they return literal false (i.e. 'return false').
*/
namespace loplugin
{
// Ctor, nothing special, pass the argument(s).
Tutorial1::Tutorial1( ASTContext& context )
: Plugin( context )
{
}
// Perform the actual action.
void Tutorial1::run()
{
// Traverse the whole AST of the translation unit (i.e. examine the whole source file).
// The Clang AST helper class will call VisitReturnStmt for every return statement.
TraverseDecl( context.getTranslationUnitDecl());
}
// This function is called for every return statement.
// Returning true means to continue with examining the AST, false means to stop (just always return true).
bool Tutorial1::VisitReturnStmt( ReturnStmt* returnstmt )
{
// Helper function from the LO base plugin class, call at the very beginning to ignore sources
// that should not be processed (e.g. system headers).
if( ignoreLocation( returnstmt ))
return true;
// Get the expression in the return statement (see ReturnStmt API docs).
const Expr* expression = returnstmt->getRetValue();
if( expression == NULL )
return true; // plain 'return;' without expression
// Check if the expression is a bool literal (Clang uses dyn_cast<> instead of dynamic_cast<>).
if( const CXXBoolLiteralExpr* boolliteral = dyn_cast< CXXBoolLiteralExpr >( expression ))
{ // It is.
if( boolliteral->getValue() == false ) // Is it 'return false;' ? (See CXXBoolLiteralExpr API docs)
{ // Ok, warn, use LO plugin helper function.
report( DiagnosticsEngine::Warning, // It's just a warning.
"returning false", // the message
boolliteral->getLocStart()) // and the exact position where the message should point
<< returnstmt->getSourceRange(); // and the full return statement to highlight (optional)
}
}
return true;
}
// Register the plugin action with the LO plugin handling.
static Plugin::Registration< Tutorial1 > X( "tutorial1" );
} // namespace
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#ifndef TUTORIAL1_H
#define TUTORIAL1_H
#include "plugin.hxx"
namespace loplugin
{
// The class implementing the plugin action.
class Tutorial1
// Inherits from the Clang class that will allow examing the Clang AST tree (i.e. syntax tree).
: public RecursiveASTVisitor< Tutorial1 >
// And the base class for LO Clang plugins.
, public Plugin
{
public:
// Ctor, nothing special.
Tutorial1( ASTContext& context );
// The function that will be called to perform the actual action.
virtual void run();
// Function from Clang, it will be called for every return statement in the source.
bool VisitReturnStmt( ReturnStmt* returnstmt );
};
} // namespace
#endif // POSTFIXINCREMENTFIX_H
// This is just an example file to see what AST looks like for return statements.
// To the the AST, run :
// clang++ -fsyntax-only -Xclang -ast-dump tutorial1_example.cxx
void f()
{
return;
}
bool g()
{
return false;
}
bool h()
{
return 3 > 2;
}
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#include "tutorial2.hxx"
/*
This is a compile check.
Warns about if statements with a comparison followed by literal return false:
if( a == 1 )
return false;
*/
namespace loplugin
{
Tutorial2::Tutorial2( ASTContext& context )
: Plugin( context )
{
}
void Tutorial2::run()
{
// The Clang AST helper class will call VisitIfStmt for every if statement.
TraverseDecl( context.getTranslationUnitDecl());
}
// This function is called for every if statement.
bool Tutorial2::VisitIfStmt( IfStmt* ifstmt )
{
if( ignoreLocation( ifstmt ))
return true;
// Check if the condition of the if statement is a binary operator.
if( const BinaryOperator* oper = dyn_cast< BinaryOperator >( ifstmt->getCond()))
{
// And if it's operator==.
if( oper->getOpcode() == BO_EQ )
{
// Now check if the sub-statement is 'return false'.
const Stmt* warn = NULL; // The return statement (for the warning message).
// Check if the sub-statement is directly 'return false;'.
if( isReturnFalse( ifstmt->getThen()))
warn = ifstmt->getThen();
// Check if the sub-statement is '{ return false; }'
else if( CompoundStmt* compound = dyn_cast< CompoundStmt >( ifstmt->getThen()))
{
if( compound->size() == 1 ) // one statement
if( isReturnFalse( *compound->body_begin())) // check the one sub-statement
warn = *compound->body_begin();
}
if( warn != NULL ) // there is a return statement to warn about.
{
report( DiagnosticsEngine::Warning,
"returning false after if with equality comparison",
cast< ReturnStmt >( warn )->getRetValue()->getLocStart()) // the 'false' in the return
<< warn->getSourceRange();
// Also add a note showing the if statement.
report( DiagnosticsEngine::Note,
"the if statement is here",
ifstmt->getLocStart());
}
}
}
return true;
}
bool Tutorial2::isReturnFalse( const Stmt* stmt )
{
// Is it return statement?
if( const ReturnStmt* returnstmt = dyn_cast< ReturnStmt >( stmt ))
{
// dyn_cast_or_null<> can also be passed NULL, unlike dyn_cast<>
if( const CXXBoolLiteralExpr* boolliteral = dyn_cast_or_null< CXXBoolLiteralExpr >( returnstmt->getRetValue()))
{
if( boolliteral->getValue() == false )
return true;
}
}
return false;
}
// Register the plugin action with the LO plugin handling.
static Plugin::Registration< Tutorial2 > X( "tutorial2" );
} // namespace
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#ifndef TUTORIAL2_H
#define TUTORIAL2_H
#include "plugin.hxx"
namespace loplugin
{
// The same like for Tutorial1.
class Tutorial2
: public RecursiveASTVisitor< Tutorial2 >
, public Plugin
{
public:
Tutorial2( ASTContext& context );
virtual void run();
// Will be called for every if statement.
bool VisitIfStmt( IfStmt* ifstmt );
private:
// Helper function to check if the statement is 'return false;'.
bool isReturnFalse( const Stmt* stmt );
};
} // namespace
#endif // POSTFIXINCREMENTFIX_H
// This is just an example file to see what AST looks like for return statements.
// To the the AST, run :
// clang++ -fsyntax-only -Xclang -ast-dump tutorial1_example.cxx
bool g()
{
if( 1 == 2 )
return false;
if( 1 == 2 )
{
return false;
}
if( true )
return false;
}
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#include "tutorial3.hxx"
/*
This is a rewriter.
It looks for if statements with a comparison followed by literal return false
and modifies the return statements to 'return maybereturntrue;'
*/
namespace loplugin
{
// Ctor, pass arguments.
Tutorial3::Tutorial3( ASTContext& context, Rewriter& rewriter )
: RewritePlugin( context, rewriter )
{
}
void Tutorial3::run()
{
TraverseDecl( context.getTranslationUnitDecl());
}
bool Tutorial3::VisitIfStmt( IfStmt* ifstmt )
{
if( ignoreLocation( ifstmt ))
return true;
if( const BinaryOperator* oper = dyn_cast< BinaryOperator >( ifstmt->getCond()))
{
if( oper->getOpcode() == BO_EQ )
{
// Modify the sub-statement if it is 'return false'.
modifyReturnFalse( ifstmt->getThen());
// Modify the sub-statement if it is '{ return false; }'.
if( CompoundStmt* compound = dyn_cast< CompoundStmt >( ifstmt->getThen()))
{
if( compound->size() == 1 ) // one statement
modifyReturnFalse( *compound->body_begin());
}
}
}
return true;
}
void Tutorial3::modifyReturnFalse( const Stmt* stmt )
{
// Is it return statement?
if( const ReturnStmt* returnstmt = dyn_cast< ReturnStmt >( stmt ))
{
// dyn_cast_or_null<> can also be passed NULL, unlike dyn_cast<>
if( const CXXBoolLiteralExpr* boolliteral = dyn_cast_or_null< CXXBoolLiteralExpr >( returnstmt->getRetValue()))
{
if( boolliteral->getValue() == false )
{ // It is, modify the false to true using LO plugin helper function.
replaceText( boolliteral->getSourceRange(), "maybereturntrue" );
}
}
}
}
// Register the plugin action with the LO plugin handling.
static Plugin::Registration< Tutorial3 > X( "tutorial3" );
} // namespace
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#ifndef TUTORIAL3_H
#define TUTORIAL3_H
#include "plugin.hxx"
namespace loplugin
{
// Similar like for Tutorial2, but this time the base class is RewritePlugin.
class Tutorial3
: public RecursiveASTVisitor< Tutorial3 >
, public RewritePlugin
{
public:
// One more argument for ctor.
Tutorial3( ASTContext& context, Rewriter& rewriter );
virtual void run();
// Will be called for every if statement.
bool VisitIfStmt( IfStmt* ifstmt );
private:
// Helper function to check if the statement is 'return false;' and
// modify it if yes.
void modifyReturnFalse( const Stmt* stmt );
};
} // namespace
#endif // POSTFIXINCREMENTFIX_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment