Skip to content

Commit

Permalink
Implement daedalean-template-specializations check for CS.R.49
Browse files Browse the repository at this point in the history
  • Loading branch information
Javier-varez committed Jul 19, 2022
1 parent 83d3432 commit 22f0b84
Show file tree
Hide file tree
Showing 10 changed files with 398 additions and 2 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/daedalean/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_clang_library(clangTidyDaedaleanModule
CommaOperatorMustNotBeUsedCheck.cpp
DerivedClassesCheck.cpp
LocalMethodsAndTypesCheck.cpp
TemplateSpecializationsCheck.cpp
TypeConversionsCheck.cpp
LambdaReturnTypeCheck.cpp
EnumClassCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "ProtectedMustNotBeUsedCheck.h"
#include "StructsAndClassesCheck.h"
#include "SwitchStatementCheck.h"
#include "TemplateSpecializationsCheck.h"
#include "TernaryOperatorMustNotBeUsedCheck.h"
#include "TypeConversionsCheck.h"
#include "UnionsMustNotBeUsedCheck.h"
Expand Down Expand Up @@ -54,6 +55,8 @@ class DaedaleanModule : public ClangTidyModule {
"daedalean-derived-classes");
CheckFactories.registerCheck<LocalMethodsAndTypesCheck>(
"daedalean-local-methods-and-types");
CheckFactories.registerCheck<TemplateSpecializationsCheck>(
"daedalean-template-specializations");
CheckFactories.registerCheck<TypeConversionsCheck>(
"daedalean-type-conversions");
CheckFactories.registerCheck<LambdaReturnTypeCheck>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//===--- TemplateSpecializationsCheck.cpp - clang-tidy --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "TemplateSpecializationsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace daedalean {

namespace {
AST_POLYMORPHIC_MATCHER(isDefaultDefinedAndHasSpecializations,
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionTemplateDecl,
ClassTemplateDecl)) {
if (Node.isThisDeclarationADefinition()) {
for (const auto *Specialization : Node.specializations()) {
if (Specialization->isThisDeclarationADefinition()) {
return true;
}
}
}
return false;
}

} // namespace

void TemplateSpecializationsCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(classTemplateDecl(isDefaultDefinedAndHasSpecializations())
.bind("class-template-decl"),
this);

Finder->addMatcher(
functionTemplateDecl(isDefaultDefinedAndHasSpecializations())
.bind("function-template-decl"),
this);
}

void TemplateSpecializationsCheck::check(
const MatchFinder::MatchResult &Result) {

if (const auto *ClassTemplate =
Result.Nodes.getNodeAs<ClassTemplateDecl>("class-template-decl")) {
const auto ClassTemplateDeclLocFile =
Result.SourceManager->getFilename(ClassTemplate->getLocation());

llvm::SmallVector<ClassTemplatePartialSpecializationDecl *>
PartialSpecializations;
ClassTemplate->getPartialSpecializations(PartialSpecializations);

for (const auto *Specialization : PartialSpecializations) {
const auto *Definition = Specialization->getDefinition();
if (Definition == nullptr) {
return;
}

const auto ClassTemplateSpecializationLocFile =
Result.SourceManager->getFilename(Definition->getLocation());

if (ClassTemplateSpecializationLocFile != ClassTemplateDeclLocFile) {
diag(Definition->getLocation(),
"Partial class template specialization %0 should be defined in "
"the "
"same file as %1")
<< Specialization << ClassTemplate;
}
}

for (auto *Specialization : ClassTemplate->specializations()) {
const auto *Definition = Specialization->getDefinition();
if (Definition == nullptr) {
return;
}

const auto ClassTemplateSpecializationLocFile =
Result.SourceManager->getFilename(Definition->getLocation());

if (ClassTemplateSpecializationLocFile != ClassTemplateDeclLocFile) {
diag(Definition->getLocation(),
"Class template specialization %0 should be defined in the "
"same file as %1")
<< Specialization << ClassTemplate;
}
}
}

if (const auto *FunctionTemplate =
Result.Nodes.getNodeAs<FunctionTemplateDecl>(
"function-template-decl")) {
const auto FunctionTemplateDeclLocFile =
Result.SourceManager->getFilename(FunctionTemplate->getLocation());

for (auto *Specialization : FunctionTemplate->specializations()) {
const auto *Definition = Specialization->getDefinition();
if (Definition == nullptr) {
return;
}

const auto FunctionTemplateSpecializationLocFile =
Result.SourceManager->getFilename(Definition->getInnerLocStart());

if (FunctionTemplateSpecializationLocFile !=
FunctionTemplateDeclLocFile) {
diag(Definition->getLocation(),
"Function template specialization %0 should be defined in the "
"same file as %1")
<< Specialization << FunctionTemplate;

diag(FunctionTemplate->getLocation(),
"Function template first defined here", DiagnosticIDs::Note);
}
}
}
}

} // namespace daedalean
} // namespace tidy
} // namespace clang
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===--- TemplateSpecializationsCheck.h - clang-tidy ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DAEDALEAN_TEMPLATESPECIALIZATIONSCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DAEDALEAN_TEMPLATESPECIALIZATIONSCHECK_H

#include "../ClangTidyCheck.h"

