blob: 27f9b58f866851c94b0fd5de9e875d36479b6206 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
using namespace clang::tooling;
using namespace llvm;
using namespace clang;
using namespace clang::ast_matchers;
namespace {
StatementMatcher CXXNewMatcher = cxxNewExpr().bind("useNew");
StatementMatcher CXXDeleteMatcher = cxxDeleteExpr().bind("useDelete");
} // namespace
class AllocLinter : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult& Result) {
auto& Context = *Result.Context;
auto& DiagnosticsEngine = Context.getDiagnostics();
auto& SourceManager = Context.getSourceManager();
const auto* NewExpr = Result.Nodes.getNodeAs<CXXNewExpr>("useNew");
if (NewExpr &&
!SourceManager.isInSystemHeader(NewExpr->getLocStart()) && // Not in system headers
NewExpr->getNumPlacementArgs() == 0) { // Not placement new
FunctionDecl* NewFuncDecl = NewExpr->getOperatorNew();
if (NewExpr->isGlobalNew()) {
diag(NewExpr->getLocStart(), "Using global `::operator new%0()`", DiagnosticsEngine)
<< (NewExpr->isArray() ? "[]" : "");
} else if (NewFuncDecl && SourceManager.isInSystemHeader(NewFuncDecl->getLocation())) {
diag(NewExpr->getLocStart(), "Using `operator new%0()` from %1", DiagnosticsEngine)
<< (NewExpr->isArray() ? "[]" : "")
<< NewFuncDecl->getLocation().printToString(SourceManager);
}
}
const auto* DeleteExpr = Result.Nodes.getNodeAs<CXXDeleteExpr>("useDelete");
if (DeleteExpr && !DeleteExpr->getDestroyedType().isNull() &&
!DeleteExpr->getDestroyedType()->isVoidType() &&
!SourceManager.isInSystemHeader(DeleteExpr->getLocStart())) { // Not in system headers
FunctionDecl* DeleteFuncDecl = DeleteExpr->getOperatorDelete();
if (DeleteExpr->isGlobalDelete()) {
diag(DeleteExpr->getLocStart(), "Using global `::operator delete%0()`", DiagnosticsEngine)
<< (DeleteExpr->isArrayForm() ? "[]" : "");
} else if (DeleteFuncDecl && SourceManager.isInSystemHeader(DeleteFuncDecl->getLocation())) {
diag(DeleteExpr->getLocStart(), "Using `operator delete%0()` from %1", DiagnosticsEngine)
<< (DeleteExpr->isArrayForm() ? "[]" : "")
<< DeleteFuncDecl->getLocation().printToString(SourceManager);
}
}
}
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
DiagnosticsEngine& DiagnosticsEngine) {
auto ID = DiagnosticsEngine.getDiagnosticIDs()->getCustomDiagID(clang::DiagnosticIDs::Error,
Description);
return DiagnosticsEngine.Report(Loc, ID);
}
};
static llvm::cl::OptionCategory AllocLinterToolCategory("alloc-linter options");
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp("\nThis is a tool for finding global "
"use of operator new/delete (from either global\n"
"or from the standard library) and other forms "
"of global allocation.\n");
int main(int argc, const char** argv) {
CommonOptionsParser OptionsParser(argc, argv, AllocLinterToolCategory);
ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());
AllocLinter Linter;
MatchFinder Finder;
Finder.addMatcher(CXXNewMatcher, &Linter);
Finder.addMatcher(CXXDeleteMatcher, &Linter);
return Tool.run(newFrontendActionFactory(&Finder).get());
}