Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
35294 views
//===--- CloneChecker.cpp - Clone detection checker -------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// CloneChecker is a checker that reports clones in the current translation10/// unit.11///12//===----------------------------------------------------------------------===//1314#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"15#include "clang/Analysis/CloneDetection.h"16#include "clang/Basic/Diagnostic.h"17#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"18#include "clang/StaticAnalyzer/Core/Checker.h"19#include "clang/StaticAnalyzer/Core/CheckerManager.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"2223using namespace clang;24using namespace ento;2526namespace {27class CloneChecker28: public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {29public:30// Checker options.31int MinComplexity;32bool ReportNormalClones = false;33StringRef IgnoredFilesPattern;3435private:36mutable CloneDetector Detector;37const BugType BT_Exact{this, "Exact code clone", "Code clone"};38const BugType BT_Suspicious{this, "Suspicious code clone", "Code clone"};3940public:41void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,42BugReporter &BR) const;4344void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,45AnalysisManager &Mgr, BugReporter &BR) const;4647/// Reports all clones to the user.48void reportClones(BugReporter &BR, AnalysisManager &Mgr,49std::vector<CloneDetector::CloneGroup> &CloneGroups) const;5051/// Reports only suspicious clones to the user along with information52/// that explain why they are suspicious.53void reportSuspiciousClones(54BugReporter &BR, AnalysisManager &Mgr,55std::vector<CloneDetector::CloneGroup> &CloneGroups) const;56};57} // end anonymous namespace5859void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,60BugReporter &BR) const {61// Every statement that should be included in the search for clones needs to62// be passed to the CloneDetector.63Detector.analyzeCodeBody(D);64}6566void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,67AnalysisManager &Mgr,68BugReporter &BR) const {69// At this point, every statement in the translation unit has been analyzed by70// the CloneDetector. The only thing left to do is to report the found clones.7172// Let the CloneDetector create a list of clones from all the analyzed73// statements. We don't filter for matching variable patterns at this point74// because reportSuspiciousClones() wants to search them for errors.75std::vector<CloneDetector::CloneGroup> AllCloneGroups;7677Detector.findClones(78AllCloneGroups, FilenamePatternConstraint(IgnoredFilesPattern),79RecursiveCloneTypeIIHashConstraint(), MinGroupSizeConstraint(2),80MinComplexityConstraint(MinComplexity),81RecursiveCloneTypeIIVerifyConstraint(), OnlyLargestCloneConstraint());8283reportSuspiciousClones(BR, Mgr, AllCloneGroups);8485// We are done for this translation unit unless we also need to report normal86// clones.87if (!ReportNormalClones)88return;8990// Now that the suspicious clone detector has checked for pattern errors,91// we also filter all clones who don't have matching patterns92CloneDetector::constrainClones(AllCloneGroups,93MatchingVariablePatternConstraint(),94MinGroupSizeConstraint(2));9596reportClones(BR, Mgr, AllCloneGroups);97}9899static PathDiagnosticLocation makeLocation(const StmtSequence &S,100AnalysisManager &Mgr) {101ASTContext &ACtx = Mgr.getASTContext();102return PathDiagnosticLocation::createBegin(103S.front(), ACtx.getSourceManager(),104Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()));105}106107void CloneChecker::reportClones(108BugReporter &BR, AnalysisManager &Mgr,109std::vector<CloneDetector::CloneGroup> &CloneGroups) const {110for (const CloneDetector::CloneGroup &Group : CloneGroups) {111// We group the clones by printing the first as a warning and all others112// as a note.113auto R = std::make_unique<BasicBugReport>(114BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr));115R->addRange(Group.front().getSourceRange());116117for (unsigned i = 1; i < Group.size(); ++i)118R->addNote("Similar code here", makeLocation(Group[i], Mgr),119Group[i].getSourceRange());120BR.emitReport(std::move(R));121}122}123124void CloneChecker::reportSuspiciousClones(125BugReporter &BR, AnalysisManager &Mgr,126std::vector<CloneDetector::CloneGroup> &CloneGroups) const {127std::vector<VariablePattern::SuspiciousClonePair> Pairs;128129for (const CloneDetector::CloneGroup &Group : CloneGroups) {130for (unsigned i = 0; i < Group.size(); ++i) {131VariablePattern PatternA(Group[i]);132133for (unsigned j = i + 1; j < Group.size(); ++j) {134VariablePattern PatternB(Group[j]);135136VariablePattern::SuspiciousClonePair ClonePair;137// For now, we only report clones which break the variable pattern just138// once because multiple differences in a pattern are an indicator that139// those differences are maybe intended (e.g. because it's actually a140// different algorithm).141// FIXME: In very big clones even multiple variables can be unintended,142// so replacing this number with a percentage could better handle such143// cases. On the other hand it could increase the false-positive rate144// for all clones if the percentage is too high.145if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) {146Pairs.push_back(ClonePair);147break;148}149}150}151}152153ASTContext &ACtx = BR.getContext();154SourceManager &SM = ACtx.getSourceManager();155AnalysisDeclContext *ADC =156Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());157158for (VariablePattern::SuspiciousClonePair &Pair : Pairs) {159// FIXME: We are ignoring the suggestions currently, because they are160// only 50% accurate (even if the second suggestion is unavailable),161// which may confuse the user.162// Think how to perform more accurate suggestions?163164auto R = std::make_unique<BasicBugReport>(165BT_Suspicious,166"Potential copy-paste error; did you really mean to use '" +167Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",168PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,169ADC));170R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());171172R->addNote("Similar code using '" +173Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",174PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,175SM, ADC),176Pair.SecondCloneInfo.Mention->getSourceRange());177178BR.emitReport(std::move(R));179}180}181182//===----------------------------------------------------------------------===//183// Register CloneChecker184//===----------------------------------------------------------------------===//185186void ento::registerCloneChecker(CheckerManager &Mgr) {187auto *Checker = Mgr.registerChecker<CloneChecker>();188189Checker->MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption(190Checker, "MinimumCloneComplexity");191192if (Checker->MinComplexity < 0)193Mgr.reportInvalidCheckerOptionValue(194Checker, "MinimumCloneComplexity", "a non-negative value");195196Checker->ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption(197Checker, "ReportNormalClones");198199Checker->IgnoredFilesPattern = Mgr.getAnalyzerOptions()200.getCheckerStringOption(Checker, "IgnoredFilesPattern");201}202203bool ento::shouldRegisterCloneChecker(const CheckerManager &mgr) {204return true;205}206207208