namespace clang {
namespace tidy {
namespace daedalean {

/// Looks for template specializations for templates with default
/// implementations and checks that they are defined in the same file
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/daedalean-template-specializations.html
class TemplateSpecializationsCheck : public ClangTidyCheck {
public:
TemplateSpecializationsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};

} // namespace daedalean
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_DAEDALEAN_TEMPLATESPECIALIZATIONSCHECK_H
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ New checks
Looks for local methods and types that should be part of an anonymous
namespace and warns the user.

- New :doc:`daedalean-template-specializations
<clang-tidy/checks/daedalean-template-specializations>` check.

Looks for template specializations for templates with default
implementations and checks that they are defined in the same file.

- New :doc:`daedalean-type-conversions
<clang-tidy/checks/daedalean-type-conversions>` check.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. title:: clang-tidy - daedalean-template-specializations

daedalean-template-specializations
==================================

Looks for template specializations for templates with default
implementations and checks that they are defined in the same file.
6 changes: 4 additions & 2 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,17 @@ Clang-Tidy Checks
`daedalean-include-order <daedalean-include-order.html>`_, "Yes"
`daedalean-lambda-implicit-capture <daedalean-lambda-implicit-capture.html>`_,
`daedalean-lambda-return-type <daedalean-lambda-return-type.html>`_, "Yes"
`daedalean-local-methods-and-types <daedalean-local-methods-and-types.html>`_, "Yes"
`daedalean-local-methods-and-types <daedalean-local-methods-and-types.html>`_,
`daedalean-operator-overloading <daedalean-operator-overloading.html>`_,
`daedalean-preprocessing-directives <daedalean-preprocessing-directives.html>`_,
`daedalean-protected-must-not-be-used <daedalean-protected-must-not-be-used.html>`_,
`daedalean-structs-and-classes <daedalean-structs-and-classes.html>`_, "Yes"
`daedalean-switch-statement <daedalean-switch-statement.html>`_,
`daedalean-template-specializations <daedalean-template-specializations.html>`_, "Yes"
`daedalean-ternary-operator-must-not-be-used <daedalean-ternary-operator-must-not-be-used.html>`_,
`daedalean-type-conversions <daedalean-type-conversions.html>`_,
`daedalean-unions-must-not-be-used <daedalean-unions-must-not-be-used.html>`_,
`daedalean-use-noexcept <daedalean-use-noexcept.html>`_, "Yes"
`daedalean-use-noexcept <daedalean-use-noexcept.html>`_,
`daedalean-vararg-functions-must-not-be-used <daedalean-vararg-functions-must-not-be-used.html>`_,
`darwin-avoid-spinlock <darwin-avoid-spinlock.html>`_,
`darwin-dispatch-once-nonstatic <darwin-dispatch-once-nonstatic.html>`_, "Yes"
Expand Down Expand Up @@ -352,6 +353,7 @@ Clang-Tidy Checks
`readability-use-anyofallof <readability-use-anyofallof.html>`_,
`zircon-temporary-objects <zircon-temporary-objects.html>`_,


.. csv-table:: Aliases..
:header: "Name", "Redirect", "Offers fixes"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#pragma once

#include <template-specializations/other-header.hh>

namespace template_specializations {

template <typename T>
void justDeclaration();

template <typename T>
void function() {}

template <typename T>
void definedInSource();

extern template void definedInSource<float>();
extern template void definedInSource<int>();
extern template void definedInSource<double>();

template <>
void function<char>() {}

template <typename T>
class ClassTemplateOnlyDeclared;

// Partial specialization
template <TriviallyCopyable T>
class ClassTemplateOnlyDeclared<T> {
public:
void doSomething() {}
};

// Full specialization
template <>
class ClassTemplateOnlyDeclared<int> {
public:
void doSomething() {}
};

template <typename T>
class SomeClass {
public:
void doSomething() {}
};

template <>
class SomeClass<char> {
public:
void doSomething() {}
};

template <NotTriviallyCopyable T>
class SomeClass<T> {
public:
void doSomething() {}
};

namespace other_header {
template <>
void function<int>() {}
// CHECK-MESSAGES: [[@LINE-1]]:6: warning: Function template specialization 'function<int>' should be defined in the same file as 'function' [daedalean-template-specializations]

template <>
class SomeClass<int> {
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: Class template specialization 'SomeClass<int>' should be defined in the same file as 'SomeClass' [daedalean-template-specializations]
public:
void doSomething() {}
};

template <TriviallyCopyable T>
class SomeClass<T> {
// CHECK-MESSAGES: [[@LINE-1]]:7: warning: Partial class template specialization 'SomeClass<T>' should be defined in the same file as 'SomeClass' [daedalean-template-specializations]
public:
void doSomething() {}
};

} // namespace other_header

} // namespace template_specializations
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

namespace template_specializations {

template <typename T>
concept TriviallyCopyable = __is_trivially_copyable(T);

template <typename T>
concept NotTriviallyCopyable = !__is_trivially_copyable(T);

namespace other_header {

template <typename T>
void function() {}

template <>
void function<char>() {}

template <typename T>
class SomeClass {
public:
void doSomething() {}
};

template <>
class SomeClass<char> {
public:
void doSomething() {}
};

template <NotTriviallyCopyable T>
class SomeClass<T> {
public:
void doSomething() {}
};

} // namespace other_header
} // namespace template_specializations
Loading

0 comments on commit 22f0b84

Please sign in to comment.